화살 피하기 게임 구현

Unity로 화살 피하기 게임을 처음부터 끝까지 구현하는 종합 가이드입니다. 플레이어 이동, 화살 생성, 충돌 감지, HP 시스템 등을 다룹니다.

게임 개요

화살 피하기 게임은 위에서 떨어지는 화살을 피하면서 생존하는 게임입니다. 플레이어는 좌우로 이동하여 화살을 피하고, 화살에 맞으면 HP가 감소합니다. HP가 0이 되면 게임 오버가 됩니다.

게임 기능

  1. 플레이어 이동: 좌우 화살표 키로 플레이어 이동
  2. 화살 생성: 일정 시간마다 화면 상단에서 화살 생성
  3. 화살 낙하: 생성된 화살이 아래로 떨어짐
  4. 충돌 감지: 플레이어와 화살의 충돌 감지
  5. HP 시스템: 화살에 맞으면 HP 감소
  6. 게임 오버: HP가 0이 되면 게임 종료

프로젝트 구조

02.Scripts/
├── PlayerController.cs      # 플레이어 이동 제어
├── GameManager.cs           # 게임 상태 및 HP 관리
├── ArrowGenerator.cs         # 화살 생성
└── ArrowController.cs        # 화살 이동 및 충돌 감지

1. 플레이어 컨트롤러 (PlayerController.cs)

기능

  • 좌우 화살표 키 입력 받기
  • 플레이어를 좌우로 이동시키기

구현 코드

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    // 플레이어의 이동 속도
    public float moveSpeed = 0.2f;

    void Update()
    {
        // 왼쪽 화살표 키 입력
        if (Input.GetKey(KeyCode.LeftArrow))
        {
            // 왼쪽으로 이동
            transform.Translate(-moveSpeed, 0, 0);
        }
        
        // 오른쪽 화살표 키 입력
        if (Input.GetKey(KeyCode.RightArrow))
        {
            // 오른쪽으로 이동
            transform.Translate(moveSpeed, 0, 0);
        }
    }
}

주요 개념

  • Input.GetKey(): 키가 눌려있는 동안 계속 true 반환(return)
  • transform.Translate(): 현재 위치를 기준으로 상대적으로 이동
  • moveSpeed: 이동 속도 조절 (Inspector에서 조정 가능)

Unity 설정

  1. 플레이어 GameObject 생성
  2. PlayerController 스크립트 추가
  3. Sprite Renderer로 플레이어 이미지 설정
  4. moveSpeed 값 조정 (기본값: 0.2)

2. 게임 매니저 (GameManager.cs)

기능

  • HP 게이지 관리
  • HP 감소 메서드(method) 제공

구현 코드

using UnityEngine;
using UnityEngine.UI;

public class GameManager : MonoBehaviour
{
    // HP 게이지를 표시하는 UI Image 컴포넌트
    public Image hpGauge;
    
    // HP를 감소시키는 메서드
    public void DecreaseHp()
    {
        // HP 게이지의 채워진 양을 0.2만큼 감소
        hpGauge.fillAmount -= 0.2f;
    }
}

주요 개념

  • Image.fillAmount: 0.0(비어있음) ~ 1.0(가득참) 사이의 값
  • public 메서드(method): 다른 스크립트에서 호출 가능

Unity 설정

  1. GameManager GameObject 생성
  2. GameManager 스크립트 추가
  3. UI Canvas 생성
  4. Image 컴포넌트 추가 (HP 게이지용)
  5. Image의 Image Type을 "Filled"로 설정
  6. GameManager의 hpGauge에 Image 할당

3. 화살 생성기 (ArrowGenerator.cs)

기능

  • 일정 시간마다 화살 생성
  • 랜덤한 X 위치에 화살 생성

구현 코드

using UnityEngine;

public class ArrowGenerator : MonoBehaviour
{
    // 화살 프리팹 오브젝트
    public GameObject arrowPrefab;
    
    // 화살 생성 간격 (초 단위)
    float spawnTime = 0.3f;
    
    // 경과 시간을 누적하는 변수
    float delta = 0f;

    void Update()
    {
        // 이전 프레임과 현재 프레임 사이의 시간 간격을 누적
        delta += Time.deltaTime;
        
        // 누적된 시간이 생성 간격을 초과했는지 확인
        if (delta > spawnTime)
        {
            // 타이머를 초기화
            delta = 0f;
            
            // 화살 프리팹을 복제하여 새로운 화살 오브젝트를 생성
            GameObject ar = Instantiate(arrowPrefab);
            
            // 화살이 생성될 X 좌표를 랜덤하게 결정
            float px = Random.Range(-8.4f, 8.4f);
            
            // 생성된 화살의 위치를 설정
            ar.transform.position = new Vector3(px, 6f, 0);
        }
    }
}

주요 개념

  • Time.deltaTime: 프레임레이트와 무관하게 일정한 시간 간격 제공
  • Instantiate(): 프리팹을 복제하여 새 오브젝트 생성
  • Random.Range(): 랜덤한 값 생성

Unity 설정

  1. ArrowGenerator GameObject 생성
  2. ArrowGenerator 스크립트 추가
  3. 화살 프리팹 생성 (Arrow GameObject)
  4. ArrowGenerator의 arrowPrefab에 화살 프리팹 할당
  5. spawnTime 값 조정 (기본값: 0.3초)

4. 화살 컨트롤러 (ArrowController.cs)

기능

  • 화살을 아래로 이동시키기
  • 화면 밖으로 나가면 삭제
  • 플레이어와의 충돌 감지
  • 충돌 시 HP 감소 및 화살 삭제

구현 코드

using UnityEngine;

public class ArrowController : MonoBehaviour
{
    // 화살의 이동 속도
    public float arrowSpeed = 0.1f;

    // 플레이어 오브젝트의 참조
    GameObject player;

    void Start()
    {
        // 씬에서 "player"라는 이름의 게임 오브젝트를 찾아 참조를 저장
        player = GameObject.Find("player");
    }

    void Update()
    {
        // 화살을 아래 방향으로 이동
        transform.Translate(0, -arrowSpeed, 0);

        // 화살이 화면 아래로 너무 멀리 나갔는지 확인
        if (transform.position.y < -6f)
        {
            // 화살 오브젝트를 메모리에서 제거
            Destroy(gameObject);
        }

        // 충돌 감지를 위한 원거리 계산 (원-원 충돌 감지 알고리즘)
        
        // 화살의 현재 위치를 2D 벡터로 저장
        Vector2 p1 = transform.position;  // Arrow
        
        // 플레이어의 현재 위치를 2D 벡터로 저장
        Vector2 p2 = player.transform.position; // Player

        // 화살에서 플레이어로의 방향 벡터를 계산
        Vector2 direction = p1 - p2;
        
        // 두 오브젝트 사이의 거리를 계산 (벡터의 크기)
        float d = direction.magnitude;
        
        // 화살의 충돌 반경
        float r1 = 0.5f; // Arrow radius
        
        // 플레이어의 충돌 반경
        float r2 = 1.0f; // Player radius

        // 원-원 충돌 감지: 두 원의 중심 사이의 거리가 두 반지름의 합보다 작으면 충돌
        if (d < r1 + r2)
        {
            // 씬에서 "GameManager"라는 이름의 게임 오브젝트를 찾아 참조를 가져옴
            GameObject gm = GameObject.Find("GameManager");
            
            // GameManager 컴포넌트를 가져와서 DecreaseHp() 메서드를 호출
            gm.GetComponent<GameManager>().DecreaseHp();

            // 충돌이 감지되었으므로 화살 오브젝트를 삭제
            Destroy(gameObject);
        }   
    }
}

주요 개념

  • GameObject.Find(): 이름으로 GameObject 찾기
  • GetComponent(): 컴포넌트 가져오기
  • Vector2.magnitude: 벡터의 크기 (거리)
  • 원-원 충돌 감지: 두 원의 중심 거리 < 반지름 합

Unity 설정

  1. 화살 프리팹에 ArrowController 스크립트 추가
  2. arrowSpeed 값 조정 (기본값: 0.1)
  3. 플레이어 GameObject 이름을 "player"로 설정
  4. GameManager GameObject 이름을 "GameManager"로 설정

게임 구현 단계별 가이드

1단계: 씬 설정

  1. 새 씬 생성: File > New Scene
  2. 카메라 설정: Orthographic 카메라 사용 (2D 게임)
  3. 배경 설정: 배경 이미지 또는 색상 설정

2단계: 플레이어 설정

  1. 플레이어 GameObject 생성

    • GameObject > 2D Object > Sprite
    • 이름을 "player"로 변경
    • Sprite Renderer로 플레이어 이미지 설정
  2. PlayerController 스크립트 추가

    • PlayerController.cs 스크립트 생성
    • 플레이어 GameObject에 추가
    • moveSpeed 값 조정

3단계: 게임 매니저 설정

  1. GameManager GameObject 생성

    • 빈 GameObject 생성
    • 이름을 "GameManager"로 변경
    • GameManager.cs 스크립트 추가
  2. HP 게이지 UI 설정

    • Canvas 생성 (GameObject > UI > Canvas)
    • Image 생성 (GameObject > UI > Image)
    • Image Type을 "Filled"로 설정
    • Fill Method: Horizontal
    • Fill Origin: Left
    • GameManager의 hpGauge에 Image 할당

4단계: 화살 설정

  1. 화살 프리팹 생성

    • GameObject > 2D Object > Sprite
    • 이름을 "Arrow"로 변경
    • Sprite Renderer로 화살 이미지 설정
    • ArrowController.cs 스크립트 추가
    • Prefabs 폴더에 드래그하여 프리팹 생성
  2. ArrowGenerator 설정

    • 빈 GameObject 생성
    • 이름을 "ArrowGenerator"로 변경
    • ArrowGenerator.cs 스크립트 추가
    • arrowPrefab에 화살 프리팹 할당

5단계: 테스트 및 조정

  1. 게임 실행: Play 버튼 클릭
  2. 속도 조정:
    • 플레이어 이동 속도 (moveSpeed)
    • 화살 낙하 속도 (arrowSpeed)
    • 화살 생성 간격 (spawnTime)
  3. 충돌 범위 조정:
    • 화살 반경 (r1)
    • 플레이어 반경 (r2)

게임 메커니즘 상세 설명

1. 시간 기반 화살 생성

delta += Time.deltaTime;  // 시간 누적
if (delta > spawnTime)    // 일정 시간 경과 시
{
    delta = 0f;            // 타이머 리셋
    // 화살 생성
}
  • Time.deltaTime: 프레임레이트와 무관한 일정한 시간
  • delta 누적: 경과 시간 추적
  • spawnTime: 생성 간격 조절

2. 원-원 충돌 감지 알고리즘

Vector2 direction = p1 - p2;        // 방향 벡터
float d = direction.magnitude;       // 거리
if (d < r1 + r2)                    // 충돌 판정
{
    // 충돌 처리
}
  • 두 점 사이의 거리: 벡터의 크기(magnitude)로 계산
  • 충돌 판정: 거리 < 반지름 합
  • 반지름 조정: 오브젝트 크기에 맞게 설정

3. HP 시스템

hpGauge.fillAmount -= 0.2f;  // HP 20% 감소
  • fillAmount: 0.0 ~ 1.0 사이의 값
  • 0.2 감소: 한 번에 20%씩 감소
  • 5번 맞으면 게임 오버: fillAmount가 0이 되면 HP 소진

게임 개선 아이디어

1. 게임 오버 처리

public void DecreaseHp()
{
    hpGauge.fillAmount -= 0.2f;
    
    // HP가 0 이하가 되면 게임 오버
    if (hpGauge.fillAmount <= 0f)
    {
        GameOver();
    }
}

void GameOver()
{
    Debug.Log("Game Over!");
    Time.timeScale = 0f;  // 게임 일시정지
}

2. 점수 시스템

public int score = 0;
public Text scoreText;

void Update()
{
    // 시간이 지날수록 점수 증가
    score += (int)(Time.deltaTime * 10);
    scoreText.text = "Score: " + score;
}

3. 난이도 증가

float spawnTime = 0.3f;
float minSpawnTime = 0.1f;

void Update()
{
    // 시간이 지날수록 생성 간격 감소
    spawnTime = Mathf.Max(minSpawnTime, 0.3f - Time.time * 0.01f);
}

4. 사운드 효과

public AudioSource hitSound;
public AudioSource missSound;

void OnHit()
{
    hitSound.Play();
}

void OnMiss()
{
    missSound.Play();
}

트러블슈팅

문제 1: 화살이 생성되지 않음

원인: arrowPrefab이 할당되지 않음 해결: ArrowGenerator의 arrowPrefab에 화살 프리팹 할당

문제 2: 충돌이 감지되지 않음

원인:

  • 플레이어나 GameManager 이름이 잘못됨
  • 충돌 반경이 너무 작음

해결:

  • GameObject 이름 확인 ("player", "GameManager")
  • r1, r2 값 증가

문제 3: HP 게이지가 업데이트되지 않음

원인: hpGauge가 할당되지 않음 해결: GameManager의 hpGauge에 Image 컴포넌트 할당

문제 4: 화살이 너무 빠르거나 느림

원인: arrowSpeed 값이 적절하지 않음 해결: ArrowController의 arrowSpeed 값 조정


정리

핵심 스크립트 역할

스크립트 역할 주요 기능
PlayerController 플레이어 제어 좌우 이동
GameManager 게임 상태 관리 HP 관리
ArrowGenerator 화살 생성 시간차 생성
ArrowController 화살 제어 이동, 충돌 감지

주요 Unity 기능

  • Input.GetKey(): 키 입력 감지
  • transform.Translate(): 오브젝트 이동
  • Instantiate(): 오브젝트 생성
  • Destroy(): 오브젝트 삭제
  • Time.deltaTime: 프레임 독립 시간
  • Vector2.magnitude: 거리 계산
  • GameObject.Find(): 오브젝트 찾기
  • GetComponent(): 컴포넌트 가져오기

다음 단계

이 게임을 기반으로 다음과 같은 기능을 추가할 수 있습니다:

  1. 게임 오버 화면
  2. 재시작 기능
  3. 점수 시스템
  4. 난이도 증가
  5. 파워업 아이템
  6. 사운드 효과
  7. 파티클 효과

연습 문제

  1. 게임 오버 기능을 추가하여 HP가 0이 되면 게임이 종료되도록 하세요.

  2. 점수 시스템을 추가하여 생존 시간에 따라 점수가 증가하도록 하세요.

  3. 난이도 증가 시스템을 추가하여 시간이 지날수록 화살 생성 속도가 빨라지도록 하세요.

  4. 화살에 맞았을 때 화면이 깜빡이는 효과를 추가하세요.

  5. 플레이어가 화살을 피했을 때 점수를 추가하는 보너스 시스템을 만드세요.