react.js

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

윤만석 2022. 10. 31. 13:33

https://orgin00.tistory.com/49

 

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

https://orgin00.tistory.com/48 리액트로 테트리스 만들어보자[1] App.js 전체 코드입니다. 난잡하지만 끊어서 보겠습니다. 첫번째 글에서는 커스텀훅으로 게임화면,크기를 지정하고 canvas를 이용해 게임

orgin00.tistory.com

 

지금까지 작성한 그림그리기와 기본 상태관리를 바탕으로

이번 글 부터는 그림을 그리고, 게임 로직을 작성하겠습니다.

 

querySelector와 캔버스 매소드인 getContext를 이용해서 드로잉 컨텍스트를 반환합니다. getContext의 인자는 2d 입니다.

보드와, 다음 블록이 나오는 박스의 컨텍스트를 반환하면 reDraw함수를 작성합니다.

 

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

  //다음블록이 나오는 박스
  const box = document.querySelector(".nextBlockBox");
  const boxctx = box?.getContext("2d");
  //그림 그리기
  const reDraw = () => {
    drawBlock(CurrentBlock, boardctx);
    drawBlock(NextBlock, boxctx, "NextBlockBox");
    drawBoard(map, boardctx);
  };

drawBlock.js

//함수의 인자로 block,context,그리고 type이 있습니다. 
export const drawBlock = (block, ctx, type = "default") => {
  if (ctx && block !== undefined) { //ctx가 존재하고 block이 undefined가 아닐경우
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    block.type.forEach((row, y) => { //y열 row열요소
      row.forEach((value, x) => { row열 요소에대해 x행
        if (value > 0) {
          ctx.fillStyle = blockColor(block.type); //block.type(number)에대해 색설정
			//nextBlockBox에 들어가는 블록의 경우 x,y좌표가 +1 되어있습니다.
          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);

          roundRect(ctx, x + block.x, y + block.y, 1, 1, 0.1);
        }
      });
    });
  }
};

drawBoard.js

export const drawBoard = (matrix, ctx) => {
  if (ctx) {
    matrix.forEach((row, y) => {
      row.forEach((value, x) => {
        if (value > 0) {  //배열에 0은 빈칸이고 1보다 큰경우 블록이므로 색을 칠해주어야합니다.
          ctx.fillStyle = readColor(value);
          roundRect(ctx, x, y, 1, 1, 0.1);
        }
      });
    });
  }
};

 

roundRect.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();
};

 

이제 UseEffect를 이용하여 그림을 그립니다.

 useEffect(() => {
    const setBoard = () => {
      if (board) {
        board.width = boardWidth;
        board.height = boardHeight;
        boardctx?.scale(blockSize, blockSize); //1 pixel을 blockSize만큼 scale합니다.
      }
      if (box) {
        box.width = blockSize * 5;
        box.height = blockSize * 5;
        boxctx?.scale(blockSize, blockSize);
      }
      reDraw();
    };
    setBoard();
  });

이제 그림을 그렸으므로, 게임로직을 작성하겠습니다.

 

필요한 함수를 정의합니다.

  //현재 블록이 바닥에 정지하는 경우 nextblock으로 바꾸고, nextblock은 랜덤 블록으로 설정합니다.
  const ChangeBlocks = () => {
    setCurrentBlock({
      ...NextBlock,
      x: NextBlock.x + 3,
    });
    setShowBlock({
      x: NextBlock.x + 3,
      y: NextBlock.y,
      type: NextBlock.type.map((row) => row.map((c) => c * -1)),
    });

    setNextBlock(createRandomBlock());
  };

 

페이지가 마운트되면 initMatrix를 실행하고, currentblock을 x축으로 3만큼 이동시킵니다.

 useEffect(() => {
    setMap(initMatrix());
    setCurrentBlock({ ...CurrentBlock, x: CurrentBlock.x + 3 });
    setShowBlock({
      ...CurrentBlock,
    });
  }, []);

 

다음 글에서 블록이 시간에 따라 자동으로 떨어지고, 키보드 입력을 받아 움직이게 만들어 보겠습니다.

 

https://github.com/Mansook/TetrisBot