section7_2tetris
S: LEFT
F: RIGHT
J, L: ROTATION A
K: ROTATION B
配列は書き換わるけど、表示してるテトリミノが下段に落ちない。
配列にミノの情報を格納したほうがいいのかな。
ゴリ押しswitch-caseで色を格納。
テトリスの体は整ったけど、なんかつまんない。
なんでかは知ってる。自分が操作してる感が足りないから。
section7_2tetris.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 jTime = 0
let lTime = 0
let kTime = 0
let sTime = 0
let fTime = 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
}
}
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
}
}
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 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 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
}
}
// 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()
debugPointing()
inputProcessing()
requestAnimationFrame(draw)
}
draw();
document.addEventListener('keydown', onKeyDown, false)
document.addEventListener('keyup', onKeyUp, false)