Dodge 기능 확장

공재원
∙∙조회 5

오늘은 처음 제작하였던 Dodge게임의 기능 확장을 배웠다.

  • UI 제작
  • Roatate
  • 탄환 종류 추가
    • 분열탄 Prefab 추가. 일정 거리 또는 시간 후 여러 개의 탄환으로 분열.
    • 유도탄 Prefab 추가. 플레이어를 추적하는 탄환.
    • 기존 Spawner에서 일반탄, 분열탄, 유도탄을 랜덤 생성.
  • 아이템 시스템
    • 아이템 생성기 추가. 일정 주기로 랜덤 위치에 아이템 스폰.
    • 플레이어와 아이템 충돌 시 효과 발동.
    • 아이템 종류 (최소 3종 이상 구현):
      플레이어 이동 속도 증가

GameManager (GUI)

csharp
using UnityEngine;
using TMPro;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour
{
    private bool isGameOver; 
    
    public GameObject gameOverUI;
    public TextMeshProUGUI timeText; 
    public TextMeshProUGUI recordText;

    private float surviveTime; 

    private void Start()
    {
        surviveTime = 0f;
        isGameOver = false;
        gameOverUI.SetActive(false);
    }

    private void Update() 
    {
        if (!isGameOver)
        {
            surviveTime += Time.deltaTime; 
            timeText.text = $"TIME: {surviveTime:F1}"; 
        }
        else
        {
            if (Input.GetKeyDown(KeyCode.R))
            {
                SceneManager.LoadScene("Game");
            }
        }
    }

    public void EndGame() 
    {
        isGameOver = true; 
        gameOverUI.SetActive(true);

        float bestScore = PlayerPrefs.GetFloat("BestTime");
        if (surviveTime > bestScore)
        {
            bestScore = surviveTime;
            PlayerPrefs.SetFloat("BestTime", bestScore);
        }
        recordText.text = $"Best Record: {bestScore:F1}";
    }
}

GameManger 코드 요약

코드한 줄 요약
using TMPro;TextMeshPro UI 텍스트 기능을 사용하기 위한 선언
using UnityEngine.SceneManagement;씬을 불러오고 관리하는 기능을 사용하기 위한 선언
private bool isGameOver;게임 오버 상태인지 저장하는 논리형 변수
public GameObject gameOverUI;게임 오버 화면 UI 오브젝트를 저장하는 변수
public TextMeshProUGUI timeText;생존 시간을 표시할 UI 텍스트
public TextMeshProUGUI recordText;최고 기록을 표시할 UI 텍스트
private float surviveTime;플레이어의 생존 시간을 저장하는 변수
gameOverUI.SetActive(false);게임 오버 UI를 비활성화
if (!isGameOver)게임 오버 상태가 아닌지 확인
surviveTime += Time.deltaTime;게임이 진행되는 동안 생존 시간을 누적
timeText.text = $"TIME: {surviveTime:F1}";생존 시간을 소수점 첫째 자리까지 UI에 표시
Input.GetKeyDown(KeyCode.R)R 키를 누른 순간을 감지
SceneManager.LoadScene("Game");Game이라는 이름의 씬을 다시 불러옴
public void EndGame()게임 오버 처리를 실행하는 메서드
PlayerPrefs.GetFloat("BestTime");저장된 최고 기록을 불러옴
PlayerPrefs.SetFloat("BestTime", bestScore);최고 기록을 로컬에 저장
if (surviveTime > bestScore)현재 생존 시간이 최고 기록보다 긴지 확인
recordText.text = $"Best Record: {bestScore:F1}";최고 기록을 소수점 첫째 자리까지 UI에 표시

핵심 개념 정리

  • Time.deltaTime : 프레임 사이에 경과한 시간(초)
  • SetActive() : 게임 오브젝트를 활성화하거나 비활성화
  • SceneManager.LoadScene() : 지정한 씬을 불러옴
  • PlayerPrefs : 간단한 설정값이나 기록을 저장하고 불러오는 기능

Rotater

csharp
using UnityEngine;

public class Rotater : MonoBehaviour
{
    public float rotateSpeed = 360f;
    private void Update()
    {
        transform.Rotate(0f, rotateSpeed * Time.deltaTime, 0f);
    }
}


SplitBullet

csharp
using UnityEngine;

public class SplitBullet : MonoBehaviour
{
    public float speed = 4f; 
    private Rigidbody splitRdbody; 
    public GameObject otherPrefab;
    private float timer;
    private float interval = 1.5f;

    private void Start()
    {
        splitRdbody = GetComponent<Rigidbody>();
        splitRdbody.linearVelocity = transform.forward * speed; 
    }

    private void Update() 
    {
        timer += Time.deltaTime; 
        if (timer > interval) 
        {
            timer = 0f; 
            Spawn(); 
        }
    }
    private void OnTriggerEnter(Collider other) 
    {
        if (other.CompareTag("Player")) 
        {
            PlayerController playerController = other.GetComponent<PlayerController>(); 
            playerController.OnDie();
        }

        if(!other.CompareTag("Bullet")) 
        {
            Destroy(gameObject);
        }
    }

    private void Spawn() 
    {
        Vector3 direction = transform.forward;

        for (int i = 0; i < 3; i++)
        {
            switch (i)
            {
                case 1:
                    direction += transform.right;
                    break;
                case 2:
                    direction -= transform.right;
                    break;
            }
            direction.Normalize();
            Quaternion rotation = Quaternion.LookRotation(direction);
            Instantiate(otherPrefab, transform.position, rotation);
        }

        Destroy(gameObject);
    }
}

SplitBullet 코드 요약

코드한 줄 요약
public class SplitBullet : MonoBehaviour분열하는 총알을 제어하는 클래스를 선언
public float speed = 4f;총알의 이동 속도를 저장
private Rigidbody splitRdbody;총알의 Rigidbody 컴포넌트를 저장
public GameObject otherPrefab;분열 후 생성할 총알 프리팹을 저장
private float timer;총알 분열 시간을 측정하는 타이머
private float interval = 1.5f;총알의 분열 간격을 1.5초로 설정
splitRdbody.linearVelocity = transform.forward * speed;총알을 앞쪽 방향으로 이동시킴
timer += Time.deltaTime;프레임 사이의 경과 시간을 타이머에 누적
if (timer > interval)타이머가 설정된 분열 간격을 초과했는지 확인
Spawn();분열 총알을 생성하는 메서드를 호출
other.CompareTag("Player")충돌한 오브젝트가 플레이어인지 확인
other.GetComponent<PlayerController>()플레이어의 PlayerController 컴포넌트를 가져옴
playerController.OnDie();플레이어의 사망 처리 메서드를 호출
if (!other.CompareTag("Bullet"))충돌한 오브젝트가 Bullet 태그가 아닌지 확인
Destroy(gameObject);현재 분열 총알 오브젝트를 제거
Vector3 direction = transform.forward;총알이 바라보는 앞쪽 방향을 분열 방향으로 설정
for (int i = 0; i < 3; i++)총알을 3개 생성하기 위해 반복
switch (i)반복 횟수에 따라 분열 방향을 다르게 설정
direction += transform.right;분열 방향에 오른쪽 방향을 더함
direction -= transform.right;분열 방향에서 오른쪽 방향을 뺌
Quaternion.LookRotation(direction)지정한 방향을 바라보는 회전값을 계산
Instantiate(otherPrefab, transform.position, rotation);현재 위치에서 지정한 방향으로 분열 총알 프리팹을 생성

핵심 개념 정리

  • transform.forward : 오브젝트의 앞쪽 방향
  • transform.right : 오브젝트의 오른쪽 방향
  • Quaternion.LookRotation() : 특정 방향을 바라보도록 회전값을 계산
  • GetComponent<T>() : 게임 오브젝트에서 지정한 컴포넌트를 가져옴

HomingBullet

csharp
using UnityEngine;

public class BulletSpawner : MonoBehaviour
{
    public GameObject[] randomBullet;

    public PlayerController controller;

    private Transform target; 

    public float interval = 1f; 
    private float timer = 0; 

    private void Start() 
    {
        GameObject findGo = GameObject.FindWithTag("Player"); 
        target = findGo.transform; 
    } 

    private void Update() 
    {
        timer += Time.deltaTime; 
        if (timer > interval) 
        {
            timer = 0f;
            Spawn();
        }
    }
    private void Spawn()
    {
        int index = Random.Range(0, 3);
        Vector3 position = transform.position; 
        Vector3 direction = (target.position - transform.position);
        direction.Normalize(); 
        Quaternion rotation = Quaternion.LookRotation(direction); 
        HomingBullet spawnedHomingBullet = Instantiate(randomBullet[index], position, rotation).GetComponent<HomingBullet>();

        if (spawnedHomingBullet != null)
        {
            spawnedHomingBullet.Initialize(controller.transform);
        }

    }
}

HomingBullet 코드 요약

코드한 줄 요약
public GameObject[] randomBullet;여러 종류의 총알 프리팹을 배열에 저장
public PlayerController controller;플레이어를 제어하는 PlayerController 컴포넌트를 저장
private Transform target;총알이 향할 목표 오브젝트의 위치 정보를 저장
GameObject.FindWithTag("Player")Player 태그를 가진 게임 오브젝트를 찾음
timer += Time.deltaTime;프레임 사이의 경과 시간을 타이머에 누적
if (timer > interval)타이머가 설정한 발사 간격을 초과했는지 확인
Spawn();총알 생성 메서드를 호출
Random.Range(0, 3)0부터 2까지의 정수 중 하나를 무작위로 선택
Vector3 position = transform.position;총알이 생성될 위치를 현재 오브젝트의 위치로 설정
Vector3 direction = (target.position - transform.position);현재 위치에서 목표 위치를 향하는 방향을 계산
direction.Normalize();방향 벡터의 크기를 1로 정규화
Quaternion.LookRotation(direction)목표 방향을 바라보도록 회전값을 계산
Instantiate(randomBullet[index], position, rotation)배열에서 선택한 총알 프리팹을 지정한 위치와 방향으로 생성
GetComponent<HomingBullet>()생성된 총알에서 HomingBullet 컴포넌트를 가져옴
if (spawnedHomingBullet != null)생성된 총알에 HomingBullet 컴포넌트가 있는지 확인
spawnedHomingBullet.Initialize(controller.transform);유도 총알에 플레이어의 Transform을 전달해 목표를 설정

핵심 개념 정리

  • GameObject[] : 여러 게임 오브젝트를 저장하는 배열
  • Random.Range() : 지정한 범위에서 무작위 값을 선택
  • Instantiate() : 프리팹이나 오브젝트를 복제하여 생성
  • GetComponent<T>() : 지정한 컴포넌트를 가져옴
  • Initialize() : 필요한 초기 설정을 수행하도록 직접 만든 메서드

ItemSpawner

csharp
using UnityEngine;

public class ItemSpawner : MonoBehaviour
{
    private GameObject[] randomItem;
    public GameObject speedItem;
    public GameObject bulletClearItem;
    public GameObject bulletSpeedDownItem;
    private float interval = 5;
    private float timer;

    private void Start() 
    {
        randomItem = new GameObject[3] {speedItem, bulletClearItem, bulletSpeedDownItem};
    } 

    private void Update() 
    {
        timer += Time.deltaTime; 
        if (timer > interval) 
        {
            timer = 0f;
            Spawn();
        }
    }
    private void Spawn()
    {
        int index = Random.Range(0, 3);
        Vector3 position = Vector3.zero; 
        position.x = Random.Range(-7, 7);
        position.z = Random.Range(-7, 7);
        Quaternion rotation = Quaternion.LookRotation(position); 
        Instantiate(randomItem[0], position, rotation);
    }
}

ItemSpawner 코드 요약

코드한 줄 요약
private GameObject[] randomItem;무작위로 생성할 아이템 프리팹을 저장하는 배열
public GameObject speedItem;이동 속도 아이템 프리팹을 저장
public GameObject bulletClearItem;총알 제거 아이템 프리팹을 저장
public GameObject bulletSpeedDownItem;총알 속도 감소 아이템 프리팹을 저장
private float interval = 5;아이템 생성 간격을 5초로 설정
randomItem = new GameObject[3] {speedItem, bulletClearItem, bulletSpeedDownItem};세 종류의 아이템을 배열에 저장
timer += Time.deltaTime;경과 시간을 타이머에 누적
if (timer > interval)타이머가 생성 간격을 초과했는지 확인
Spawn();아이템 생성 메서드를 호출
Random.Range(0, 3)0부터 2까지의 정수 중 하나를 무작위로 선택
Vector3 position = Vector3.zero;아이템 생성 위치를 (0, 0, 0)으로 초기화
position.x = Random.Range(-7, 7);생성 위치의 X 좌표를 무작위로 설정
position.z = Random.Range(-7, 7);생성 위치의 Z 좌표를 무작위로 설정
Quaternion.LookRotation(position);지정한 방향을 바라보도록 회전값을 계산
Instantiate(randomItem[0], position, rotation);배열의 첫 번째 아이템을 지정한 위치와 회전값으로 생성

핵심 개념 정리

  • GameObject[] : 여러 게임 오브젝트를 저장하는 배열
  • Vector3.zero : (0, 0, 0)을 나타내는 벡터
  • Random.Range() : 지정한 범위에서 무작위 값을 선택
  • Instantiate() : 프리팹을 복제하여 생성
  • Quaternion.LookRotation() : 특정 방향을 바라보도록 회전값을 계산

SpeedItem

csharp
using UnityEngine;

public class SpeedItem : MonoBehaviour
{ 
    private float timer;
    private float interval;
    
    private void OnTriggerEnter(Collider other) 
    {
        if (other.CompareTag("Player")) 
        {
            PlayerController playerController = other.GetComponent<PlayerController>(); 
            playerController.OnSpeedUp(); 
            Destroy(gameObject);
        }
    }
}

SpeedItem 코드 요약

코드한 줄 요약
public class SpeedItem : MonoBehaviour속도 증가 아이템을 제어하는 클래스를 선언
private void OnTriggerEnter(Collider other)다른 오브젝트가 Trigger 영역에 들어왔을 때 호출
other.CompareTag("Player")충돌한 오브젝트의 Tag가 Player인지 확인
other.GetComponent<PlayerController>()플레이어의 PlayerController 컴포넌트를 가져옴
playerController.OnSpeedUp();플레이어의 속도 증가 메서드를 호출
Destroy(gameObject);획득한 속도 아이템 오브젝트를 제거

PlayerController (플레이어에게 적용되는 SpeedItem)

csharp
    private void Update() 
    {
        Vector3 direction = Vector3.zero; 
        direction.x = Input.GetAxis("Horizontal"); 
        direction.z = Input.GetAxis("Vertical"); 

        if (direction.magnitude > 1f) 
        {
            direction.Normalize(); 
        }

        if (isSpeedUp)
        {
            speedUpTimer += Time.deltaTime; 
            if (speedUpTimer > speedInterval) 
            {
                speedUpTimer = 0f;
                isSpeedUp = false;
            }
            rgdbody.linearVelocity = direction * speedUp; 
        }
        else
        {
            rgdbody.linearVelocity = direction * speed; 
        }
    }

    public void OnSpeedUp()
    {
        isSpeedUp = true;
        speedUpTimer = 0;
    }

오늘은 Dodge 게임 기능 확장을 해보았다.
이번 과제는 두 명이 네비게이터, 드라이버로 나뉘어 진행하였다.
이번 과제에서 나는 드라이버로 작업하였다.
맵도 돌려보고, 새로운 탄환도 만들어 보고, 아이템도 생성해보았다.
혼자 진행했더라면 하나 구현하는 것 조차 쉽지 않았을 텐데 네비게이터 분께서 코드 설명과 흐름, 상황 등을 잘 설명해주셨다.
말해주시는 대로 코드를 짜보면서 궁금했던 점도 물어보고 Unity의 기능도 다시 돌아보게 되는 시간이 되었다.