あいうえおch.
Members Link
前へ - 次へ

section6_3slide

S: LEFT

 F: RIGHT


J, L: ROTATION A

 K: ROTATION B


横溜め実装


section6_3slide.js
  1. const canvas = document.getElementById(('myCanvas'))
  2. const context = canvas.getContext('2d')
  3. const bgColor = '#222'
  4. let screenX = screenY = 0
  5. const nextNumber = 3
  6. let size = 24
  7. let nextTetrimino = []
  8. let current = {
  9. name: 'a',
  10. color: '#000',
  11. shape: [[]],
  12. condition: 0
  13. }
  14.  
  15. // mino.. 配列ベースの位置関係
  16. // location.. sizeベースの位置関係 = size * mono
  17.  
  18. const minoOffsetLeft = 5
  19. let minoDigitalX = minoOffsetLeft
  20. let locationX = size * minoOffsetLeft
  21.  
  22. const minoOffsetTop = 1
  23. let minoDigitalY = minoOffsetTop
  24. // next表示分にheaderとってあるからminoOffsetTopよりズレる
  25. // next分ってことを明示するconst置けばよくない?
  26. const nextMinoOffsetTop = 3
  27. let locationY = size * (minoOffsetTop + nextMinoOffsetTop)
  28. let dy = 1
  29. let lockDelayTime = 0
  30. let LockDelayLimit = 30
  31. const testSize = 5
  32. const sizeOffsetTop = 4
  33. const sizeOffsetLeft = 1
  34. const empty = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
  35. let field = [
  36. [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]
  37. ]
  38. for (let i = 0; i < 21; i++) {
  39. field.unshift(empty.concat())
  40. }
  41. let jFired = false
  42. let lFired = false
  43. let kFired = false
  44. let sFired = false
  45. let fFired = false
  46. let jTime = 0
  47. let lTime = 0
  48. let kTime = 0
  49. let sTime = 0
  50. let fTime = 0
  51. function onKeyDown(e) {
  52. if (e.keyCode === 74) {
  53. jFired = true
  54. }
  55. if (e.keyCode === 76) {
  56. lFired = true
  57. }
  58. if (e.keyCode === 75) {
  59. kFired = true
  60. }
  61. if (e.keyCode === 83) {
  62. sFired = true
  63. }
  64. if (e.keyCode === 70) {
  65. fFired = true
  66. }
  67. }
  68. function onKeyUp(e) {
  69. if (e.keyCode === 74) {
  70. jFired = false
  71. jTime = 0
  72. }
  73. if (e.keyCode === 76) {
  74. lFired = false
  75. lTime = 0
  76. }
  77. if (e.keyCode === 75) {
  78. kFired = false
  79. kTime = 0
  80. }
  81. if (e.keyCode === 83) {
  82. sFired = false
  83. sTime = 0
  84. }
  85. if (e.keyCode === 70) {
  86. fFired = false
  87. fTime = 0
  88. }
  89. }
  90. function shuffle() {
  91. let arr = ['T', 'J', 'L', 'Z', 'S', 'I', 'O']
  92. for (let i = arr.length - 1; i > 0; i--){
  93. let r = Math.floor(Math.random() * (i + 1))
  94. let tmp = arr[i]
  95. arr[i] = arr[r]
  96. arr[r] = tmp
  97. }
  98. return arr
  99. }
  100. function setTetrimino(obj, target) {
  101. switch (target) {
  102. case 'T':
  103. obj.name = 'T'
  104. obj.color = '#0ff'
  105. obj.shape = [
  106. [0, 0, 0],
  107. [1, 1, 1],
  108. [0, 1, 0]]
  109. break;
  110. case 'J':
  111. obj.name = 'J'
  112. obj.color = '#00f'
  113. obj.shape = [
  114. [0, 0, 0],
  115. [1, 1, 1],
  116. [0, 0, 1]]
  117. break;
  118. case 'L':
  119. obj.name = 'L'
  120. obj.color = '#f60'
  121. obj.shape = [
  122. [0, 0, 0],
  123. [1, 1, 1],
  124. [1, 0, 0]]
  125. break;
  126. case 'Z':
  127. obj.name = 'Z'
  128. obj.color = '#0f0'
  129. obj.shape = [
  130. [0, 0, 0],
  131. [1, 1, 0],
  132. [0, 1, 1]]
  133. break;
  134. case 'S':
  135. obj.name = 'S'
  136. obj.color = '#f0f'
  137. obj.shape = [
  138. [0, 0, 0],
  139. [0, 1, 1],
  140. [1, 1, 0]]
  141. break;
  142. case 'I':
  143. obj.name = 'I'
  144. obj.color = '#f00'
  145. obj.shape = [
  146. [0, 0, 0, 0],
  147. [1, 1, 1, 1],
  148. [0, 0, 0, 0],
  149. [0, 0, 0, 0]]
  150. break;
  151. case 'O':
  152. obj.name = 'O'
  153. obj.color = '#ff0'
  154. obj.shape = [
  155. [0, 0, 0],
  156. [0, 1, 1],
  157. [0, 1, 1]]
  158. break;
  159. }
  160. obj.condition = 0
  161. return obj
  162. }
  163. function drawTetrimino() {
  164. for (let i = 0; i < current.shape.length; i++) {
  165. for (let j = 0; j < current.shape[i].length; j++) {
  166. if (current.shape[i][j] === 1) {
  167. field[minoDigitalY + i - 1][minoDigitalX + j - 1] = 1
  168. context.fillStyle = current.color
  169. context.fillRect(locationX + j * size, locationY + i * size, size, size)
  170. }
  171. }
  172. }
  173. }
  174. function drawNext() {
  175. let next = {
  176. name: 'a',
  177. color: '#000',
  178. shape: [[]],
  179. condition: 0
  180. }
  181. let nextShowX = minoOffsetLeft * size
  182. let nextShowY = 2 * size
  183. let nextShowWidth = 4 * size
  184. let nextShowHeight = 2 * size
  185. context.clearRect(nextShowX, nextShowY, nextShowWidth, nextShowHeight)
  186. setTetrimino(next, nextTetrimino[0])
  187. for (let i = 0; i < next.shape.length; i++) {
  188. for (let j = 0; j < next.shape[i].length; j++) {
  189. if (next.shape[i][j] === 1) {
  190. context.fillStyle = next.color
  191. context.fillRect(size * (minoOffsetLeft + j),size * (minoOffsetTop + i), size, size)
  192. }
  193. }
  194. }
  195. }
  196. function debugPointing() {
  197. for (let i = 0; i < field.length; i++) {
  198. for (let j = 0; j < field[i].length; j++) {
  199. if (field[i][j] === 1) {
  200. context.fillStyle = '#333'
  201. context.fillRect((j + sizeOffsetLeft) * size, (i + sizeOffsetTop) * size, testSize, testSize)
  202. }else{
  203. context.fillStyle = '#fff'
  204. context.fillRect((j + sizeOffsetLeft) * size, (i + sizeOffsetTop) * size, testSize, testSize)
  205. }
  206. }
  207. }
  208. }
  209. function rotation(clockwise) {
  210. refresh()
  211. current.condition %= 4
  212. let originalShape = current.shape
  213. let isOverlap = false
  214. let rows = current.shape.length
  215. let columns = current.shape[0].length
  216. let buffer = new Array(rows)
  217. for (let i = 0; i < columns; i++) {
  218. buffer[i] = new Array(rows).fill(0)
  219. }
  220. function roll(clockwise) {
  221. if (clockwise) {
  222. for (let i = 0; i < rows; i++) {
  223. for (let j = 0; j < columns; j++) {
  224. buffer[j][i] = current.shape[columns - i - 1][j]
  225. }
  226. }
  227. }else{
  228. for (let i = 0; i < rows; i++) {
  229. for (let j = 0; j < columns; j++) {
  230. buffer[j][i] = current.shape[i][rows - j - 1]
  231. }
  232. }
  233. }
  234. current.shape = buffer
  235. }
  236. switch (current.name) {
  237. case 'O':
  238. break;
  239. case 'Z':
  240. if(current.condition % 2) {
  241. roll(true)
  242. }else{
  243. roll(false)
  244. }
  245. break;
  246. case 'S':
  247. case 'I':
  248. if(current.condition % 2) {
  249. roll(false)
  250. }else{
  251. roll(true)
  252. }
  253. break;
  254. default:
  255. if (clockwise) {
  256. if (current.condition === 2) {
  257. let bufferShape = current.shape.shift()
  258. current.shape.push(bufferShape)
  259. }
  260. roll(true)
  261. if (current.condition === 3) {
  262. let bufferShape = current.shape.pop()
  263. current.shape.unshift(bufferShape)
  264. }
  265. }else{
  266. if (current.condition === 2) {
  267. let bufferShape = current.shape.shift()
  268. current.shape.push(bufferShape)
  269. }
  270. roll(false)
  271. if (current.condition === 1) {
  272. let bufferShape = current.shape.pop()
  273. current.shape.unshift(bufferShape)
  274. }
  275. }
  276. break;
  277. }
  278. for (let i = 0; i < rows; i++) {
  279. for (let j = 0; j < columns; j++) {
  280. if (buffer[i][j] === 1) {
  281. console.log(field[minoDigitalY + i - 1][minoDigitalX + j - 1])
  282. if (field[minoDigitalY + i - 1][minoDigitalX + j - 1] === 1) {
  283. isOverlap = true
  284. }
  285. }
  286. }
  287. }
  288. if(lockDelayTime === 0 && !isOverlap){
  289. for (let i = 0; i < buffer.length; i++) {
  290. for (let j = 0; j < buffer.length; j++) {
  291. if (buffer[i][j] === 1) {
  292. if (field[minoDigitalY + i][minoDigitalX + j - 1] === 1) {
  293. isOverlap = true
  294. }
  295. }
  296. }
  297. }
  298. }
  299. if (isOverlap) {
  300. current.shape = originalShape
  301. }else{
  302. if (clockwise) {
  303. current.condition += 3
  304. } else {
  305. current.condition += 1
  306. }
  307. }
  308. }
  309. function chkUnder() {
  310. for (let i = 0; i < current.shape.length; i++) {
  311. for (let j = 0; j < current.shape[i].length; j++) {
  312. if (current.shape[i][j] === 1) {
  313. if (field[minoDigitalY + i][minoDigitalX + j - 1] === 1) {
  314. return true
  315. }
  316. }
  317. }
  318. }
  319. }
  320. function judgment() {
  321. // landing
  322. if (chkUnder()) {
  323. lockDelayTime += 1
  324. if(LockDelayLimit <= lockDelayTime){
  325. for (let i = 0; i < current.shape.length; i++) {
  326. for (let j = 0; j < current.shape[i].length; j++) {
  327. if (current.shape[i][j] === 1) {
  328. field[minoDigitalY + i - 1][minoDigitalX + j - 1] = 1
  329. context.fillStyle = current.color
  330. context.fillRect(locationX + j * size, locationY + i * size, size, size)
  331. }
  332. }
  333. }
  334. lockDelayTime = 0
  335. // lottery
  336. if (nextTetrimino.length < nextNumber) {
  337. nextTetrimino.push(...shuffle())
  338. }
  339. setTetrimino(current, nextTetrimino.shift())
  340. minoDigitalX = minoOffsetLeft
  341. locationX = size * minoOffsetLeft
  342. minoDigitalY = minoOffsetTop
  343. locationY = size * (minoOffsetTop + nextMinoOffsetTop)
  344. drawNext()
  345. }
  346. }else{
  347. // falling
  348. locationY += dy
  349. minoDigitalY = Math.floor(locationY / size) - nextMinoOffsetTop
  350. if(0 < lockDelayTime) {
  351. lockDelayTime = 0
  352. }
  353. }
  354. }
  355. function refresh() {
  356. for (let i = 0; i < current.shape.length; i++) {
  357. for (let j = 0; j < current.shape[i].length; j++) {
  358. if (current.shape[i][j] === 1) {
  359. field[minoDigitalY + i - 1][minoDigitalX + j - 1] = 0
  360. context.fillStyle = bgColor
  361. context.fillRect(locationX + j * size, locationY + i * size, size, size)
  362. }
  363. }
  364. }
  365. }
  366. function refreshDigital() {
  367. for (let i = 0; i < current.shape.length; i++) {
  368. for (let j = 0; j < current.shape[i].length; j++) {
  369. if (current.shape[i][j] === 1) {
  370. field[minoDigitalY + i - 1][minoDigitalX + j - 1] = 0
  371. }
  372. }
  373. }
  374. }
  375. function slideLeft() {
  376. refreshDigital()
  377. let detect = false
  378. for (let i = 0; i < current.shape.length; i++) {
  379. for (let j = 0; j < current.shape[i].length; j++) {
  380. if (current.shape[i][j] === 1) {
  381. if (field[minoDigitalY + i - 1][minoDigitalX + j - 2] === 1) {
  382. detect = true
  383. }
  384. }
  385. }
  386. }
  387. if (lockDelayTime === 0 && !detect) {
  388. for (let i = 0; i < current.shape.length; i++) {
  389. for (let j = 0; j < current.shape[i].length; j++) {
  390. if (current.shape[i][j] === 1) {
  391. if (field[minoDigitalY + i][minoDigitalX + j - 2] === 1) {
  392. return true
  393. }
  394. }
  395. }
  396. }
  397. }
  398. if (!detect) {
  399. minoDigitalX -= 1
  400. refresh()
  401. locationX = size * minoDigitalX
  402. }
  403. }
  404. function slideRight() {
  405. refreshDigital()
  406. let detect = false
  407. for (let i = 0; i < current.shape.length; i++) {
  408. for (let j = 0; j < current.shape[i].length; j++) {
  409. if (current.shape[i][j] === 1) {
  410. if (field[minoDigitalY + i - 1][minoDigitalX + j] === 1) {
  411. detect = true
  412. }
  413. }
  414. }
  415. }
  416. if (lockDelayTime === 0 && !detect) {
  417. for (let i = 0; i < current.shape.length; i++) {
  418. for (let j = 0; j < current.shape[i].length; j++) {
  419. if (current.shape[i][j] === 1) {
  420. if (field[minoDigitalY + i][minoDigitalX + j] === 1) {
  421. detect = true
  422. }
  423. }
  424. }
  425. }
  426. }
  427. if (!detect) {
  428. minoDigitalX += 1
  429. refresh()
  430. locationX = size * minoDigitalX
  431. }
  432. }
  433. function inputProcessing() {
  434. if (jFired) {
  435. if (jTime === 0) {
  436. rotation(false)
  437. }
  438. jTime += 1
  439. }
  440. if (lFired) {
  441. if (lTime === 0) {
  442. rotation(false)
  443. }
  444. lTime += 1
  445. }
  446. if (kFired) {
  447. if (kTime === 0) {
  448. rotation(true)
  449. }
  450. kTime += 1
  451. }
  452. if (sFired) {
  453. if (sTime === 0) {
  454. slideLeft()
  455. } else if (14 <= sTime) {
  456. slideLeft()
  457. }
  458. sTime += 1
  459. }
  460. if (fFired) {
  461. console.log(fTime)
  462. if (fTime === 0) {
  463. slideRight()
  464. } else if (14 <= fTime) {
  465. slideRight()
  466. }
  467. fTime += 1
  468. }
  469. }
  470. // setup
  471. !function() {
  472. context.fillStyle = '#ccc'
  473. context.fillRect(size, size, size * field[0].length, size * (nextMinoOffsetTop + field.length))
  474. context.fillStyle = bgColor
  475. const fieldOffsetLeft = size * 2
  476. const fieldOffsetTop = size * 5
  477. const fieldWidth = size * 10
  478. const fieldHeight = size * 20
  479. context.fillRect(fieldOffsetLeft, fieldOffsetTop, fieldWidth, fieldHeight)
  480. nextTetrimino.push(...shuffle())
  481. setTetrimino(current, nextTetrimino.shift())
  482. drawNext()
  483. }()
  484. function draw(){
  485. refresh()
  486. judgment()
  487. drawTetrimino()
  488. debugPointing()
  489. inputProcessing()
  490. requestAnimationFrame(draw)
  491. }
  492. draw();
  493.  
  494. document.addEventListener('keydown', onKeyDown, false)
  495. document.addEventListener('keyup', onKeyUp, false)

キーボードのハード的仕様かOSの悪さで、

押下直後は入力がリピートされないようになってる。

aaaaaaaaaaaって打つときの最初のアレ。

AHKの時には気にしなくてよかったから多分Winちゃんの仕様。


なのでJS内でキーリピート判定を見る。

キープレスは連続して受け付けてくれてるから、

キーリリースまでカウンタを置いてあげる。


横入れっぱなしでテトリミノが見えなくなるので、

refreshDigital()作って解消。

あんまり意味ねーな。多分1ループの中で処理と描画の順序がおかしいんだ。


やっぱり中途半端なところで横入れすると消滅する。

移動後の下も調べて、埋まらないようにする。