- UI 제작
- Roatate
- 탄환 종류 추가
- 분열탄 Prefab 추가. 일정 거리 또는 시간 후 여러 개의 탄환으로 분열.
- 유도탄 Prefab 추가. 플레이어를 추적하는 탄환.
- 기존 Spawner에서 일반탄, 분열탄, 유도탄을 랜덤 생성.
- 아이템 시스템
- 아이템 생성기 추가. 일정 주기로 랜덤 위치에 아이템 스폰.
- 플레이어와 아이템 충돌 시 효과 발동.
- 아이템 종류 (최소 3종 이상 구현):
플레이어 이동 속도 증가
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}";
}
}
| 코드 | 한 줄 요약 |
|---|
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 : 간단한 설정값이나 기록을 저장하고 불러오는 기능
using UnityEngine;
public class Rotater : MonoBehaviour
{
public float rotateSpeed = 360f;
private void Update()
{
transform.Rotate(0f, rotateSpeed * Time.deltaTime, 0f);
}
}
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);
}
}
| 코드 | 한 줄 요약 |
|---|
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>() : 게임 오브젝트에서 지정한 컴포넌트를 가져옴
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);
}
}
}
| 코드 | 한 줄 요약 |
|---|
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() : 필요한 초기 설정을 수행하도록 직접 만든 메서드
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);
}
}
| 코드 | 한 줄 요약 |
|---|
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() : 특정 방향을 바라보도록 회전값을 계산
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);
}
}
}
| 코드 | 한 줄 요약 |
|---|
public class SpeedItem : MonoBehaviour | 속도 증가 아이템을 제어하는 클래스를 선언 |
private void OnTriggerEnter(Collider other) | 다른 오브젝트가 Trigger 영역에 들어왔을 때 호출 |
other.CompareTag("Player") | 충돌한 오브젝트의 Tag가 Player인지 확인 |
other.GetComponent<PlayerController>() | 플레이어의 PlayerController 컴포넌트를 가져옴 |
playerController.OnSpeedUp(); | 플레이어의 속도 증가 메서드를 호출 |
Destroy(gameObject); | 획득한 속도 아이템 오브젝트를 제거 |
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의 기능도 다시 돌아보게 되는 시간이 되었다.