개선 이유와 방법
업데이트 로직을 바꾸면 효율적으로 되지 않을까 라는 생각이 들어서 시작하게 되었다.
아래는 현재 업데이트 로직이다.
https://ungumungum.tistory.com/95 구현부분
1. 게임 종료되면 업데이트 하지 않음
2. 줄을 지울 수 있는 지를 먼저 확인함
3. 만약 지워야할 줄이 있다면, 수정함
4. 그게 아니라고 현재 블록이 존재하지 않는다면, 랜덤 블럭을 생성해서 현재 블럭으로 저장하고 블럭 생성 가능하다면, 현재을 랜더링함, 그게 아니라면 겜 종료..
5. 만약 현재 블록이 존재한다면, timer를 통해서 블록을 내리고, 아래쪽 이동이 안되는 상황이라면 블럭을 위치시킴.
public update(time: number, delta: number): void {
if (this.gameEnd) return;
const { isClear, line } = this.checkForClearableLines();
if (line !== undefined) {
this.clearLines(line);
this.lineDown(line);
} else {
if (this.currentTetrisBlock === undefined) {
const block = this.spawnRandomBlock(
GameConfig.MainScene.GAME_BOARD_WIDTH_CNT / 2,
0
);
if (this.canSpawnBlock(block)) {
this.currentTetrisBlock = block;
} else {
this.currentTetrisBlock = this.setLastBlockPos(block);
this.gameEnd = true;
this.scene.cameras.main.shake(500);
}
} else {
if (this.timerManger.checkBlockDropTime()) {
if (this.canMoveBlock(0, 1)) {
this.currentTetrisBlock.move(0, 1);
} else {
this.placeBlock();
}
}
}
}
}
키보드 space를 누를떄도 dropBlock 함수가 발동하면서 내려가는데, 이 경우에도 placeBlock 함수가 최종적으로 실행되는 것을 볼 수 있다.
// 키보드 상호작용함수
private handleKeyUp(event: { keyCode: number }) {
if (this.currentTetrisBlock === undefined || this.gameEnd) return;
if (
event.keyCode === Phaser.Input.Keyboard.KeyCodes.LEFT &&
this.canMoveBlock(-1, 0)
) {
this.currentTetrisBlock?.move(-1, 0);
} else if (
event.keyCode === Phaser.Input.Keyboard.KeyCodes.RIGHT &&
this.canMoveBlock(1, 0)
) {
this.currentTetrisBlock?.move(1, 0);
} else if (
event.keyCode === Phaser.Input.Keyboard.KeyCodes.DOWN &&
this.canMoveBlock(0, 1)
) {
this.currentTetrisBlock?.move(0, 1);
} else if (
event.keyCode === Phaser.Input.Keyboard.KeyCodes.UP &&
this.canRotateBlock()
) {
this.currentTetrisBlock?.rotate();
} else if (event.keyCode === Phaser.Input.Keyboard.KeyCodes.SPACE) {
this.dropBlock();
}
}
//dropBlock 함수
private dropBlock() {
if (this.currentTetrisBlock === undefined) return;
for (let y = 0; y < GameConfig.MainScene.GAME_BOARD_HEIGHT_CNT; y++) {
if (!this.canMoveBlock(0, y)) {
this.currentTetrisBlock.move(0, y - 1);
break;
}
}
this.placeBlock();
}
여기서 의문점이 생겼다.
1. 테트리스에서는 언제 줄이 삭제가 될까? === 한 줄이 가득찰 때
2. 테트리스에서 언제 줄이 가득 찰까? === 블록이 놓였을 때
즉 update함수는 프레임마다 실행이 되게 되는데, 이럴 경우 매번 아래의 함수가 호출된다.
private checkForClearableLines() {
for (let y = GameConfig.MainScene.GAME_BOARD_HEIGHT_CNT - 1; y >= 0; y--) {
let isClear = true;
for (let x = 0; x < GameConfig.MainScene.GAME_BOARD_WIDTH_CNT; x++) {
if (this.board[y][x] === 0) {
isClear = false;
break;
}
}
if (isClear) {
return { isClear, line: y };
}
}
return { isClear: false };
}
private clearLines(line: number) {
for (let x = 0; x < GameConfig.MainScene.GAME_BOARD_WIDTH_CNT; x++) {
this.board[line][x] = 0;
}
}
private lineDown(line: number) {
for (let x = 0; x < GameConfig.MainScene.GAME_BOARD_WIDTH_CNT; x++) {
for (let y = line; y >= 1; y--) {
this.board[y][x] = this.board[y - 1][x];
this.board[y - 1][x] = 0;
}
}
}
삭제 할 수 있는지 먼저 확인하고, 가능하다면 줄을 제거한다음에, 내려준다( 내린 위치로 board판 수정)
1. 체크를 하게 되면 반복문을 통해서 모든줄에 대한 확인을 실행하는데, 비록 10*20의 블록이기게 오랜 시간이 걸리진 않더라도 불필요한 리소스를 매번 잡아먹는다.
2. 또한 if else문으로 처리됐기 때문에, 줄 삭제가 필요한 경우에는 다음 프레임에서 새 블록이 생성된다.
따라서 이를 블록이 놓일때만 수정하도록 개선하고자 한다.
public update(time: number, delta: number): void {
if (this.gameEnd) return;
if (this.currentTetrisBlock === undefined) {
const block = this.spawnRandomBlock(
GameConfig.MainScene.GAME_BOARD_WIDTH_CNT / 2,
0
);
if (this.canSpawnBlock(block)) {
this.currentTetrisBlock = block;
} else {
this.currentTetrisBlock = this.setLastBlockPos(block);
this.gameEnd = true;
this.scene.cameras.main.shake(500);
}
} else {
if (this.timerManger.checkBlockDropTime()) {
if (this.canMoveBlock(0, 1)) {
this.currentTetrisBlock.move(0, 1);
} else {
this.placeBlock();
}
}
}
}
우선 업데이트 함수에서 check하던 기능을 제거해준다.
이후 블록이 놓인 다음에 확인하도록 코드를 수정했다.
private placeBlock() {
if (this.currentTetrisBlock === undefined) return;
const renderInfo = this.currentTetrisBlock.getRenderInfo();
for (let y = renderInfo.startY, y2 = 0; y < renderInfo.endY; y++, y2++) {
for (let x = renderInfo.startX, x2 = 0; x < renderInfo.endX; x++, x2++) {
if (renderInfo.tiles[y2][x2] !== 0) {
// 현재 보드를 board에 계속 저장해야함.
this.board[y][x] = renderInfo.tiles[y2][x2];
// 블록이 놓인다음에 제거
const { line } = this.checkForClearableLines();
if (line !== undefined) {
this.clearLines(line);
this.lineDown(line);
}
}
}
}
// 현재 블럭 없애줌
this.currentTetrisBlock = undefined;
}
잘 삭제 되는 것을 확인할 수 있다.
개선 이후 성능 측정
이제 성능을 측정해보려고 한다. 개발자 도구에서 성능 탭을 통해 줄이 삭제되는 시점을 찾았고,


해당 시점에서 space바를 통해서 블록을 내렸고 줄이 가득차게 되면서 삭제되게 된다.
그리고 삭제되고 다음 이벤트에서 랜더링이 되는 것을 확인할 수 있다.
(3450에 줄삭제, 3565에 줄삭제된 것이 반영)


시간을 측정했을 때는 0.24~0.29밀리초가 update에 걸렸다.
개선 후



0.24~0.29 9 에서 0.11~0.19로 거의 절반에 가깝게 update 성능이 개선되었다.
'프로젝트' 카테고리의 다른 글
R3F에서 물체가 지나갈 수 있는 언덕 만들기(react-three/cannon) (0) | 2024.06.04 |
---|---|
R3F에서 시바를 키보드 입력을 통해 회전시키기(트러블 슈팅) (0) | 2024.05.30 |
자바스크립트로 테트리스 클론 코딩 및 디벨롭(Phaser , Vite)-1 리뷰 (0) | 2024.03.21 |
windowWidth를 통해서 구한 값을 통해, ui라이브러리에 반응형 크기주기 (리팩토링을 하며) (0) | 2023.10.08 |
ssh를 통해서 배포된 서버 디버깅하기(DOCKER, EC2,윈도우) (0) | 2023.09.27 |