사망 이후 상태 복구 및 자원 기반 즉시 부활 시스템 설계
1. 시스템 요구 사항
플레이어가 사망했을 때 두 가지 선택지가 제공되어야 한다.
하나는 마을에서 부활하는 기본 부활이며, 다른 하나는 소비 아이템(깃털)을 사용해 즉시 현재 위치에서 부활하는 방식이다.
부활이 이루어질 경우 플레이어의 체력과 마나는 최대치로 회복되어야 하며, 사망 상태 애니메이션은 초기화되어야 한다.
또한 부활 UI는 비활성화되어야 하고, 마을 부활의 경우에는 해당 맵으로 씬 이동이 이루어져야 한다.
즉시 부활은 소비 자원을 요구하며, 자원이 부족할 경우에는 부활이 이루어지지 않고 사용자에게 오류 메시지를 표시해야 한다.
실패 시에는 어떠한 데이터도 변경되지 않아야 한다.
2. 설계 목표
- 부활을 상태 복구 트랜잭션으로 설계
- 자원 기반 즉시 부활과 기본 부활 로직을 분리
- 사망 상태 해제 시 애니메이션과 스탯을 완전 초기화
- 실패 시 데이터 변경 차단
- 오류 피드백을 코루틴 기반으로 처리
3. 흐름도
사망 발생
↓
리스폰 UI 활성화
↓
(선택)
├─ 마을 부활 → 상태 복구 → 맵 이동 → UI 비활성화
└─ 즉시 부활 → 자원 확인
├─ 충분 → 자원 차감 → 상태 복구 → UI 비활성화
└─ 부족 → 오류 패널 표시
이 구조는 '검증 → 상태 변경 → 후처리' 흐름을 유지한다.
즉시 부활은 반드시 자원 확인을 선행한다.
4. 구현
4.1. 기본 부활 로직
public void ReSpawnOnVillage()
{
PlayerManager.instance.CurrentHp = PlayerManager.instance.MaxHp;
PlayerManager.instance.CurrentMp = PlayerManager.instance.MaxMp;
GameObject.FindGameObjectWithTag("Player").GetComponent<Animator>().Rebind();
GameObject.FindGameObjectWithTag("Player").GetComponent<Animator>().SetBool("Die", false);
GameManager.instance.LoadDestinationMap(GameManager.Map.Village);
UI_Manager.instance.respawnUI.SetActive(false);
}이 함수는 마을에서 부활하는 기본 부활 로직이다.
먼저 PlayerManager를 통해 현재 체력과 마나를 최대치로 복구한다.
이는 단순 값 변경이지만, 부활을 하나의 완전 상태 복구로 정의했기 때문에 HP와 MP를 부분 회복이 아닌 최대 회복으로 설정했다.
그 다음 Animator 컴포넌트에 접근해 Rebind를 호출한다.
Animator.Rebind는 애니메이션 상태를 초기 상태로 재설정하는 Unity API다.
사망 애니메이션이 재생된 상태에서 부활할 경우, 애니메이션 레이어나 파라미터 상태가 남아 있을 수 있으므로 Rebind로 애니메이션 상태를 리셋한다.
이후 SetBool("Die", false)를 호출해 사망 상태 파라미터를 명시적으로 해제한다.
이 과정은 단순히 체력만 회복하는 것이 아니라, 시각적 상태까지 정상 상태로 되돌리는 절차다.
GameObject.FindGameObjectWithTag("Player")를 사용한 이유는, 플레이어 오브젝트를 태그 기반으로 찾기 위함이다.
장점은 계층 구조가 바뀌어도 태그만 유지하면 참조가 가능하다는 점이다.
단점은 런타임 탐색 비용과, 동일 태그가 여러 개 존재하면 예기치 않은 결과가 발생할 수 있다는 점이다.
현재 구조에서는 플레이어가 하나만 존재하므로 안전하다.
이후 GameManager.instance.LoadDestinationMap을 호출해 마을 맵으로 이동한다.
씬 이동은 부활 이후 위치를 보장하는 가장 확실한 방법이다.
마지막으로 respawnUI를 비활성화해 부활 선택 UI를 닫는다.
UI 비활성화를 마지막에 배치한 이유는, 상태 복구와 맵 이동이 완료된 후 인터페이스를 정리하기 위함이다.
4.2. 자원 기반 즉시 부활
public void InstantResurrection()
{
if (InventoryManager.instance.feather.CurrentAmount >= 1)
{
InventoryManager.instance.feather.CurrentAmount -= 1;
PlayerManager.instance.CurrentHp = PlayerManager.instance.MaxHp;
PlayerManager.instance.CurrentMp = PlayerManager.instance.MaxMp;
GameObject.FindGameObjectWithTag("Player").GetComponent<Animator>().Rebind();
GameObject.FindGameObjectWithTag("Player").GetComponent<Animator>().SetBool("Die", false);
UI_Manager.instance.respawnUI.SetActive(false);
}
else
{
StartCoroutine(StartError(errorPanel));
}
}이 함수는 소비 아이템을 사용해 즉시 현재 위치에서 부활하는 로직이다.
가장 먼저 feather.CurrentAmount가 1 이상인지 검사한다.
이 검증은 상태 변경 이전에 반드시 수행되어야 한다.
자원이 부족한 상태에서 HP를 회복하거나 애니메이션을 리셋하면 데이터 일관성이 깨지기 때문이다.
조건을 통과하면 깃털을 1개 차감한다.
차감은 부활 성공 조건이 확인된 이후에 수행된다.
이 순서를 지키는 이유는 실패 시 자원이 감소하지 않도록 보장하기 위함이다.
이후 기본 부활과 동일하게 HP, MP를 최대치로 회복하고 애니메이션을 초기화한다.
여기서는 씬 이동을 수행하지 않는다.
즉시 부활은 현재 위치에서 다시 살아나는 설계이기 때문이다.
이는 플레이 리듬을 빠르게 유지하려는 의도다.
조건을 통과하지 못한 경우에는 StartCoroutine을 통해 오류 패널을 표시한다.
이 경우에는 어떠한 데이터도 변경되지 않는다.
이 구조는 실패 시 상태 전이를 완전히 차단한다는 설계 원칙을 유지한다.
4.3. 일시적 오류 표시
IEnumerator StartError(GameObject obj)
{
obj.SetActive(true);
yield return new WaitForSeconds(1.0f);
obj.SetActive(false);
}StartError는 오류 패널을 일정 시간 동안 표시한 뒤 자동으로 비활성화하는 코루틴이다.
Unity의 Coroutine은 별도의 스레드를 생성하지 않고, 메인 루프에서 yield 지점까지 실행한 뒤 다음 프레임들에서 이어서 실행하는 방식이다.
WaitForSeconds는 지정된 시간만큼 대기하도록 하는 Unity의 yield instruction이다.
Update 기반 타이머로 구현할 수도 있지만, 코루틴은 1초간 보여주고 자동 종료라는 흐름이 코드에 명확히 드러난다는 장점이 있다.
단점은 동일 오류가 연속으로 발생하면 코루틴이 중첩 실행될 수 있다는 점이다.
다만 현재 구조에서는 마지막 코루틴 종료 시점에 패널이 꺼지므로 기능적 문제는 발생하지 않는다.
필요하다면 StopCoroutine을 통해 중복 실행을 제어할 수 있다.
5. 개발 의도
이 리스폰 시스템은 단순 체력 회복 기능이 아니라, 사망 상태에서 정상 상태로 완전히 복구하는 상태 전환 시스템으로 설계했다.
체력과 마나 회복, 애니메이션 리셋, UI 정리, 맵 이동까지 하나의 트랜잭션으로 묶어 처리한다.
즉시 부활은 자원 소비 기반으로 설계해 전략적 선택 요소를 추가했다.
자원이 부족할 경우에는 어떠한 데이터도 변경하지 않도록 검증을 선행하고, 오류 메시지를 통해 사용자에게 명확한 피드백을 제공한다.
이 구조는 게임 플레이 흐름을 끊지 않으면서도, 사망이라는 이벤트를 명확한 상태 전이로 관리하는 설계다.
