react.js

리액트로 테트리스 만들어보자[1]

윤만석 2022. 10. 31. 11:43

App.js 전체 코드입니다.

난잡하지만 끊어서 보겠습니다.

 

첫번째 글에서는 커스텀훅으로 게임화면,크기를 지정하고

canvas를 이용해 게임보드와 현재 블록, 그리고 다음 블록이 보여질 박스까지 그려보겠습니다.

 

커스텀 훅을 만들어 화면의 가로세로 너비를 설정합니다.

UseCustomSize.js

import { useEffect, useState } from "react";

export const useCustomSize = () => {
  const [width, setWidth] = useState(0);
  const [height, setHeight] = useState(0);
  const [blockSize, setBlockSize] = useState(0);

  useEffect(() => {
    const setClientWidthHeight = () => {
      //테트리스 세로:21칸 가로:10칸
      const columnBlocks = 21;
      const rowBlocks = 10;

      //화면의 가로길이 window.innerWidth기준으로 최대 600으로 제한.
      const windowInnerWidth =
        window.innerWidth > 600 ? 600 : window.innerWidth;

      const boardWidth = Math.floor(windowInnerWidth * 0.6);

      //블록 사이즈는 보드의 가로길이 / 10칸
      setBlockSize(Math.floor(boardWidth / columnBlocks));

      setHeight(Math.floor(blockSize * columnBlocks));
      setWidth(Math.floor(blockSize * rowBlocks));
    };
    setClientWidthHeight();

    //화면 크기를 변경할때의 이벤트리스너를 추가해주면 화면크기가 바뀔때마다 이벤트 발생
    window.addEventListener("resize", setClientWidthHeight);

    //UseEffect 언마운트시 이벤트리스너를 해제한다.
    return () => {
      window.removeEventListener("resize", setClientWidthHeight);
    };
  });

  const clientRect = { width, height, blockSize };

  return clientRect;
};


/*App.js 에서 가로세로 너비를 설정합니다.

  const clientRect = useCustomSize();
  const boardWidth = clientRect.width;
  const boardHeight = clientRect.height;
  const blockSize = Math.floor(boardWidth / 10);
  
 */

 

querySelector로 ctx를 2개 정의해주었습니다. 하나는 게임보드, 하나는 다음블록이 보이는 박스입니다.

  //보드
  const board = document.querySelector(".board");
  const boardctx = board?.getContext("2d");

  //다음블록이 나오는 박스
  const box = document.querySelector(".nextBlockBox");
  const boxctx = box?.getContext("2d");
  //.?는 옵셔널체이닝입니다. undefined가 아닌경우 이후 메소드를 실행합니다.

그리고 보드를 그려줍니다. 

  const reDraw = () => {
    drawBlock(CurrentBlock, boardctx);
    drawBlock(NextBlock, boxctx, "NextBlockBox");
    drawBoard(map, boardctx);
  };

useEffect(() => {
    const setBoard = () => {
      if (board) {
        board.width = boardWidth;
        board.height = boardHeight;
        boardctx?.scale(blockSize, blockSize);
      }
      if (box) {
        box.width = blockSize * 5;
        box.height = blockSize * 5;
        boxctx?.scale(blockSize, blockSize);
      }
      reDraw();
    };
    setBoard();
  });

drawBlock,drawBoard는 function 디렉토리에 drawBlock,drawBoard로 각각 정의했습니다.

//rectDesign.js
export const roundRect = (ctx, x, y, width, height, radius = 1) => {
  if (radius !== 0) {
    radius = { tl: radius, tr: radius, br: radius, bl: radius };
  } else {
    radius = { tl: 0, tr: 0, br: 0, bl: 0 };
  }
  ctx.beginPath();
  ctx.moveTo(x + radius.tl, y);
  ctx.lineTo(x + width - radius.tr, y);
  ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr);
  ctx.lineTo(x + width, y + height - radius.br);
  ctx.quadraticCurveTo(
    x + width,
    y + height,
    x + width - radius.br,
    y + height
  );
  ctx.lineTo(x + radius.bl, y + height);
  ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl);
  ctx.lineTo(x, y + radius.tl);
  ctx.quadraticCurveTo(x, y, x + radius.tl, y);
  ctx.closePath();

  ctx.fill();
  ctx.lineWidth = 0.088;
  ctx.strokeStyle = "white";
  ctx.stroke();
};


//DrawBoard.js
export const drawBoard = (matrix, ctx) => {
  if (ctx) {
    matrix.forEach((row, y) => {
      row.forEach((value, x) => {
        if (value > 0) {
          ctx.fillStyle = readColor(value);
          roundRect(ctx, x, y, 1, 1, 0.1);
        }
      });
    });
  }
};

//DrawBlock.js
export const drawBlock = (block, ctx, type = "default") => {
  if (ctx && block !== undefined) {
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    block.type.forEach((row, y) => {
      row.forEach((value, x) => {
        if (value > 0) {
          ctx.fillStyle = blockColor(block.type);

          if (type === "NextBlockBox")
            roundRect(ctx, 1 + x + block.x, 1 + y + block.y, 1, 1, 0.1);
          else roundRect(ctx, x + block.x, y + block.y, 1, 1, 0.1);
        } else if (value < 0) {
          ctx.fillStyle = blockColor(block.type);
          //ctx.globalAlpha = 0.5;
          roundRect(ctx, x + block.x, y + block.y, 1, 1, 0.1);
        }
      });
    });
  }
};

화면에 그림을 그리는 canvas를 사용했습니다.

roundRect으로 그림을 그릴 블록의 크기와 좌표, 테두리, boarder등을 설정했고 아래에서 fillStyle매소드로 색을 설정했습니다.

 

https://github.com/Mansook/TetrisBot

 

GitHub - Mansook/TetrisBot: tetris

tetris. Contribute to Mansook/TetrisBot development by creating an account on GitHub.

github.com