본문 바로가기
Project/테트리스

[테트리스] 14. 블럭이 지면에 닿았는지 판단하는 알고리즘 개선

by 햄과함께 2020. 5. 4.
320x100

오늘의 이슈.

setInterval로 설정한 스피드가 아닌 바닥에 도달하였는지 여부는 시간 설정을 다르게 한다.

예를 들어 바닥에 닿았다고 하더라도 왼쪽이나 오른쪽으로 이동한 경우, 블럭을 회전시킨 경우 특정 시간이후 다시 바닥에 닿았는지 체크했다.


Timer 클래스를 만들고 그 클래스에 게임 시작, 게임 종료, setInterval 관련 코드들 실행을 모두 위임했다.

 

class Timer {

    this.gameTimerId = null; // setInterval Id. 종료시 사용.
    this.bottomTime = null; // 바닥에 닿았는지 판단하는 타임아웃 최대시각
    this.bottomTempTime = null; // 바닥에 닿았는지 판단하는 타임아웃 중간 시각.( 블럭 이동, 회전시 갱신)

    this.speed; // 블럭 떨어지는 속도
    this.bottomTimeInterval; // bottomTime 세팅시 사용. bottomTime = 현재 시각 + bottomTimeInterval
    this.bottomTimeTempInterval; // bottomTempTime 세팅시 사용. bottomTempTime = 현재 시각 + bottomTempTimeInterval

	this.gameFunction; // 특정 시간 이후 실행되는 함수. 블럭이 한 칸 아래로 떨어지는 함수.
    this.gameoverFunction; // 게임 종료시 실행되는 함수.
    this.whenDropBlockNextFunction; // 블럭이 땅에 도달했다고 판단 후 후처리 함수. (ex, 다음 블럭 생성)
    
    // ==== game ====
    // 게임 시작
    startGame() {
        var that = this;
        return that.gameTimerId = setInterval(function() { 
            if(bottomTime 혹은 bottomTempTime보다 현재 시각이 작은 경우)
                아직 떨어졌다고 판단하지 않음.
            else
                블럭이 바닥에 닿았다고 판단.
                set) bottomTime, bottomTempTime = null
                
            if(that.bottomTime != null && that.bottomTempTime != null){
                if(new Date().valueOf() < Math.min(that.bottomTime, that.bottomTempTime)) {
                    return;
                } else {
                    that.bottomTime = null;
                    that.bottomTempTime = null;
                    that.whenDropBlockNextFunction(); // 블럭 바닥에 닿았을 때 실행.
                }
            }
            that.gameFunction(); // 게임 함수 실행
        }, SPEED);
    }

    // 게임 종료
    stopGame() {
        if(!this.gameTimerId) return;
        clearInterval(this.gameTimerId); // setInterval 종료.
        this.gameTimerId = null;
        this.gameoverFunction(); // 게임종료 함수 실행
    }

    // ==== bottom ====
    startBottom() {
        if(this.bottomTime != null) return; // 있는 경우 패스
        var tmp = new Date().valueOf();
        this.bottomTime = tmp + this.bottomTimeInterval;
    }

    stopBottom() {
        this.bottomTime = null;
    }

    // ==== bottomTemp ====
    startBottomTemp() {
        if(this.bottomTempTime != null) return; // 있는 경우 패스
        var tmp = new Date().valueOf();
        this.bottomTempTime = tmp + this.bottomTimeTempInterval;
    }

    // 블럭이 이동하거나 회전한 경우 bottomTempTime을 현재 시각 기준으로 갱신한다.
    refreshBottomTemp() {
        var tmp = new Date().valueOf();
        this.bottomTempTime = tmp + this.bottomTimeTempInterval;
    }

    stopBottomTemp() {
        this.bottomTempTime = null;
    }
}

Timer 클래스.

평소에 javascript를 사용하지 않아서 this scope를 잘 몰랐다.

그래서 startGame 시 setInterval 내부에서 this.bottomTime, this.bottomTempTime이 인식되지 않는 이유를 찾는데 헤맸다.

setInterval에서는 실행될 때, 즉 speed가 지났을 때의 this를 인식하기 때문에 (Timer 내부 아님) window 였나 그걸 this로 인식한다고 한다.

그래서 var that = this. 로 객체 내부 정보를 that이라는 변수에 저장하고 이를 사용하여 bottomTime, bottomTempTime을 호출하니까 제대로 사용할 수 있었다.

 

Timer 클래스에서는 setInterval이 한 개 돌아가고 타임아웃 시각을 bottomTempTime, bottomTime에 저장하여 이를 비교해서 체크하고 있다.

처음에는 setTimeout 을 bottomTempTime, bottomTime 각자 둬서 1개의 setInterval, 2개의 setTimeout을 실행 시켰는데 함수 호출들이 꼬이면서 예측할 수 없는 에러들이 빵빵 터졌다. 그래서 지금처럼 setInterval 1개만 사용하게 수정하였다.

var gameEnd = function() {alert("game over!");};

var timer = new Timer(
    SPEED, 
    BLOCK_BOTTOM_TIMEOUT, 
    BLOCK_BOTTOM_TEMP_TIMEOUT, 
    function() { nowBlock.drawDown(nowBlock.x, nowBlock.y + 1)}, 
    gameEnd, 
    function() {setBlockInGameScreen(nowBlock); nowBlock.checkRowsAndErase(); drawNewBlock();
});

timer는 위와 같이 호출.

눈여겨 볼 것은 생성자의 마지막 파라미터인 whenDropBlockNextFunction(바닥에 닿았다고 판단했을 때 호출하는 함수) 이다. 이 함수에서는 총 3개의 함수를 호출한다.

1. setBlockInGameScreen : 현재 떨어지고 있던 블럭을 gamescreenArray에 세팅해준다.

2. checkRowsAndErase : 지울 수 있는 블럭들이 있는지 확인하고 지워주는 함수를 호출한다.

3. drawNewBlock : 새로운 블럭을 떨어트려야 하기 때문에 새 블럭을 세팅해주는 함수를 호출한다.

 

실행 영상

깃허브

Improving check that block is reached bottom algorithm

320x100

댓글