From c51c2cdef1269bde4c09d9ec92f4bc036a8611e0 Mon Sep 17 00:00:00 2001 From: fpdjsns Date: Mon, 28 Feb 2022 18:40:04 +0900 Subject: [PATCH] =?UTF-8?q?resolved=20#18.=EB=B8=94=EB=A1=9D=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99,=20=EB=B0=94=EB=8B=A5=EC=97=90=20=EB=8F=84=EB=8B=AC?= =?UTF-8?q?=ED=96=88=EC=9D=84=20=EB=95=8C=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Tetris/js/block.js | 237 ++++++++++++++++++++++--------------- Tetris/js/blockType.js | 14 ++- Tetris/js/constants.js | 3 +- Tetris/js/gameAlgorithm.js | 42 +++---- Tetris/js/keyEvent.js | 6 +- 5 files changed, 176 insertions(+), 126 deletions(-) diff --git a/Tetris/js/block.js b/Tetris/js/block.js index ddb598d..bd05632 100644 --- a/Tetris/js/block.js +++ b/Tetris/js/block.js @@ -1,3 +1,16 @@ +function Point(x, y) { + this.x = x; + this.y = y; +} + +Point.prototype.isEquals = function (x, y) { + return this.x == x && this.y == y; +} + +Point.prototype.isLower = function (x, y) { + return this.y < y; +}; + class Block { constructor(blockTypeIndex, x, y) { @@ -5,68 +18,39 @@ class Block { this.type = blockType[this.typeIndex]; this.shape = [...this.type.shape]; this.blockNum = this.shape.length - this.x = x; - this.y = y; + + this.position = new Point(x, y) + this.bottomPosition = new Point(x, y) this.isLoaded = false; + + this.setPreviewCoordinate() } + // === private === drawPreview() { - for (let k = 0; ; k++) { - if (this.isBottom(this.x, this.y + k + 1)) { - this.justDrawBlock(this.x, this.y + k, 'gray'); - return; - } - } + this.justDrawBlock(this.bottomPosition.x, this.bottomPosition.y, 'gray'); } erasePreview() { - for (let k = 0; ; k++) { - if (this.isBottom(this.x, this.y + k + 1)) { - this.eraseBlock(this.x, this.y + k); - return; - } - } + this.eraseBlock(this.bottomPosition.x, this.bottomPosition.y); } - drawLeftOrRight(nx, ny) { - const isDuplicated = this.isDuplicatedBlockOrOutOfGameScreen(nx, ny); - if (isDuplicated != NONE_DUPLICATED) { - console.log("duplicated!"); - } else { - this.eraseBeforeBlock(); - this.drawBlock(nx, ny); - } + moveLeftOrRight(nx, ny) { + this.move(nx, ny) timer.refreshBottomTemp(); } - checkRowsAndErase() { - checkRowsAndErase( - this.y, - Math.min( - GAME_SCREEN_HEIGHT_NUM - 1, - this.y + this.blockNum - 1 - ) - ); - } - - drawDown(nx = this.x, ny = this.y + 1) { - if (this.isBottom(nx, ny)) { - timer.startBottom(); - timer.startBottomTemp(); - } else { - this.eraseBeforeBlock(); - this.drawBlock(nx, ny); - timer.stopBottom(); - timer.stopBottomTemp(); + setPreviewCoordinate() { + for (let k = 0; ; k++) { + if (this.isBottom(this.position.x, this.position.y + k + 1)) { + this.bottomPosition.x = this.position.x + this.bottomPosition.y = this.position.y + k + return; + } } } - eraseBeforeBlock() { - this.erasePreview(); - this.eraseBlock(); - } - - eraseBlock(x = this.x, y = this.y) { + eraseBlock(x = this.position.x, y = this.position.y) { for (let i = 0; i < this.blockNum; i++) { for (let j = 0; j < this.blockNum; j++) { if (this.shape[i][j] == 1) { @@ -76,7 +60,7 @@ class Block { } } - justDrawBlock(x = this.x, y = this.y, colorName = undefined) { + justDrawBlock(x = this.position.x, y = this.position.y, colorName = undefined) { for (let i = 0; i < this.blockNum; i++) { for (let j = 0; j < this.blockNum; j++) { if (this.shape[i][j] == 1) { @@ -90,26 +74,14 @@ class Block { } } - drawBlock(x, y) { - this.x = x; - this.y = y; - - // this.erasePreview(this.x, this.y); - this.drawPreview(); - - // Change colors just before drawing to avoid affecting the previous or next block color. - this.justDrawBlock(); + isDuplicatedBlockOrOutOfGameScreen(x, y, shape = this.shape) { + let checkShape = shape; - } - - isDuplicatedBlockOrOutOfGameScreen(x, y, block) { - var checkShape = this.shape; - if (block != undefined) checkShape = block; + for (let i = 0; i < this.blockNum; i++) { + for (let j = 0; j < this.blockNum; j++) { + const nx = x + i; + const ny = y + j; - for (var i = 0; i < this.blockNum; i++) { - for (var j = 0; j < this.blockNum; j++) { - var nx = x + i; - var ny = y + j; if (checkShape[j][i] == 0) continue; // out of game screen @@ -119,6 +91,9 @@ class Block { if (GAME_SCREEN_WIDTH_NUM < nx) { return RIGHT_DUPLICATED; } + if (GAME_SCREEN_HEIGHT_NUM <= ny) { + return BOTTOM_DUPLICATED; + } // duplicated another block if (gameScreenArray[ny] != undefined && gameScreenArray[ny][nx] != -1) { return EITHER_DUPLICATED; @@ -128,7 +103,78 @@ class Block { return NONE_DUPLICATED; } - isBottom(x, y) { + // 블럭을 nx, ny로 움직일 때 실행 + // 블럭이 성공적으로 (nx, ny)로 움직였는지 boolean 값 반환 + move(nx, ny, shape = this.shape) { + let moved = false + + // 움직일 수 있는 곳인지 체크 가능하지 않다면 + if (this.isDuplicatedBlockOrOutOfGameScreen(nx, ny) != NONE_DUPLICATED) { + console.log("duplicated!"); + } else { + // bottom 좌표 갱신 + this.eraseBeforeBlock(); + + // bottom 좌표와 동일한지 체크( = 바닥인지 체크) + if (this.bottomPosition.isLower(nx, ny)) { + ny = this.bottomPosition.y + } else { + // move + this.shape = shape; + moved = true + } + + this.drawBlock(nx, ny) + } + + return moved; + } + + // position의 갱신은 해당 함수에서만 이루어진다. + drawBlock(x, y) { + this.position.x = x + this.position.y = y + + this.setPreviewCoordinate() + this.drawPreview(); + this.justDrawBlock(); + } + + // public + moveLeft() { + this.moveLeftOrRight(this.position.x - 1, this.position.y) + } + + moveRight() { + this.moveLeftOrRight(this.position.x + 1, this.position.y) + } + + checkRowsAndErase() { + checkRowsAndErase( + this.position.y, + Math.min( + GAME_SCREEN_HEIGHT_NUM - 1, + this.position.y + this.blockNum - 1 + ) + ); + } + + drawDown(nx = this.position.x, ny = this.position.y + 1) { + if (!this.move(nx, ny)) { + timer.startBottom(); + timer.startBottomTemp(); + } else { + timer.stopBottom(); + timer.stopBottomTemp(); + } + } + + eraseBeforeBlock() { + this.erasePreview(); + this.eraseBlock(); + } + + isBottom(x = this.position.x, y = this.position.y) { for (let i = 0; i < this.blockNum; i++) { for (let j = 0; j < this.blockNum; j++) { const nx = x + i; @@ -142,38 +188,36 @@ class Block { return false; } + // shape 갱신 rotation() { - this.eraseBeforeBlock(); - const length = this.shape.length; - const nextShape = []; - for (let i = 0; i < length; i++) nextShape[i] = []; - - for (let i = 0; i < length; i++) - for (let j = 0; j < length; j++) - nextShape[length - 1 - j][i] = this.shape[i][j]; - - var checkDuplicated = this.isDuplicatedBlockOrOutOfGameScreen( - this.x, - this.y, - nextShape - ); + const nextShape = getRotateShape(this.shape); + + const originPosition = this.position + let nx = this.position.x + let ny = this.position.y + + let checkDuplicated = this.isDuplicatedBlockOrOutOfGameScreen(nx, ny, nextShape); let rotatable = true; + + // 충돌한 곳이 있다면 if (checkDuplicated != NONE_DUPLICATED) { - var moveIndex = 0; + rotatable = false; + // 왼쪽 면이 중복이거나 다른 블록가 중복이라면 if ( checkDuplicated == LEFT_DUPLICATED || checkDuplicated == EITHER_DUPLICATED ) { - for (i = 1; i < this.blockNum; i++) { + for (let i = 1; i < this.blockNum; i++) { if ( this.isDuplicatedBlockOrOutOfGameScreen( - this.x + i, - this.y, + originPosition.x + i, + originPosition.y, nextShape ) == NONE_DUPLICATED ) { - moveIndex = i; + rotatable = true; + nx = originPosition.x + i; break; } } @@ -185,27 +229,28 @@ class Block { for (let i = 1; i < this.blockNum; i++) { if ( this.isDuplicatedBlockOrOutOfGameScreen( - this.x - i, - this.y, + originPosition.x - i, + originPosition.y, nextShape ) == NONE_DUPLICATED ) { - moveIndex = -i; + rotatable = true; + nx = originPosition.x - i; break; } } } - if (moveIndex == 0) { // x축을 움직여도 안되는경우 회전하지 않음 - rotatable = false; - } else { // x축을 움직인다. - this.x += moveIndex; - } + // x축을 움직여도 안되는경우 회전하지 않음 } if (rotatable) { - this.shape = nextShape.slice(); + this.move(nx, ny, nextShape.slice()); } - this.drawBlock(this.x, this.y); } + + moveBottom() { + this.move(this.bottomPosition.x, this.bottomPosition.y) + } + } diff --git a/Tetris/js/blockType.js b/Tetris/js/blockType.js index ecbf5db..26b66ab 100644 --- a/Tetris/js/blockType.js +++ b/Tetris/js/blockType.js @@ -41,6 +41,16 @@ const blockTypeMap = blockType.reduce((map, type, index, array) => { return map; }, new Map()); -var getBlockTypeIndex = function(blockType) { +function getRotateShape(shape) { + const length = shape.length + const nextShape = []; + for (let i = 0; i < length; i++) nextShape[i] = []; + for (let i = 0; i < length; i++) + for (let j = 0; j < length; j++) + nextShape[length - 1 - j][i] = shape[i][j]; + return nextShape; +} + +var getBlockTypeIndex = function (blockType) { return blockTypeMap[blockType.name]; -} \ No newline at end of file +} diff --git a/Tetris/js/constants.js b/Tetris/js/constants.js index 24fc553..a830419 100644 --- a/Tetris/js/constants.js +++ b/Tetris/js/constants.js @@ -32,6 +32,7 @@ const NONE_DUPLICATED = 0; const LEFT_DUPLICATED = 1; const RIGHT_DUPLICATED = 2; const EITHER_DUPLICATED = 3; +const BOTTOM_DUPLICATED = 4; const LINE_COLOR = "gray"; @@ -41,4 +42,4 @@ const BLOCK_BOTTOM_TEMP_TIMEOUT = 500; // 0.5sec const SPEED_UP_INTERVAL = 10000; // 10sec const SPEED_UNIT_PERCENT = 0.9; // 90% -const CANVAS_BORDER_LINE_WIDTH = 2; \ No newline at end of file +const CANVAS_BORDER_LINE_WIDTH = 2; diff --git a/Tetris/js/gameAlgorithm.js b/Tetris/js/gameAlgorithm.js index 6189303..f6aaca3 100644 --- a/Tetris/js/gameAlgorithm.js +++ b/Tetris/js/gameAlgorithm.js @@ -9,9 +9,7 @@ var timer = new Timer(SPEED, BLOCK_BOTTOM_TIMEOUT, BLOCK_BOTTOM_TEMP_TIMEOUT, SP nowBlock.drawDown() }, gameEnd, function () { - setBlockInGameScreen(nowBlock); - nowBlock.checkRowsAndErase(); - drawNewBlock(); + moveBottomAndSetting() }); function gameStart() { @@ -93,8 +91,8 @@ function drawKeepBlock() { // sy ~ se row에서 지워질 수 있는 행 체크 & 지우기 // TODO test function checkRowsAndErase(sy, ey) { - var isEraseAnything = false; - for (var i = sy; i <= ey; i++) { + let isEraseAnything = false; + for (let i = sy; i <= ey; i++) { if (canEraseRow(i)) { eraseRow(i); isEraseAnything = true; @@ -106,7 +104,7 @@ function checkRowsAndErase(sy, ey) { }; function canEraseRow(row) { - for (var j = 0; j < GAME_SCREEN_WIDTH_NUM; j++) { + for (let j = 0; j < GAME_SCREEN_WIDTH_NUM; j++) { if (gameScreenArray[row][j] == -1) { return false; } @@ -115,7 +113,7 @@ function canEraseRow(row) { }; function eraseRow(row) { - for (var j = 0; j < GAME_SCREEN_WIDTH_NUM; j++) { + for (let j = 0; j < GAME_SCREEN_WIDTH_NUM; j++) { eraseOneBlock(row, j); gameScreenArray[row][j] = NOW_DELETE; } @@ -185,31 +183,27 @@ function rearrange() { } }; -function drawBelow(block) { +function moveBottomAndSetting(block = nowBlock) { console.log("below"); - const x = block.x; - let y = block.y; - block.eraseBeforeBlock(); - while (!block.isBottom(x, y + 1)) { - y = y + 1; - } + block.moveBottom() + setBlockInGameScreen(block); + block.checkRowsAndErase(); timer.stopBottom(); timer.stopBottomTemp(); - block.drawBlock(x, y); - setBlockInGameScreen(block); - checkRowsAndErase( - y, - Math.min(GAME_SCREEN_HEIGHT_NUM - 1, y + block.blockNum - 1) - ); drawNewBlock(); }; function setBlockInGameScreen(block) { - for (var i = 0; i < block.blockNum; i++) { - for (var j = 0; j < block.blockNum; j++) { - if (block.shape[j][i] == 1) { - gameScreenArray[block.y + j][block.x + i] = block.typeIndex; + const length = block.blockNum; + const typeIndex = block.typeIndex; + const shape = block.shape + const position = block.position + + for (let i = 0; i < length; i++) { + for (let j = 0; j < length; j++) { + if (shape[j][i] == 1) { + gameScreenArray[position.y + j][position.x + i] = typeIndex; } } } diff --git a/Tetris/js/keyEvent.js b/Tetris/js/keyEvent.js index 74d5d18..d48a43d 100644 --- a/Tetris/js/keyEvent.js +++ b/Tetris/js/keyEvent.js @@ -58,7 +58,7 @@ var spacebar = $(".fa-arrow-circle-down"); var leftKeyDown = function () { //left arrow_left.addClass("click"); - nowBlock.drawLeftOrRight(nowBlock.x - 1, nowBlock.y); + nowBlock.moveLeft(); }; var upKeyDown = function () { //up @@ -70,7 +70,7 @@ var upKeyDown = function () { var rightKeyDown = function () { //right arrow_right.addClass("click"); - nowBlock.drawLeftOrRight(nowBlock.x + 1, nowBlock.y); + nowBlock.moveRight(); }; var downKeyDown = function () { //down @@ -79,7 +79,7 @@ var downKeyDown = function () { }; var spacebarKeyDown = function () { spacebar.addClass("click"); - drawBelow(nowBlock); + moveBottomAndSetting(); }; const keepOrLoadBlockKey = function () {