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