고전 Snake 게임 만들기 - React II
이것은 자습서Create the classic Snake game - React의 두 번째 부분입니다. 첫 번째 부분은 에서 찾을 수 있습니다.
튜토리얼의 첫 번째 부분을 따랐다면 다음이 있어야 합니다.
누락된 부품과 기능을 계속 살펴보겠습니다.
목차 - 파트 2
1. 반대 방향 키에 대한 충돌 감지 수정
Currently, the collision detection return true if we press the opposite key of the current direction the snake is moving. For example: The key ArrowRight
is pressed, so the snake moves right, and then the key ArrowLeft
is pressed. This would trigger a collision, which is a wrong behaviour. We have to fix this.
An easy way to fix this, is to filter out keys which are in the opposite direction. Since we have a state for direction and coordinates for arrow keys, we can simply sum up the current direction and the arrow direction.
The sum of x-coordinates for ArrowLeft and ArrowRight equal 0 and return a falsy value, hence this can be filtered.
ArrowLeft: { x: -1, y: 0 },
ArrowRight: { x: 1, y: 0 },
Update the moveSnake with the following code:
const moveSnake = (event: React.KeyboardEvent) => {
const { key } = event;
// Check if key is arrow key
if (
key === 'ArrowUp' ||
key === 'ArrowDown' ||
key === 'ArrowRight' ||
key === 'ArrowLeft'
) {
// disable backwards key, this means no collision when going right, and then pressing ArrowLeft
if (
direction.x + directions[key].x &&
direction.y + directions[key].y
) {
setDirection(directions[key]);
}
}
};
2. 스타일링 업데이트 및 자동 초점
The styling of the game needs some improvement, and we have to add an overlay, if we lost the game, and autofocus. The styling will be made in the App.css
, there are plenty of other ways to do styling in a React application. What styling method do you prefer? Leave a comment.
The game wrapper should be autofocussed, after the start button is clicked. We have access to the focus()
method, when we use the useRef
hook.
Add the wrapperRef and a state for isPlaying:
// add wrapper ref and isPlaying flag for showing start button
const wrapperRef = useRef<HTMLDivElement>(null);
const [isPlaying, setIsPlaying] = useState<boolean>(false);
Now we have to update the startGame and endGame function:
// update startGame
const startGame = () => {
setIsPlaying(true);
setSnake(SNAKE_START);
setApple(APPLE_START);
setDirection(DIRECTION_START);
setSpeed(SPEED);
setGameOver(false);
wrapperRef.current?.focus();
};
// update endGame
const endGame = () => {
setIsPlaying(false);
setSpeed(null);
setGameOver(true);
};
Now we update the wrapper with some classNames and some condition for an overlay and the reference.
// Update div with classes and flag for showing buttons, conditional styles
return (
<div className="wrapper">
<div
ref={wrapperRef}
className="canvas"
role="button"
tabIndex={0}
onKeyDown={(event: React.KeyboardEvent) => moveSnake(event)}
>
<canvas
style={
gameOver
? { border: '1px solid black', opacity: 0.5 }
: { border: '1px solid black' }
}
ref={canvasRef}
width={CANVAS_SIZE.x}
height={CANVAS_SIZE.y}
/>
{gameOver && <div className="game-over">Game Over</div>}
{!isPlaying && (
<button className="start" onClick={startGame}>
Start Game
</button>
)}
</div>
</div>
);
Now we can update our styling.
.wrapper {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: 100vh;
}
.canvas {
display: flex;
justify-content: center;
align-items: center;
background: rgb(151, 216, 148);
position: relative;
}
.start {
font-size: 1rem;
position: absolute;
border: 1px solid black;
background: none;
border-radius: 1rem;
padding: 1rem;
outline: none;
}
.start:hover {
border: none;
background: white;
}
.game-over {
position: absolute;
font-size: 5rem;
margin-bottom: 10rem;
}
The fillStyle should be updated as well from red
and green
to #1C1B17
, so we have this retro feeling/styling of the game.
We have now a working and styled version of the Classic Snake Game. Well, done. 😎
What's next?
- Adding points
- Game End
3. 게임 포인트
Add state for points:
const [points, setPoints] = useState<number>(0);
Add setPoints to startGame to reset score:
setPoints(0);
Increase points if apple is eaten, add this to checkAppleCollision
:
setPoints(points + 1);
Add points to game wrapper:
<p className="points">{points}</p>
Add some styling for the points:
.points {
position: absolute;
bottom: 0;
right: 1rem;
font-size: 2rem;
}
4. 게임 종료
We have to define a condition, when somebody has finished the game, which is unlikely, though to be feature-complete. The game end, besides a collision, would be the reaching of the maximum of available points. With the current scaling, the calculation is 40x40 = 1600points.
So we just add a condition to check if the maxPoints are reached and update the state and show some message.
We add the state to track if the game hasFinished
const [hasFinishedGame, setHasFinishedGame] = useState<boolean>(
false,
);
We add some condition to show the hasFinished message.
{
hasFinishedGame && <p className="finished-game">Congratulations</p>;
}
.finished-game {
position: absolute;
top: 60px;
font-size: 5rem;
color: red;
text-decoration: underline;
}
We add a variable for maxPoints and import it into App.tsx
:
export const maxPoints = 1600;
We add the check if maxPoints have been reached:
const checkAppleCollision = (newSnake: ICoords[]) => {
if (newSnake[0].x === apple.x && newSnake[0].y === apple.y) {
let newApple = createRandomApple();
while (checkCollision(newApple, newSnake)) {
newApple = createRandomApple();
}
setPoints(points + 1);
if (points === maxPoints) {
setHasFinishedGame(true);
endGame();
}
setApple(newApple);
return true;
}
return false;
};
In case hasFinishedGame has been set to true and we start a new game, the value has to be resetted.
const startGame = () => {
setHasFinishedGame(false);
setPoints(0);
setIsPlaying(true);
setSnake(snake_start);
setApple(apple_start);
setDirection(direction_start);
setSpeed(initial_speed);
setGameOver(false);
wrapperRef.current?.focus();
};
That's it. We can always come back and add more features, like sound effects or saving the score in localStorage or ...
Yay, ✨✨ Congratulations ✨✨. Well, done.
Your Game should look like this.읽어 주셔서 감사합니다. 궁금한 점이 있으면 댓글 기능을 사용하거나 메시지를 보내주세요.
참조(및 큰 감사): Maksim 및 .
Reference
이 문제에 관하여(고전 Snake 게임 만들기 - React II), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/mariokandut/create-the-classic-snake-game-react-ii-3o8h텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)