본문 바로가기

javaScript

JavaScript 테트리스 만들기 - 2

728x90

 

2024.04.02 - [javaScript] - JavaScript 테트리스 만들기 - 1

 

JavaScript 테트리스 만들기 - 1

요즘 개발 트렌드는 javaScript를 편하게 사용하도록 도와주는 라이브러리를 활용한 개발이 아닌 순수(바닐라) javaScript를 이용한 개발이라고 어디에선가 들은 적이 있다. javaScript의 꾸준히 업데이

son33.tistory.com

ㄴ 1에 이어서 2편으로 블록을 생성하고 시간이 지나면 블록이 아래로 내려오도록 만들어보았다.

1. 게임시작

// 게임시작
function play() {
    createBlock();
    setInterval(downBlock, 1000);
}

1) createBlock()

먼저 시작버튼을 누르면 블록을 생성해 게임이 시작되도록 createBlock()을 작성해 준다.

2) setInterval()

시간이 지나면 블록이 아래로 내려오도록 setInterval함수를 이용해 1초에 한 번씩 downBlock()을 호출시켜 준다. setInterval은 나중에 레벨이 오르면 블록이 더 빨리 내려오도록 수정할 예정이다.

2. 블록 만들기

// 블록만들기
function createBlock() {
    const shapes = [
        [[1, 1], [1, 1]], // ㅁ
        [[1, 1, 1, 1]], // I
        [[1, 1, 1], [0, 1, 0]], // ㅗ
        [[1, 1, 0], [0, 1, 1]], // Z
        [[0, 1, 1], [1, 1, 0]], // S
        [[1, 1, 1], [0, 0, 1]], // L
        [[1, 1, 1], [1, 0, 0]] // J
    ];
    const colors = ["red", "orange", "yellow", "green", "blue", "navy", "purple"]
    const random = Math.floor(Math.random() * shapes.length);
    const shape = shapes[random];
    color = colors[random];
    drawBlock(3, 0, shape);
};

1) createBlock()

클래식 테트리스에 필요한 블록은 총 7개로 ㅁ, 1, ㅗ, z, s, l, j가 있다.

블록을 생성하기 위한 다양한 방법이 존재하지만 쉬운 방법 중 하나인 행렬을 이용해 블록을 생성하겠다. 배열을 이용해 7개의 배열을 생성해 주고,  1 x 4, 2 x 2, 3 x 2의 행렬을 이용하여 0과 1을 사용해 블록 모양 행렬을 생성해 준다. 여기서 0은 빈 공간, 1은 블록공간이 될 것이다.

다음 각 블록들을 이쁘게 만들어 줄 색상값을 가진 colors 배열을 지정해 준 뒤, Math.random()을 이용하여 7가지 숫자를 랜덤으로 생성해 random으로 나온 숫자에 해당하는 shapes, colors 배열의 값을 shape와 color에 담아준다. 그리고 맨 처음 블록이 생성될 위치 값을 x, y에 지정해 준 뒤 생성한 블록을 보드판 위에 그려줄 drawBlock()에 시작 x, y, shape를 넣어서 호출해 준다.

2) drawBlock()

// 블록그리기
function drawBlock(startX, startY, shape) {
    let block = [];
    if(shape[0][0] == 0){
        if(shape[0][1] == 0){
            startX--;
        } 
        startX--;
    }
    for (let y = 0; y < shape.length; y++) {
        for (let x = 0; x < shape[y].length; x++) {
            if (shape[y][x] == 1) {
                const cell = document.querySelector('.col' + (startX + x) + '.row' + (startY + y));
                if(cell){
                    cell.classList.add("block");
                    if(cell.style.backgroundColor == ""){
                        cell.style.backgroundColor = color;
                    }
                    block.push(cell);
                } else {
                    return false;
                }
            }
        }
    }
    return block;
}

drawBlock()에선 block값을 넣을 빈배열 block을 만들어주고, 받은 shap의 0번째 배열의 0, 1의 값이 0인지를 확인해 시작위치를 shape의 0,0으로 조정해 준다.

다음 shape의 길이와 높이를 이용해 반복을 돌리면서 1에 해당하는 cell을 찾은 뒤, 만약 cell이 제대로 선택되었으면, 해당 cell에 "block"클래스를 추가하고, 기존에 색상이 없을 때 color를 입혀준다. 그렇게 생성된 cell은 block에 담고, 반복이 끝나면 block을 반환시켜 준다. 여기서 없는 cell을 선택하면 false를 반환하는데 이는 나중에 블록 회전을 시킬 때 사용된다.

지금까지 작업한 뒤 게임시작 버튼을 누르면 랜덤으로 블록이 생성되는 것을 확인할 수 있다.

3) downBlock()

마지막으로 시간이 지나면 블록이 아래로 내려오도록 하겠다.

// 시간이 지나면 블록내리기
function downBlock() {
    let nowBlock = document.querySelectorAll(".block");
    let currentShape = [];
    let startX = parseInt(nowBlock[0].classList[0].replace("col", ""));
    let startY = parseInt(nowBlock[0].classList[1].replace("row", ""));
    let shape = blockShape(nowBlock);
    if (collisionBlock(nowBlock, 0)) {
        fixBlock(nowBlock);
        return createBlock();
    } else {
        deleteBlock(nowBlock);
        deleteRow(nowBlock);
        drawBlock(startX, startY + 1, shape);
    }
}

블록이 내려오는 것을 구현하는 많은 방법이 있지만 생성되어 있는 블록의 위치를 가져오고, 가져온 블록을 배열모양으로 변환시켰다. 이후 충돌여부를 확인한 후 충돌되면 fixed-block으로 바꾸고, 줄 삭제여부를 확인해 줄을 삭제한 뒤 createBlock()을 호출시키게 했다. 아니라면 기존 블록은 삭제시키고 한 칸 내려 새 블록을 그리는 식으로 코드를 구현했다.

먼저 클래스 명이 "block"인 요소들을 가져와 nowBlock 변수에 저장해 주고, blockShape()에 nowBlock을 넘겨 가져온 nowBlock의 배열모양을 받아온다. 다음 collisionBlock()에 nowBlock과 방향 값 0을 넘겨 이동하고자 하는 위치에 충돌이 발생하는지 확인하고, true면 nowBlock을 고정시키고 createBlock()을 다시 실행시킨다. 만약 충돌이 발생하지 않았다면 nowBlock은 삭제시키고, 바로 밑에 새 block을 그리게 했다.

4) blockShape()

// 블록을 배열로 변환
function blockShape(nowBlock) {
    let minRow = 20;
    let maxRow = 0;
    let minCol = 10;
    let maxCol = 0;
    nowBlock.forEach(block => {
        let col = parseInt(block.classList[0].replace("col", ""));
        let row = parseInt(block.classList[1].replace("row", ""));
        if (col < minCol) minCol = col;
        if (col > maxCol) maxCol = col;
        if (row < minRow) minRow = row;
        if (row > maxRow) maxRow = row;
    });
    // 모양을 담을 2차원 배열 생성
    let currentShape = Array.from({ length: maxRow - minRow + 1 }, () => Array.from({ length: maxCol - minCol + 1 }, () => 0));
    // NodeList에서 각 요소의 위치에 1로 설정
    nowBlock.forEach(shape => {
        const col = parseInt(shape.classList[0].replace("col", "")) - minCol;
        const row = parseInt(shape.classList[1].replace("row", "")) - minRow;
        currentShape[row][col] = 1;
    });
    return currentShape;
}

blockShape()는 다음과 같다.

5) deleteBlock()

// 기존 블록 지우기
function deleteBlock(nowBlock) {
    nowBlock.forEach(block => {
        block.classList.remove("block");
        if (!block.classList.contains("fixed-block"))
            block.style.backgroundColor = "";
    });
}

deleteBlock 코드는 다음과 같다. 현재 블록을 전달받으면 반복을 통해 'block'클래스를 제거하고 색상도 제거하되 만약 'fixed-block'이라면 색상은 그대로 두게 한다.

지금까지 작업 완료 후 게임 시작을 누르면 블록이 1초마다 아래로 내려오는 것을 확인할 수 있다.

728x90

'javaScript' 카테고리의 다른 글

JavaScript 테트리스 만들기 - 4  (0) 2024.04.08
JavaScript 테트리스 만들기 - 3  (1) 2024.04.04
JavaScript 테트리스 만들기 - 1  (1) 2024.04.02