본문 바로가기
JavaScript

[JavaScript] 비행기 슈팅 게임 만들기 - (4) 충돌 감지와 게임 오버

by teamnova 2024. 10. 4.
728x90

안녕하세요.

오늘은 지난 시간에 이어서, 적과 총알이 충돌하면 적을 제거하고, 적과 비행기가 충돌하면 게임 오버가 되는 기능을 구현하겠습니다.

 

 

 

 

이전 코드는 다음 링크를 통해 확인할 수 있습니다.

2024.09.15 - [JavaScript] - [JavaScript] 비행기 슈팅 게임 만들기 - (3) 총알 발사하기

 

1. 게임 오버 상태 변수 설정

  • isGameOver 변수를 추가하여 게임이 종료되었는지 여부를 추적합니다. 초기값은 false로 설정됩니다.
let isGameOver = false; // 게임 오버 상태를 추적할 변수

 

2. 충돌 감지 함수 작성

  • 두 개의 객체가 충돌하는지 확인하는 함수입니다. 이 함수는 총알과 적, 적과 비행기의 충돌을 처리할 수 있습니다.
  • obj1과 obj2는 충돌을 검사할 두 개의 객체(총알, 적, 비행기 등)를 의미하며, 각각의 위치와 크기를 비교해 충돌 여부를 반환합니다.
// 충돌 감지 함수 (총알과 적의 충돌, 적과 비행기의 충돌을 처리)
function checkCollision(obj1, obj2) {
    // 두 객체가 겹쳤는지 여부를 반환
    return (
        obj1.x < obj2.x + obj2.width && // obj1의 오른쪽 경계가 obj2의 왼쪽 경계를 넘지 않음
        obj1.x + obj1.width > obj2.x && // obj1의 왼쪽 경계가 obj2의 오른쪽 경계를 넘음
        obj1.y < obj2.y + obj2.height && // obj1의 아래쪽 경계가 obj2의 위쪽 경계를 넘지 않음
        obj1.y + obj1.height > obj2.y // obj1의 위쪽 경계가 obj2의 아래쪽 경계를 넘음
    );
}

 

 

3. 충돌 처리 함수 작성

  • bullets 배열의 각 총알과 enemies 배열의 각 적에 대해 충돌을 확인하고, 충돌 시 총알과 적을 배열에서 제거합니다.
  • 또한 적과 비행기가 충돌하면 isGameOver를 true로 설정하여 게임 오버 상태로 만듭니다.
// 적과 총알 및 비행기의 충돌을 확인하고 처리하는 함수
function handleCollisions() {
    bullets.forEach((bullet, bulletIndex) => {
        enemies.forEach((enemy, enemyIndex) => {
            // 총알과 적의 충돌 처리
            if (checkCollision(bullet, enemy)) {
                // 충돌 시 적과 총알을 배열에서 제거
                bullets.splice(bulletIndex, 1); // 총알 제거
                enemies.splice(enemyIndex, 1); // 적 제거
            }
        });
    });

    enemies.forEach((enemy) => {
        // 적과 비행기의 충돌 처리
        if (checkCollision(enemy, plane)) {
            isGameOver = true; // 게임 오버 설정
        }
    });
}

 

4. 게임 오버 처리 함수 작성

  • 게임이 종료되면 화면에 "Game Over" 메시지를 표시하는 함수입니다. ctx.fillText()를 사용해 캔버스 중앙에 게임 오버 메시지를 출력합니다.
// 게임 오버를 처리하는 함수
function gameOver() {
    ctx.fillStyle = 'red'; // 게임 오버 메시지 색상 설정
    ctx.font = '48px Arial'; // 폰트 설정
    ctx.fillText('Game Over', canvas.width / 2 - 120, canvas.height / 2); // 화면에 메시지 표시
}

 

5. 게임 루프 수정

  • 게임 루프에서 isGameOver가 true일 경우, 게임을 중단하고 gameOver() 함수를 호출해 게임 오버 메시지를 표시합니다. 그렇지 않으면 계속해서 게임을 진행합니다.
// 게임 루프 함수: 게임의 각 프레임을 업데이트하고 렌더링하는 함수
function gameLoop() {
    if (isGameOver) {
        gameOver(); // 게임 오버 시 메시지 표시
        return; // 더 이상 게임을 진행하지 않음
    }
    ctx.clearRect(0, 0, canvas.width, canvas.height); // 캔버스 전체를 지워서 이전 프레임의 그림을 지움
    drawPlane(); // 비행기 그리기
    update(); // 비행기 위치 업데이트

    drawEnemies(); // 적 그리기
    updateEnemies(); // 적 위치 업데이트

    drawBullets(); // 총알 그리기
    updateBullets(); // 총알 위치 업데이트

    handleCollisions(); // 충돌 감지 및 처리

    requestAnimationFrame(gameLoop); // 다음 프레임 호출
}

 

 


 

현재까지 전체 코드

const canvas = document.getElementById('gameCanvas'); // HTML에서 캔버스를 가져옵니다.
const ctx = canvas.getContext('2d'); // 캔버스에서 2D 그리기 기능을 사용할 수 있게 합니다.

canvas.width = 400; // 캔버스의 너비를 400px로 설정
canvas.height = 600; // 캔버스의 높이를 600px로 설정

// 비행기 객체 설정
const plane = {
    x: canvas.width / 2 - 20, // 비행기의 초기 x 위치를 캔버스 중앙에 설정
    y: canvas.height - 60, // 비행기의 초기 y 위치를 캔버스 하단에 설정
    width: 40, // 비행기의 너비
    height: 40, // 비행기의 높이
    speed: 5, // 비행기의 이동 속도
    moveLeft: false, // 왼쪽으로 이동 중인지 여부
    moveRight: false, // 오른쪽으로 이동 중인지 여부
};

// 비행기 그리기 함수
function drawPlane() {
    ctx.fillStyle = 'white'; // 비행기의 색상을 흰색으로 설정
    ctx.fillRect(plane.x, plane.y, plane.width, plane.height); // 비행기를 직사각형으로 그립니다.
}

// 비행기 위치 업데이트 함수
function update() {
    if (plane.moveLeft && plane.x > 0) { // 왼쪽으로 이동 중이고 캔버스를 벗어나지 않았을 때
        plane.x -= plane.speed; // 왼쪽으로 이동
    }
    if (plane.moveRight && plane.x + plane.width < canvas.width) { // 오른쪽으로 이동 중이고 캔버스를 벗어나지 않았을 때
        plane.x += plane.speed; // 오른쪽으로 이동
    }
}

const enemies = []; // 적 배열을 선언하여 다수의 적을 관리

// 적 객체를 생성하는 함수
function createEnemy() {
    const enemy = {
        x: Math.random() * (canvas.width - 40), // 적의 X 위치를 랜덤으로 설정
        y: 0, // 적의 초기 Y 위치는 화면 위
        width: 40, // 적의 너비
        height: 40, // 적의 높이
        speed: 2, // 적이 내려오는 속도
    };
    enemies.push(enemy); // 적을 배열에 추가
}

// 적을 그리는 함수
function drawEnemies() {
    enemies.forEach((enemy) => {
        ctx.fillStyle = 'red'; // 적의 색상을 빨간색으로 설정
        ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height); // 적을 그립니다.
    });
}

// 적의 위치를 업데이트하는 함수
function updateEnemies() {
    enemies.forEach((enemy, index) => {
        enemy.y += enemy.speed; // 적이 아래로 이동

        // 적이 화면 아래로 나가면 배열에서 제거
        if (enemy.y > canvas.height) {
            enemies.splice(index, 1); // 적 제거
        }
    });
}

const bullets = []; // 비행기에서 발사할 총알을 담을 배열

// 총알을 발사하는 함수
function shootBullet() {
    const bullet = {
        x: plane.x + plane.width / 2 - 5, // 비행기 중앙에서 발사
        y: plane.y, // 비행기 위치에서 발사
        width: 5, // 총알 너비
        height: 10, // 총알 높이
        speed: 7, // 총알 속도
    };
    bullets.push(bullet); // 총알 배열에 추가
}

// 총알을 그리는 함수
function drawBullets() {
    bullets.forEach((bullet) => {
        ctx.fillStyle = 'yellow'; // 총알 색상 설정
        ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height); // 총알 그리기
    });
}

// 총알의 위치를 업데이트하는 함수
function updateBullets() {
    bullets.forEach((bullet, index) => {
        bullet.y -= bullet.speed; // 총알이 위로 올라감

        // 총알이 화면을 벗어나면 배열에서 제거
        if (bullet.y < 0) {
            bullets.splice(index, 1); // 총알 제거
        }
    });
}

// 일정 시간마다 적이 생성되도록 설정 (적이 계속 등장)
setInterval(createEnemy, 1000); // 1초마다 적을 생성


// 키보드 입력 처리: 키를 눌렀을 때
window.addEventListener('keydown', function(e) {
    if (e.code === 'ArrowLeft') { // 왼쪽 화살표 키가 눌렸을 때
        plane.moveLeft = true; // 왼쪽 이동을 시작
    }
    if (e.code === 'ArrowRight') { // 오른쪽 화살표 키가 눌렸을 때
        plane.moveRight = true; // 오른쪽 이동을 시작
    }
    if (e.code === 'Space') { // 스페이스바를 누르면 총알 발사
        shootBullet(); // 총알 발사 함수 호출
    }
});

// 키보드 입력 처리: 키를 뗐을 때
window.addEventListener('keyup', function(e) {
    if (e.code === 'ArrowLeft') { // 왼쪽 화살표 키를 뗐을 때
        plane.moveLeft = false; // 왼쪽 이동을 멈춤
    }
    if (e.code === 'ArrowRight') { // 오른쪽 화살표 키를 뗐을 때
        plane.moveRight = false; // 오른쪽 이동을 멈춤
    }
});

let isGameOver = false; // 게임 오버 상태를 추적할 변수

// 충돌 감지 함수 (총알과 적의 충돌, 적과 비행기의 충돌을 처리)
function checkCollision(obj1, obj2) {
    // 두 객체가 겹쳤는지 여부를 반환
    return (
        obj1.x < obj2.x + obj2.width && // obj1의 오른쪽 경계가 obj2의 왼쪽 경계를 넘지 않음
        obj1.x + obj1.width > obj2.x && // obj1의 왼쪽 경계가 obj2의 오른쪽 경계를 넘음
        obj1.y < obj2.y + obj2.height && // obj1의 아래쪽 경계가 obj2의 위쪽 경계를 넘지 않음
        obj1.y + obj1.height > obj2.y // obj1의 위쪽 경계가 obj2의 아래쪽 경계를 넘음
    );
}

// 적과 총알 및 비행기의 충돌을 확인하고 처리하는 함수
function handleCollisions() {
    bullets.forEach((bullet, bulletIndex) => {
        enemies.forEach((enemy, enemyIndex) => {
            // 총알과 적의 충돌 처리
            if (checkCollision(bullet, enemy)) {
                // 충돌 시 적과 총알을 배열에서 제거
                bullets.splice(bulletIndex, 1); // 총알 제거
                enemies.splice(enemyIndex, 1); // 적 제거
            }
        });
    });

    enemies.forEach((enemy) => {
        // 적과 비행기의 충돌 처리
        if (checkCollision(enemy, plane)) {
            isGameOver = true; // 게임 오버 설정
        }
    });
}

// 게임 오버를 처리하는 함수
function gameOver() {
    ctx.fillStyle = 'red'; // 게임 오버 메시지 색상 설정
    ctx.font = '48px Arial'; // 폰트 설정
    ctx.fillText('Game Over', canvas.width / 2 - 120, canvas.height / 2); // 화면에 메시지 표시
}

// 게임 루프 함수: 게임의 각 프레임을 업데이트하고 렌더링하는 함수
function gameLoop() {
    if (isGameOver) {
        gameOver(); // 게임 오버 시 메시지 표시
        return; // 더 이상 게임을 진행하지 않음
    }
    ctx.clearRect(0, 0, canvas.width, canvas.height); // 캔버스 전체를 지워서 이전 프레임의 그림을 지움
    drawPlane(); // 비행기 그리기
    update(); // 비행기 위치 업데이트

    drawEnemies(); // 적 그리기
    updateEnemies(); // 적 위치 업데이트

    drawBullets(); // 총알 그리기
    updateBullets(); // 총알 위치 업데이트

    handleCollisions(); // 충돌 감지 및 처리

    requestAnimationFrame(gameLoop); // 다음 프레임 호출
}

gameLoop(); // 게임 시작