ZombieSurvival IDamagable, Player추적, HpBar, Spawner 정리
20261026 ZombieSurvival IDamagable, Player추적, HpBar, Spawner
Animator에서 Trigger로 상태변경시 주의사항
Animator에서 Animation변경을 Trigger로 발동시키는 경우 Trigger가 호출 되었을 때 바로 호출되고 사라지는게 아니라 호출될 수 있는 상황이 될때까지 대기했다가 호출된다. 예를들어 A -> B, A -> C 이렇게 변경이 가능하고 A -> C로 변경 할 때만 Trigger가 필요한 경우 상태가 B일 때 Trigger를 실행시키면 바로 실행되고 없어지는게 아니라 B에서 A로 돌아오기 까지 기다렸다가 A가 되면 Trigger가 발동해서 C로 변경된다.
IDamageable을 통한 피격관리
IDamageable인터페이스를 작성하여 IDamageable을 통해 피격이 가능한 Object들에 붙여 관리하는 component를 생성. IDamageable을 받는 LivingEntity 클래스를 생성. 피격이 가능한 Player와 Zombie에 상속하여 피격에 대한 관리를 해준다.
OnDead Event 등록
Event 등록에 대한 자세한 설명은 이번에는 넘어가고 다음에 deligate를 배울 때 다시 다룰 예정.
using UnityEngine.Events;로 using을 선언하고 public UnityEvent OnDead;로 Event를 만들어준 후 OnDead.Invoke();로 특정 상황에 OnDead를 호출해준다. 여기서는 Die()에서 호출.
// Die 할 때 OnDead Event 호출
public virtual void Die()
{
isDead = true;
OnDead.Invoke();
}
Zombie AI 설정
Zombie의 AI는 일정 반경 안에 Player가 들어오면 Player 방향으로 이동한다.(추격) 일정 반경을 벗어나면 기본 상태로 복귀한다.(기본 혹은 패트롤) 공격범위 안에 들어오면 플레이어를 공격하도록 한다.(공격) 공격상태에서 공격범위를 벗어나면 다시 추격상태로 돌아간다. 위 상황을 switch문을 통한 상태패턴으로 작성 예정이다. 또한 추격중 경로가 막힐 경우를 위한 길찾기도 가능해야한다. 유니티에서 기본 제공하는 길찾기 시스템이 있다(Nav Mesh Agent). 이번에는 이걸 사용할 예정이다.
NavMesh Surface 생성
길찾기를 적용하기 위하여 우선 NavMesh Surface를 생성해야한다.
- Hierachy -> AI -> NavMesh Surface로 생성
- Inspector의 설정에서 Use Geometry -> physics colliders로 설정
- Bake 버튼으로 NavMesh Data를 생성한다.
- bake할 때 Player나 Zombie같은 Bake후 못가게될 위치에서 제외해야 할 것들은 Bake 할때 비활성화 후 Bake 해야 한다.
Bake에 영향을 주는 Component가 있다. NavMesh Modifier: 장애물로 사용할 Object 등에 해당 Component를 붙인 후 설정을 통해 NavMesh Surface에 영향을 줄 수 있다.
Zombie 생성시 주의사항
Zombie에 NavMesh를 통하여 Player에게 향하는 길찾기를 생성해야 하므로 Zombie 생성시 생성 위치가 NavMesh 범위 안에 위치하도록 생성해주어야 함.
private NavMeshAgent agent; // 길찾기를 하기 위해 변수로 가지고 있는다.
agent.isStopped = true; // 상태에 따라서 이동을 멈춰야 할 경우 false로 할당한다.
agent.ResetPath(); // 현재 Zombie 사망시 삭제(Destroy)가 아니라 비활성화를 한 후 필요한 위치에서 다시 활성화 예정이기 떄문에 ResetPath를 해주어야 한다.
agent.enabled;,agent.ResetPath(); 이 2개는 세트라고 봐야한다. (활성화, 비활성화시 초기화)
// Random한 위치에 생성할 때 NavMesh 위에 생성해 주기위해 다음 함수를 사용한다.
// 기준 position, NavMesh의 한 점, 확인할 범위, 공간설정(여러 옵션가능)
if(NavMesh.SamplePosition(transform.position, out NavMeshHit hit, 10f, NavMesh.AllAreas))
{
agent.Warp(hit.position);// 원래는 실패에 대한 예외처리 해줘야 함.
}
생성된 position을 받아 NavMesh의 설정한 범위에 존재할 수 있는지 확인하고 존재할 수 있다면 NavMeshHit으로 해당 position을 반환한다.
PlayerHpBar
UI를 출력하기 위한 Canvas를 Rendering하는 방법은 2가지가 있다.
- Main Camera에 Rendering하는 방법
- 전용 Camera에 Rendering하는 방법
캐릭터의 머리위에 현재 HP나 데미지가 떠오르는건 MainCamera(World) 방식이라고 보면 된다.
HP Bar UI 추가하는 방법
Canvas 생성 -> Canvas(GameObject) -> Canvas(Component) -> RenderMode를 WorldSpace로 변경 -> Canvas를 Player의 자식으로 변경한다. 해당 Canvas의 자식으로 Hp의 배경이 될 Background Image와 실제 Hp를 표기할 이미지를 겹쳐놓는다. 실제 Hp를 표기할 Image의 Inspector에서 Image Type을 Filled로 설정한다. 원하는 형태로 감소하는 모습을 보이도록 Fill Method를 설정한다. Radial : 회전하는 방식으로 Hp가 늘어나고 줄어든다. Vertical, Horizontal: 각각 세로, 가로로 HpBar가 늘어나고 줄어든다. Fill Amount에 현재 Hp를 비율로 넘겨주면 해당 비율에 따라 표시된다.
ZombieSpawner
Zombie에 대한 생성, 삭제를 관리하는 Spawner를 생성하여 Zombie Prefab을 관리한다.
Instantiate<Zombie>(zombiePrefab);를 통하여 Zombie GameObject를 생성한다.
AddListener는 Event에 메소드를 등록하는 함수이다.
ObjectPool
Zombie를 생성하고 사망할때마다 삭제할 경우 가비지 컬렉터를 계속 사용하며 성능에 좋지 않다. 한번 생성하고 사망시 삭제하는 것이 아니라 비활성화 상태로 변환 후 별도의 공간에 담아놓고 다음번 생성시 꺼내서 활성화 상태로 만드는 것으로 생성 및 삭제를 최소화 할 수 있다.
// ObjectPool을 활용한 좀비생성 코드 예시
private readonly List<Zombie> zombies = new List<Zombie>();
private readonly List<Zombie> zombiePool = new List<Zombie>();
private void CreateZombie()
{
Transform point = spawnPoints[Random.Range(0, spawnPoints.Length)];
ZombieData data = zombieDatas[Random.Range(0, zombieDatas.Length)];
Zombie zombie;
if(zombiePool.Count == 0)
{
zombie = Instantiate(zombiePrefab, point.position, point.rotation);
zombie.gameObject.SetActive(false);
zombiePool.Add(zombie);
zombie.OnDead.AddListener(() => zombies.Remove(zombie));
zombie.OnDead.AddListener(() => uiManager.SetWaveText(wave, zombies.Count));
zombie.OnDead.AddListener(() => StartCoroutine(CoReturnToPoolZombie(zombie)));
zombie.OnDead.AddListener(() => gameManager.AddScore(score));
}
zombie = zombiePool[0];
zombie.Setup(data);
zombie.transform.position = point.position;
zombie.transform.rotation = point.rotation;
zombie.gameObject.SetActive(true);
zombies.Add(zombie);
zombiePool.Remove(zombie);
}
private IEnumerator CoReturnToPoolZombie(Zombie zombie)
{
yield return new WaitForSeconds(5f);
zombie.gameObject.SetActive(false);
zombiePool.Add(zombie);
}
실제 소환해서 움직이게 되는 Zombie들은 zombies에 담아 관리하고 사망할 경우 비활성화 하여 zombiePool에 담는다. 이후 생성이 필요할 때 zombiePool에 이미 생성되어 있는 객체가 있다면 해당 객체를 가져오고 담겨있는 객체가 없을때에만 추가로 생성해준다.



