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

[테트리스] 6. 블럭 회전

by 햄과함께 2019. 8. 27.
320x100

2018. 7. 30.


# 블럭 회전
var blockType = [
    {
        name: "O",
        color: "skyblue",
        shape: [[[0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]]]
    },
    {
        name: "S",
        color: "gray",
        shape: [
            [[0, 0, 0, 0], [0, 0, 1, 1], [0, 1, 1, 0], [0, 0, 0, 0]],
            [[0, 0, 1, 0], [0, 0, 1, 1], [0, 0, 0, 1], [0, 0, 0, 0]]
        ]
    },
    {
        name: "Z",
        color: "purple",
        shape: [
            [[0, 0, 0, 0], [0, 1, 1, 0], [0, 0, 1, 1], [0, 0, 0, 0]],
            [[0, 0, 0, 1], [0, 0, 1, 1], [0, 0, 1, 0], [0, 0, 0, 0]]
        ]
    },
    {
        name: "I",
        color: "red",
        shape: [
            [[0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]],
            [[0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0]]
        ]
    },
    {
        name: "T",
        color: "yellow",
        shape: [
            [[0, 0, 0, 0], [0, 1, 1, 1], [0, 0, 1, 0], [0, 0, 0, 0]],
            [[0, 0, 1, 0], [0, 0, 1, 1], [0, 0, 1, 0], [0, 0, 0, 0]],
            [[0, 0, 1, 0], [0, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]],
            [[0, 0, 1, 0], [0, 1, 1, 0], [0, 0, 1, 0], [0, 0, 0, 0]]
        ]
    },
    {
        name: "L",
        color: "green",
        shape: [
            [[0, 0, 0, 0], [0, 1, 1, 1], [0, 1, 0, 0], [0, 0, 0, 0]],
            [[0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 1], [0, 0, 0, 0]],
            [[0, 0, 0, 1], [0, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]],
            [[0, 1, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 0, 0]]
        ]
    },
    {
        name: "J",
        color: "blue",
        shape: [
            [[0, 0, 0, 0], [0, 1, 1, 1], [0, 0, 0, 1], [0, 0, 0, 0]],
            [[0, 0, 1, 1], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 0, 0]],
            [[0, 1, 0, 0], [0, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]],
            [[0, 0, 1, 0], [0, 0, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]]
        ]
    }
];

this.rotation = function() {
    this.eraseBeforeBlock(); // 모양 변경되기 전에꺼 지움
    this.shapeIndex = (this.shapeIndex + 1) % this.type.shape.length;
    this.shape = this.type.shape[this.shapeIndex];
    this.drawBlock(this.x, this.y); // 모양 변경된거 그림
}

처음에는 블럭의 중심을 두고 그 회전 알고리즘을 짜서 블럭 모양을 변경하려고 했다. 그런데 블럭 배열이 홀수가 아니라서(끄응..;;) 생각보다 어려웠다. 그래서 그냥 4가지 방향의 블럭 모양을 하드코딩해서 배열에 때려박았다.

# 회전 시 블럭 충돌 체크

const NONE_DUPLICATED = 0; // 충돌 없음
const LEFT_DUPLICATED = 1; // 왼쪽 충돌
const RIGHT_DUPLICATED = 2; // 오른쪽 충돌
const EITHER_DUPLICATED = 3; // 왼쪽이나 오른쪽 충돌

this.isDuplicatedBlockOrOutOfGameScreen = function(x, y) {
    for (var i = 0; i < SMALL_BLOCK_NUM; i++) {
        for (var j = 0; j < SMALL_BLOCK_NUM; j++) {
            var nx = x + i;
            var ny = y + j;
            if (this.shape[j][i] == 0) continue;

            // out of game screen
            if (nx < 0) {
                console.log("LEFT DUPL");
                return LEFT_DUPLICATED;
            }
            if (GAME_SCREEN_WIDTH_NUM < nx) {
                console.log("RIGHT DUPL");
                return RIGHT_DUPLICATED;
            }
            // duplicated another block
            if (gameScreenArray[ny][nx] != -1) {
                console.log("EITHER DUPL");
                return EITHER_DUPLICATED;
            }
        }
    }
    return NONE_DUPLICATED;
};


this.rotation = function() {
    this.eraseBeforeBlock();
    this.shapeIndex = (this.shapeIndex + 1) % this.type.shape.length;
    this.shape = this.type.shape[this.shapeIndex];
    var checkDuplicated = this.isDuplicatedBlockOrOutOfGameScreen(
        this.x,
        this.y
    );
    // 모양 변경시 충돌이라면
    if (checkDuplicated != NONE_DUPLICATED) {
        var moveIndex = 0;
        // 왼쪽으로 한 칸씩 움직이면서 충돌 확인
        if (
            checkDuplicated == LEFT_DUPLICATED ||
            checkDuplicated == EITHER_DUPLICATED
        ) {
            for (var i = 1; i < SMALL_BLOCK_NUM; i++) {
               if (
                    this.isDuplicatedBlockOrOutOfGameScreen(
                        this.x + i,
                        this.y
                    ) == NONE_DUPLICATED
                ) {
                    moveIndex = i;
                    break;
                }
            }
        }
        // 오른쪽으로 한 칸씩 움직이면서 충돌 확인
        if (
            checkDuplicated == RIGHT_DUPLICATED ||
            checkDuplicated == EITHER_DUPLICATED
        ) {
            for (var i = 1; i < SMALL_BLOCK_NUM; i++) {
                if (
                    this.isDuplicatedBlockOrOutOfGameScreen(
                        this.x - i,
                        this.y
                    ) == NONE_DUPLICATED
                ) {
                    moveIndex = -i;
                    console.log(-moveIndex);
                    break;
                }
            }
        }

        // 움직여도 다 충돌인 경우
        if (moveIndex == 0) {
            // 이전 모양으로 되돌리기
            this.shapeIndex =
                (this.shapeIndex + this.type.shape.length - 1) %
                this.type.shape.length;
            this.shape = this.type.shape[this.shapeIndex];
        } else {
            this.x += moveIndex;
        }
    }
    this.drawBlock(this.x, this.y);
};

중요하게 생각했던게 다른 블럭도 마찬가지이지만 블럭의 방향을 변경했을 때 다른 블럭과의 충돌이 발생했을 때였다. (특히 I 블럭)
몇 개의 테트리스 게임을 탐색해본 결과 좌우 둘 중 하나 충돌인 경우 충돌되지 않게 안쪽으로 옮겨준다.
좌우 둘 다 충돌인 경우 변경되지 않는다.
변경한 경우 바닥충돌인 경우, 이 경우 경우의 수가 좀 더 다양했는데 어떤 곳은 변경될 수 있는 위치로 이동해서 블럭 모양을 변경한다. 어떤 경우는 변경되지 않는다. 나는 일단 변경할 수 없게 개발하기로 했다.

코드 리팩토링하고 디자인 고치고 점수 추가하는 등 추가할거는 아직 많지만 테트리스 기본기능은 거의 끝난 듯.
그리고 포매터 설정도 손 좀 봐야겠다..


깃허브
develop rotation block shape
Resolve conflict when block rotation

320x100

댓글