티스토리 뷰

728x90

 

 

 

 

 

테트리스

 

고전 게임인 테트리스를 Javascript를 사용하여 구현하였습니다.

 

 

주요 기능

 

  • 시작 버튼

 시작 버튼 클릭 시 테르리스 게임이 시작됩니다.

 

 

  • 점수 계산

 한 줄을 완성할 경우 스코어가 1점 오르고 해당 줄을 삭제합니다. 

 

 

소스 코드

 

전체 소스는 https://github.com/wonseok22/ToyProjects/tree/main/tetrisGame 를 참고하세요.

 

const tetrisInner = document.querySelector(".tetris__inner > ul");
      const tetrisS = document.querySelector(".tetris__wrap .s");

      const game_rows = 20;
      const game_cols = 10;

      let gameScore = 0;
      let gameDuration = 500;
      let gameInterval;
      let gameMovingItem;

      const blocks = {
        Tmino: [
          [
            [2, 1],
            [0, 1],
            [1, 0],
            [1, 1],
          ],
          [
            [1, 2],
            [0, 1],
            [1, 0],
            [1, 1],
          ],
          [
            [1, 2],
            [0, 1],
            [2, 1],
            [1, 1],
          ],
          [
            [2, 1],
            [1, 2],
            [1, 0],
            [1, 1],
          ],
        ],
        Imino: [
          [
            [0, 0],
            [1, 0],
            [2, 0],
            [3, 0],
          ],
          [
            [0, 0],
            [0, 1],
            [0, 2],
            [0, 3],
          ],
          [
            [0, 0],
            [1, 0],
            [2, 0],
            [3, 0],
          ],
          [
            [0, 0],
            [0, 1],
            [0, 2],
            [0, 3],
          ],
        ],
        Omino: [
          [
            [0, 0],
            [1, 0],
            [1, 1],
            [0, 1],
          ],
          [
            [0, 0],
            [1, 0],
            [1, 1],
            [0, 1],
          ],
          [
            [0, 0],
            [1, 0],
            [1, 1],
            [0, 1],
          ],
          [
            [0, 0],
            [1, 0],
            [1, 1],
            [0, 1],
          ],
        ],
        Zmino: [
          [
            [0, 0],
            [1, 0],
            [1, 1],
            [2, 1],
          ],
          [
            [1, 0],
            [1, 1],
            [0, 1],
            [0, 2],
          ],
          [
            [0, 0],
            [1, 0],
            [1, 1],
            [2, 1],
          ],
          [
            [1, 0],
            [1, 1],
            [0, 1],
            [0, 2],
          ],
        ],
        Smino: [
          [
            [0, 1],
            [1, 1],
            [1, 0],
            [2, 0],
          ],
          [
            [1, 0],
            [1, 1],
            [2, 1],
            [2, 2],
          ],
          [
            [0, 1],
            [1, 1],
            [1, 0],
            [2, 0],
          ],
          [
            [1, 0],
            [1, 1],
            [2, 1],
            [2, 2],
          ],
        ],
        Jmino: [
          [
            [1, 0],
            [1, 1],
            [1, 2],
            [0, 2],
          ],
          [
            [0, 1],
            [0, 2],
            [1, 2],
            [2, 2],
          ],
          [
            [0, 2],
            [1, 2],
            [0, 3],
            [0, 4],
          ],
          [
            [1, 2],
            [1, 1],
            [0, 1],
            [-1, 1],
          ],
        ],
        Lmino: [
          [
            [0, 0],
            [0, 1],
            [0, 2],
            [1, 2],
          ],
          [
            [0, 2],
            [0, 3],
            [1, 2],
            [2, 2],
          ],
          [
            [-1, 2],
            [0, 2],
            [0, 3],
            [0, 4],
          ],
          [
            [0, 1],
            [0, 2],
            [-1, 2],
            [-2, 2],
          ],
        ],
      };

      let isGame = false;
      const movingItem = {
        type: "Zmino",
        direction: 0,
        top: 0,
        left: 3,
      };

      document.querySelector(".btn").addEventListener("click", function () {
        if (!isGame) {
          isGame = true;
          init();
        }
      });

      // 시작하기
      function init() {
        gameMovingItem = { ...movingItem };

        //console.log(gameMovingItem)

        for (let i = 0; i < game_rows; i++) {
          tetrisLine();
        }
        //renderBlocks();
        generateNewBlock();
      }

      // 블록 만들기
      function tetrisLine() {
        const li = document.createElement("li");
        const ul = document.createElement("ul");
        for (let j = 0; j < game_cols; j++) {
          const block = document.createElement("li");
          ul.prepend(block);
        }
        li.prepend(ul);
        tetrisInner.prepend(li);
      }

      // 블록 출력하기
      function renderBlocks(moveType = " ") {
        const { type, direction, top, left } = gameMovingItem;

        const movingBlocks = document.querySelectorAll(".moving");
        movingBlocks.forEach((moving) => {
          moving.classList.remove(type, "moving");
        });

        blocks[type][direction].some((block) => {
          const x = block[0] + left;
          const y = block[1] + top;
          //console.log(tetrisInner)
          const target = tetrisInner.childNodes[y]
            ? tetrisInner.childNodes[y].childNodes[0].childNodes[x]
            : null;
          const isAvailable = checkEmpty(target);

          if (isAvailable) {
            target.classList.add(type, "moving");
          } else {
            gameMovingItem = { ...movingItem };

            if (moveType === "retry") {
              clearInterval(gameInterval);
              gameover();
            }

            setTimeout(() => {
              renderBlocks("retry");

              if (moveType === "top") {
                seizeBlock();
              }
            }, 0);
            return true;
          }
        });
        movingItem.left = left;
        movingItem.top = top;
        movingItem.direction = direction;
      }

      function seizeBlock() {
        const movingBlocks = document.querySelectorAll(".moving");
        movingBlocks.forEach((moving) => {
          moving.classList.remove("moving");
          moving.classList.add("seized");
        });
        checkMatch();
      }

      //블록 한줄 제거하기
      function checkMatch() {
        const childNodes = tetrisInner.childNodes;
        childNodes.forEach((child) => {
          let matched = true;
          child.children[0].childNodes.forEach((li) => {
            if (!li.classList.contains("seized")) {
              matched = false;
            }
          });
          if (matched) {
            child.remove();
            tetrisLine();
            gameScore++;
            tetrisS.innerText = "Score : " + gameScore;
          }
        });
        generateNewBlock();
      }

      //새로운 블록 만들기
      function generateNewBlock() {
        clearInterval(gameInterval);
        gameInterval = setInterval(() => {
          moveBlock("top", 1);
        }, gameDuration);

        const blockArray = Object.entries(blocks);
        const randomIndex = Math.floor(Math.random() * blockArray.length);
        movingItem.type = blockArray[randomIndex][0];
        movingItem.top = 0;
        movingItem.left = 3;
        movingItem.direction = 0;
        gameMovingItem = { ...movingItem };
        renderBlocks();
      }

      function checkEmpty(target) {
        if (!target || target.classList.contains("seized")) {
          return false;
        }
        return true;
      }

      // 블록 움직이기
      function moveBlock(moveType, amount) {
        gameMovingItem[moveType] += amount;
        renderBlocks(moveType);
      }

      // 모양 변경하기
      function changeDirectioin() {
        const direction = gameMovingItem.direction;
        direction === 3
          ? (gameMovingItem.direction = 0)
          : (gameMovingItem.direction += 1);
        renderBlocks();
      }

      // 스페이스바 누르기
      function dropBlock() {
        clearInterval(gameInterval);
        gameInterval = setInterval(() => {
          moveBlock("top", 1);
        }, 10);
      }

      function gameover() {
        //게임 끝났을 때
        isGame = false;
        console.log("게임오버");
      }

      // 키보드 이벤트
      document.addEventListener("keydown", (e) => {
        //console.log(e)
        switch (e.keyCode) {
          case 39:
            moveBlock("left", 1);
            break;
          case 37:
            moveBlock("left", -1);
            break;
          case 40:
            moveBlock("top", 1);
            break;
          case 38:
            changeDirectioin();
            break;
          case 32:
            dropBlock();
            break;
          default:
            break;
        }
      });
320x100
댓글
© 2022 WonSeok, All rights reserved