작업한 내용들 병합하고 테스트 중인데 버그가 있어 수정했다.
문제 상황
실제 스테이지를 플레이하는 MainScene을 Reload하는 경우가 아래와 같이 3가지인데
1. 일시정지 버튼 클릭 -> Retry 클릭
2. 게임 패배 조건(스테이지의 하트 수 0) 만족하여 LosePopUp 활성화 -> Retry 클릭
3. 게임 승리 조건(모든 인간 처치) 만족하여 WinPopUp 창 활성화 -> Retry 클릭
1번은 제대로 Reload되는데 2, 3번에서 Reload하면 인게임 내 스폰된 몬스터와 인간 오브젝트가 그대로 남아있는 문제가 발생했다.
문제 원인
디버깅 결과 문제 원인은 이러했다.
1. 둘다 ReturnToPool할 때 코루틴 프로세스로 시간차를 두고 풀에 반환
2. 게임 종료 이벤트가 발생하며 Time.timeScale이 0이 되어 코루틴이 멈추고 풀에 바로 반환되지 못하고 활성화된 상태 유지
3. PoolManager(DontDestroyOnLoad)로 관리되는 풀링 객체라 Reload하여 Scene이 활성화되면 활성화된 풀링 객체도 활성화된 상태로 재시작
=> 재시작 시 풀링 오브젝트가 비활성화 된 상태로 시작하는 로직 필요
시도해 본 방법
고려해본 방법은 3가지이다.
1. 몬스터와 인간 오브젝트 OnEnable 할 때 SetActive(false)
=> 실행이 되지 않으므로 바로 기각!
애초에 풀링된 객체가 DontDestroyOnLoad로 관리되는데 OnEnable에서 비활성화 해봤자 Scene이 다시 로드될 때 OnEnable 함수가 호출이 되지 않으므로 전혀 소용이 없다.
2. PoolManager에 특정 풀 or 전체 풀 비활성화 로직 추가
=> 실행은 되나 리소스 많이 소모함
풀을 탐색하는 비용이 크다.
풀 자체를 비활성화했다가 켜면 같은 문제가 남아있다.
부모 오브젝트를 비활성화하면 자식 오브젝트도 비활성화 되는 것으로 단순하게 생각할 수 있지만,
부모 & 자식 오브젝트 활성 상태 -> 부모 오브젝트 비활성화 -> 부모 오브젝트 활성화
하면 당연히 자식 오브젝트도 같이 활성화된다. 그러니까 풀 자체를 껐다 키는 것은 해결 방법이 될 수 없다.
즉 자식 오브젝트(풀링된 객체)를 일일히 순회하며 활성화 된 오브젝트를 비활성 해줘야한다.
전체 풀 대상으로 이 작업을 하면 리소스가 너무 많이 든다 (오브젝트풀링의 의미가 없다...)
그렇다고 특정 풀 대상으로 하자니 너무 하드코딩될 가능성이 높다. 지금은 몬스터랑 인간 종류가 많지 않아 10줄 안쪽으로 작성되겠지만 확장성을 고려하면 좋은 방법은 아니다.
3. 몬스터와 인간 OnEnable에 게임 종료 이벤트 연결
=> 실행되고 상대적으로 리소스 소모 적음
그럼 풀에서 말고 개별 객체가 스스로 비활성화되게 하는 방식으로 접근하자!
현재 몬스터와 인간은 DontDestroyOnLoad이며 스테이지에서 최초로 스폰될 때와 사용후 풀에 반환되었다가 다시 활성화할 때 OnEnable 함수를 실행한다.
따라서 게임 종료 이벤트가 발생했을 때 풀에 반환하는 메서드를 연결해주면 종료 시점에 활성화 된 객체가 알아서 풀에 반환된다.
이벤트가 여기저기 흩어져 있으면 관리하기가 힘들긴한데... 그래도 이만큼 편하게 핸들링 하는 방법도 없어서 유용하게 사용하고 있다. 현재 전체 게임 로직에 Action 사용하는 부분이 적어서 아직까지는 괜찮을 것 같다.
Human.cs
private void OnEnable()
{
// 게임 종료 이벤트 발생하면 풀로 바로 반환
HumanManager.Instance.OnGameClear -= () => { PoolManager.Instance.ReturnToPool(gameObject.name, gameObject); };
HumanManager.Instance.OnGameClear += () => { PoolManager.Instance.ReturnToPool(gameObject.name, gameObject); };
StageManager.Instance.OnGameOver -= () => { PoolManager.Instance.ReturnToPool(gameObject.name, gameObject); };
StageManager.Instance.OnGameOver += () => { PoolManager.Instance.ReturnToPool(gameObject.name, gameObject); };
}
HumanManager.cs
public void SubHumanCount()
{
CountPerWave--;
if (CountPerWave <= 0 && isLastWave)
OnGameClear?.Invoke();
}
마지막 웨이브이고, 인간 인원수가 0보다 적으면 게임 클리어
StageManager.cs
// health 변경
public void ChangeHealth(int health)
{
CurrHealth += health;
stageInfoController.ChangeUI();
if (CurrHealth <= 0)
{
CurrHealth = 0;
OnGameOver?.Invoke();
}
}
스테이지 라이프 수가 0 이하이면 게임 오버
'내일배움캠프 > TIL' 카테고리의 다른 글
| [내배캠][Unity6기][TIL] CSV 데이터 파싱 방법(Feat. UGS) (0) | 2024.12.17 |
|---|---|
| [내배캠][Unity6기][TIL] Scene Reload 버그 트러블슈팅 (2) (0) | 2024.12.16 |
| [내배캠][Unity6기][TIL] Find 계열 함수 호출 빈도 줄이기 (0) | 2024.12.12 |
| [내배캠][Unity6기][TIL] NavMesh 트러블슈팅 (2) (0) | 2024.12.11 |
| [내배캠][Unity6기][TIL] NavMesh 트러블슈팅 (0) | 2024.12.09 |