section8softDrop
S: LEFT
D: SOFT DROP
F: RIGHT
J, L: ROTATION A
K: ROTATION B
下入力を実装。
めちゃ瞬間移動してるみたい。
ずっと気になってた21段目を隠して、デバッグ情報を隠したら完成。
section8softDrop.js
const canvas = document.getElementById(('myCanvas')) const context = canvas.getContext('2d') const bgColor = '#222' let screenX = screenY = 0 const nextNumber = 3 let size = 24 let nextTetrimino = [] let current = { name: 'a', color: '#000', shape: [[]], condition: 0 } // mino.. 配列ベースの位置関係 // location.. sizeベースの位置関係 = size * mono const minoOffsetLeft = 5 let minoDigitalX = minoOffsetLeft let locationX = size * minoOffsetLeft const minoOffsetTop = 1 let minoDigitalY = minoOffsetTop // next表示分にheaderとってあるからminoOffsetTopよりズレる // next分ってことを明示するconst置けばよくない? const nextMinoOffsetTop = 3 let locationY = size * (minoOffsetTop + nextMinoOffsetTop) let dy = 1 let lockDelayTime = 0 let LockDelayLimit = 30 const testSize = 5 const sizeOffsetTop = 4 const sizeOffsetLeft = 1 const empty = ['1', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1'] let field = [ ['0', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '0'] ] for (let i = 0; i < 21; i++) { field.unshift(empty.concat()) } let jFired = false let lFired = false let kFired = false let sFired = false let fFired = false let dFired = false let jTime = 0 let lTime = 0 let kTime = 0 let sTime = 0 let fTime = 0 let dTime = 0 function onKeyDown(e) { if (e.keyCode === 74) { jFired = true } if (e.keyCode === 76) { lFired = true } if (e.keyCode === 75) { kFired = true } if (e.keyCode === 83) { sFired = true } if (e.keyCode === 70) { fFired = true } if (e.keyCode === 68) { dFired = true } } function onKeyUp(e) { if (e.keyCode === 74) { jFired = false jTime = 0 } if (e.keyCode === 76) { lFired = false lTime = 0 } if (e.keyCode === 75) { kFired = false kTime = 0 } if (e.keyCode === 83) { sFired = false sTime = 0 } if (e.keyCode === 70) { fFired = false fTime = 0 } if (e.keyCode === 68) { dFired = false dTime = 0 } } function shuffle() { let arr = ['T', 'J', 'L', 'Z', 'S', 'I', 'O'] for (let i = arr.length - 1; i > 0; i--){ let r = Math.floor(Math.random() * (i + 1)) let tmp = arr[i] arr[i] = arr[r] arr[r] = tmp } return arr } let Tcolor = '#0ff' let Jcolor = '#00f' let Lcolor = '#f60' let Zcolor = '#0f0' let Scolor = '#f0f' let Icolor = '#f00' let Ocolor = '#ff0' function setTetrimino(obj, target) { switch (target) { case 'T': obj.name = 'T' obj.color = Tcolor obj.shape = [ [0, 0, 0], [1, 1, 1], [0, 1, 0]] break; case 'J': obj.name = 'J' obj.color = Jcolor obj.shape = [ [0, 0, 0], [1, 1, 1], [0, 0, 1]] break; case 'L': obj.name = 'L' obj.color = Lcolor obj.shape = [ [0, 0, 0], [1, 1, 1], [1, 0, 0]] break; case 'Z': obj.name = 'Z' obj.color = Zcolor obj.shape = [ [0, 0, 0], [1, 1, 0], [0, 1, 1]] break; case 'S': obj.name = 'S' obj.color = Scolor obj.shape = [ [0, 0, 0], [0, 1, 1], [1, 1, 0]] break; case 'I': obj.name = 'I' obj.color = Icolor obj.shape = [ [0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]] break; case 'O': obj.name = 'O' obj.color = Ocolor obj.shape = [ [0, 0, 0], [0, 1, 1], [0, 1, 1]] break; } obj.condition = 0 return obj } function drawTetrimino() { for (let i = 0; i < current.shape.length; i++) { for (let j = 0; j < current.shape[i].length; j++) { if (current.shape[i][j] === 1) { field[minoDigitalY + i - 1][minoDigitalX + j - 1] = current.name context.fillStyle = current.color context.fillRect(locationX + j * size, locationY + i * size, size, size) } } } } function drawNext() { let next = { name: 'a', color: '#000', shape: [[]], condition: 0 } let nextShowX = minoOffsetLeft * size let nextShowY = 2 * size let nextShowWidth = 4 * size let nextShowHeight = 2 * size context.clearRect(nextShowX, nextShowY, nextShowWidth, nextShowHeight) setTetrimino(next, nextTetrimino[0]) for (let i = 0; i < next.shape.length; i++) { for (let j = 0; j < next.shape[i].length; j++) { if (next.shape[i][j] === 1) { context.fillStyle = next.color context.fillRect(size * (minoOffsetLeft + j),size * (minoOffsetTop + i), size, size) } } } } function drawHideRow() { context.fillStyle = '#ccc' context.fillRect(size, 4 * size, 12* size, size) } function debugPointing() { for (let i = 0; i < field.length; i++) { for (let j = 0; j < field[i].length; j++) { if (field[i][j] === '0') { context.fillStyle = '#fff' context.fillRect((j + sizeOffsetLeft) * size, (i + sizeOffsetTop) * size, testSize, testSize) }else{ context.fillStyle = '#333' context.fillRect((j + sizeOffsetLeft) * size, (i + sizeOffsetTop) * size, testSize, testSize) } } } } function rotation(clockwise) { refresh() current.condition %= 4 let originalShape = current.shape let isOverlap = false let rows = current.shape.length let columns = current.shape[0].length let buffer = new Array(rows) for (let i = 0; i < columns; i++) { buffer[i] = new Array(rows).fill(0) } function roll(clockwise) { if (clockwise) { for (let i = 0; i < rows; i++) { for (let j = 0; j < columns; j++) { buffer[j][i] = current.shape[columns - i - 1][j] } } }else{ for (let i = 0; i < rows; i++) { for (let j = 0; j < columns; j++) { buffer[j][i] = current.shape[i][rows - j - 1] } } } current.shape = buffer } switch (current.name) { case 'O': break; case 'Z': if(current.condition % 2) { roll(true) }else{ roll(false) } break; case 'S': case 'I': if(current.condition % 2) { roll(false) }else{ roll(true) } break; default: if (clockwise) { if (current.condition === 2) { let bufferShape = current.shape.shift() current.shape.push(bufferShape) } roll(true) if (current.condition === 3) { let bufferShape = current.shape.pop() current.shape.unshift(bufferShape) } }else{ if (current.condition === 2) { let bufferShape = current.shape.shift() current.shape.push(bufferShape) } roll(false) if (current.condition === 1) { let bufferShape = current.shape.pop() current.shape.unshift(bufferShape) } } break; } for (let i = 0; i < rows; i++) { for (let j = 0; j < columns; j++) { if (buffer[i][j] === 1) { if (!(field[minoDigitalY + i - 1][minoDigitalX + j - 1] === '0')) { isOverlap = true } } } } if(lockDelayTime === 0 && !isOverlap){ for (let i = 0; i < buffer.length; i++) { for (let j = 0; j < buffer.length; j++) { if (buffer[i][j] === 1) { if (!(field[minoDigitalY + i][minoDigitalX + j - 1] === '0')) { isOverlap = true } } } } } if (isOverlap) { current.shape = originalShape }else{ if (clockwise) { current.condition += 3 } else { current.condition += 1 } } } function chkUnder() { for (let i = 0; i < current.shape.length; i++) { for (let j = 0; j < current.shape[i].length; j++) { if (current.shape[i][j] === 1) { if (!(field[minoDigitalY + i][minoDigitalX + j - 1] === '0')) { return true } } } } } function judgment() { // landing if (chkUnder()) { lockDelayTime += 1 if(LockDelayLimit <= lockDelayTime){ for (let i = 0; i < current.shape.length; i++) { for (let j = 0; j < current.shape[i].length; j++) { if (current.shape[i][j] === 1) { field[minoDigitalY + i - 1][minoDigitalX + j - 1] = current.name context.fillStyle = current.color context.fillRect(locationX + j * size, locationY + i * size, size, size) } } } lockDelayTime = 0 // lottery if (nextTetrimino.length < nextNumber) { nextTetrimino.push(...shuffle()) } setTetrimino(current, nextTetrimino.shift()) minoDigitalX = minoOffsetLeft locationX = size * minoOffsetLeft minoDigitalY = minoOffsetTop locationY = size * (minoOffsetTop + nextMinoOffsetTop) drawNext() clearline() } }else{ // falling locationY += dy minoDigitalY = Math.floor(locationY / size) - nextMinoOffsetTop if(0 < lockDelayTime) { lockDelayTime = 0 } } } function refresh() { for (let i = 0; i < current.shape.length; i++) { for (let j = 0; j < current.shape[i].length; j++) { if (current.shape[i][j] === 1) { field[minoDigitalY + i - 1][minoDigitalX + j - 1] = '0' context.fillStyle = bgColor context.fillRect(locationX + j * size, locationY + i * size, size, size) } } } } function refreshDigital() { for (let i = 0; i < current.shape.length; i++) { for (let j = 0; j < current.shape[i].length; j++) { if (current.shape[i][j] === 1) { field[minoDigitalY + i - 1][minoDigitalX + j - 1] = '0' } } } } function slideLeft() { refreshDigital() let detect = false for (let i = 0; i < current.shape.length; i++) { for (let j = 0; j < current.shape[i].length; j++) { if (current.shape[i][j] === 1) { if (!(field[minoDigitalY + i - 1][minoDigitalX + j - 2] === '0')) { detect = true } } } } if (lockDelayTime === 0 && !detect) { for (let i = 0; i < current.shape.length; i++) { for (let j = 0; j < current.shape[i].length; j++) { if (current.shape[i][j] === 1) { if (!(field[minoDigitalY + i][minoDigitalX + j - 2] === '0')) { detect = true } } } } } if (!detect) { minoDigitalX -= 1 refresh() locationX = size * minoDigitalX } } function slideRight() { refreshDigital() let detect = false for (let i = 0; i < current.shape.length; i++) { for (let j = 0; j < current.shape[i].length; j++) { if (current.shape[i][j] === 1) { if (!(field[minoDigitalY + i - 1][minoDigitalX + j] === '0')) { detect = true } } } } if (lockDelayTime === 0 && !detect) { for (let i = 0; i < current.shape.length; i++) { for (let j = 0; j < current.shape[i].length; j++) { if (current.shape[i][j] === 1) { if (!(field[minoDigitalY + i][minoDigitalX + j] === '0')) { detect = true } } } } } if (!detect) { minoDigitalX += 1 refresh() locationX = size * minoDigitalX } } function softDrop() { refreshDigital() let detect = false for (let i = 0; i < current.shape.length; i++) { for (let j = 0; j < current.shape[i].length; j++) { if (current.shape[i][j] === 1) { if (!(field[minoDigitalY + i][minoDigitalX + j - 1] === '0')) { detect = true } } } } if (lockDelayTime === 0 && !detect) { for (let i = 0; i < current.shape.length; i++) { for (let j = 0; j < current.shape[i].length; j++) { if (current.shape[i][j] === 1) { if (!(field[minoDigitalY + i + 1][minoDigitalX + j - 1] === '0')) { detect = true } } } } } if (!detect) { minoDigitalY += 1 refresh() locationY += size } } function clearline() { let line = 0 for (let i = 0; i < field.length - 1; i++) { if(field[i].every(x => !(x === '0'))) { console.log('test') console.log(i) for (let i = 1; i < field.length - 1; i++) { for (let j = 1; j < field[i].length - 1; j++) { if (!(field[i][j] === '0')) { context.fillStyle = bgColor context.fillRect((1 + j) * size, (4 + i) * size, size, size) } } } field.splice(i, 1) for (let i = 1; i < field.length - 1; i++) { for (let j = 1; j < field[i].length - 1; j++) { if (!(field[i][j] === '0')) { switch (field[i][j]) { case 'T': context.fillStyle = '#0ff' break; case 'J': context.fillStyle = '#00f' break; case 'L': context.fillStyle = '#f60' break; case 'Z': context.fillStyle = '#0f0' break; case 'S': context.fillStyle = '#f0f' break; case 'I': context.fillStyle = '#f00' break; case 'O': context.fillStyle = '#ff0' break; } context.fillRect((1 + j) * size, (5 + i) * size, size, size) } } } line++ } } for (let i = 0; i < line; i++) { field.unshift(empty.concat()) } } function inputProcessing() { if (jFired) { if (jTime === 0) { rotation(false) } jTime += 1 } if (lFired) { if (lTime === 0) { rotation(false) } lTime += 1 } if (kFired) { if (kTime === 0) { rotation(true) } kTime += 1 } if (sFired) { if (sTime === 0) { slideLeft() } else if (14 <= sTime) { slideLeft() } sTime += 1 } if (fFired) { if (fTime === 0) { slideRight() } else if (14 <= fTime) { slideRight() } fTime += 1 } if (dFired) { softDrop() dTime += 1 } } // setup !function() { context.fillStyle = '#ccc' context.fillRect(size, size, size * field[0].length, size * (nextMinoOffsetTop + field.length)) context.fillStyle = bgColor const fieldOffsetLeft = size * 2 const fieldOffsetTop = size * 5 const fieldWidth = size * 10 const fieldHeight = size * 20 context.fillRect(fieldOffsetLeft, fieldOffsetTop, fieldWidth, fieldHeight) nextTetrimino.push(...shuffle()) setTetrimino(current, nextTetrimino.shift()) drawNext() }() function draw(){ refresh() judgment() drawTetrimino() drawHideRow() // debugPointing() inputProcessing() requestAnimationFrame(draw) } draw(); document.addEventListener('keydown', onKeyDown, false) document.addEventListener('keyup', onKeyUp, false)