trail, tailLength 변수 만들기

뱀의 몸은 여러 개의 블럭으로 이루어져 있습니다. 뱀이 상하좌우로 움직일 때 이전의 위치들을 추적하는 것이 중요합니다. trail이라는 배열을 만들어 저장하도록 하겠습니다.

현재 꼬리의 길이를 저장하는 tailLength라는 변수도 생성하도록 하겠습니다. 초기값으로 5를 지정하면 게임을 시작할 때 뱀의 블럭 수는 5개가 됩니다.

let positionX = 10, positionY = 10
const gridSize = 20, tileCount = 20

let velocityX = 0, velocityY = 0

let appleX = 15, appleY = 15

const trail = []
let tailLength = 5

 

trail 배열 갱신하기

trail 배열에 positionX, positionY의 위치를 객체 형태로 저장하는 원소들을 채워넣도록 하겠습니다. 단, tailLength 만큼의 원소를 저장하며, trail 배열의 길이가 tailLength를 초과하면 shift()를 통해 배열의 맨 앞 원소를 제거하는 작업을 해야 합니다.

function game() {
    
    // ...
    
    ctx.fillStyle = "black"
    ctx.fillRect(0, 0, canvas.width, canvas.height)

    ctx.fillStyle = "lime"
    ctx.fillRect(positionX * gridSize, positionY * gridSize, gridSize - 2, gridSize - 2)
    
    trail.push({
        x: positionX, 
        y: positionY
    })
    while(trail.length > tailLength) {
        trail.shift()
    }
    
    if(appleX === positionX && appleY === positionY) {
        appleX = Math.floor(Math.random() * tileCount)
        appleY = Math.floor(Math.random() * tileCount)
    }
    
    // ...
}

여기까지 작성해도 표면적인 변화는 없습니다. 뱀의 몸을 그리는 렌더링 부분이 제대로 작성되지 않았기 때문입니다.

 

뱀 렌더링 부분 구현

game 함수에서 뱀을 그리는 부분을 아래와 같이 변경합니다.

ctx.fillStyle = "lime"
// 삭제: ctx.fillRect(positionX * gridSize, positionY * gridSize, gridSize - 2, gridSize - 2)

for(let i = 0; i < trail.length; i++) {
    ctx.fillRect(trail[i].x * gridSize, trail[i].y * gridSize, gridSize - 2, gridSize - 2)
}

기존의 단일 position을 렌더링하던 것이 for문으로 변경되어 trail의 배열을 순회하도록 바뀌었습니다. 사이즈 부분은 변함이 없고, 프레임 당 trail의 배열을 따라 블럭을 여러 개 그립니다. 이렇게 하면 드디어 뱀 모양으로 이동하는 효과가 나타나게 됩니다.

그리고 뱀의 머리와 꼬리가 부딪혔을 때 게임오버가 되도록 합니다. 게임오버가 되면 tailLength 가 5인 처음 상황으로 돌아가도록 만듭니다.

for(let i = 0; i < trail.length; i++) {
    ctx.fillRect(trail[i].x * gridSize, trail[i].y * gridSize, gridSize - 2, gridSize - 2)
    
    if(trail[i].x === positionX && trail[i].y === positionY) {
        tail = 5
    }
}

 

 

최종 완성 코드


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Snake Game</title>
</head>
<body>
<canvas id="game-canvas" width="400" height="400"></canvas>
<script>
const canvas = document.getElementById("game-canvas")
const ctx = canvas.getContext("2d")
window.onload = () => {
document.addEventListener("keydown", keyPush)
// 게임 시작시 초당 15fps로 game 함수 호출
setInterval(game, 1000 / 15)
}
// 뱀의 위치
let positionX = 10, positionY = 10
// gridSize: 가로세로 20px , tileCount = 가로세로 20개씩 총 400개의 타일
const gridSize = 20, tileCount = 20
// 뱀이 움직이는 방향을 설정
let velocityX = 0, velocityY = 0
// 사과(먹이) 위치변수
let appleX = 15, appleY = 15
// 뱀의 몸통을 저장하는 배열
const trail = []
// 현재 뱀의 길이
let tailLength = 5
function game() {
// velocity 상황에 따라 positionXY의 위치를 결정
positionX += velocityX
positionY += velocityY
// 뱀 머리가 경계에 있을 떄 처리
if (positionX < 0) {
positionX = tileCount – 1
}
if (positionX > tileCount – 1) {
positionX = 0
}
if (positionY < 0) {
positionY = tileCount – 1
}
if (positionY > tileCount – 1) {
positionY = 0
}
ctx.fillStyle = "black"
ctx.fillRect(0, 0, canvas.width, canvas.height)
// 뱀 그리기
ctx.fillStyle = "lime"
for(let i = 0; i < trail.length; i++) {
// trail 배열만큼 그림
ctx.fillRect(trail[i].x * gridSize, trail[i].y * gridSize, gridSize – 2, gridSize – 2)
// 게임 오버 case 1
if(trail[i].x === positionX && trail[i].y === positionY) {
tail = 5
}
}
// 게임이 진행될 때마다 positionXY를 trail 배열에 삽입
trail.push({
x: positionX,
y: positionY
})
// 단, trail의 크기는 tailLength를 넘지 않게
while(trail.length > tailLength) {
trail.shift()
}
// 사과 먹었을 때
if(appleX === positionX && appleY === positionY) {
tailLength++
appleX = Math.floor(Math.random() * tileCount)
appleY = Math.floor(Math.random() * tileCount)
}
// 사과 그리기
ctx.fillStyle = "red"
ctx.fillRect(appleX * gridSize, appleY * gridSize, gridSize – 2, gridSize – 2)
}
// 방향키 이벤트
function keyPush(evt) {
// arrows keys and it's left and then clockwise
switch (evt.keyCode) {
case 37:
velocityX = -1;
velocityY = 0;
break;
case 38:
velocityX = 0;
velocityY = -1;
break;
case 39:
velocityX = 1;
velocityY = 0;
break;
case 40:
velocityX = 0;
velocityY = 1;
break;
}
}
</script>
</body>
</html>

view raw

snake.html

hosted with ❤ by GitHub

 

문의 | 코멘트 또는 yoonbumtae@gmail.com


카테고리: WEB: Frontend


1개의 댓글

쟁선 · 2021년 6월 2일 1:25 오전

감사합니다
도움이 많이 되었습니다

답글 남기기

Avatar placeholder

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다