게임 전체 상태를 관리하는 싱글톤 GameManager를 만들고,
적을 n명 잡으면 승리하는 조건을 구현한 과정을 기록한다.
1. 싱글톤(Singleton) 패턴이란?
"게임에 딱 하나만 존재하는 오브젝트"를 만드는 패턴.
이번 작업에서는 GameManager를 싱글톤으로 만들어 보려고 한다.
비유하자면
학교에 교장선생님은 한 명.
누구든 "교장선생님"이라고 하면 같은 사람을 가리킴.
GameManager도 마찬가지.
어떤 스크립트에서든 GameManager.Instance 라고 하면 같은 객체를 가리킴.
코드 구조
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject); // 이미 있으면 새로 만든 건 파괴
return;
}
Instance = this; // 자기 자신을 Instance에 등록
}
}
static: 클래스 자체에 속하는 변수. 인스턴스 없이도 접근 가능Instance: 자기 자신의 참조. 어디서든GameManager.Instance로 접근Awake()에서 중복 체크: 이미 존재하면 새로 만든 건 파괴 → 항상 하나만 유지
사용 예시
// Enemy.cs에서 (적이 죽었을 때)
GameManager.Instance.OnEnemyKilled();
// PlayerHealth.cs에서 (플레이어가 죽었을 때)
GameManager.Instance.SetGameOver();
어느 스크립트에서든 GameManager.Instance로 바로 접근. 참조를 따로 연결할 필요 없음!
2. 게임 상태 관리
public enum GameState
{
Playing, // 게임 진행 중
Victory, // 승리
GameOver // 패배
}
상태 흐름
Playing (게임 중)
→ 적을 n명 잡음 → Victory (승리!)
→ 플레이어 HP 0 → GameOver (패배!)
상태 체크로 중복 방지
public void OnEnemyKilled()
{
if (State != GameState.Playing) return; // 이미 끝났으면 무시
// ...
}
게임이 끝난 후에도 적이 죽을 수 있으니까, Playing 상태일 때만 카운트한다.
3. 승리 조건: 적 n명 잡기
[SerializeField]
private int killsToWin = 1; // Inspector에서 변경 가능
private int _killCount;
public void OnEnemyKilled()
{
if (State != GameState.Playing) return;
_killCount++;
UpdateUI();
if (_killCount >= killsToWin)
Victory();
}
killsToWin을 Inspector에서 바꿀 수 있어서 난이도 조절이 쉽다.
4. 승리 연출
private void Victory()
{
State = GameState.Victory;
if (victoryPanel != null)
victoryPanel.SetActive(true);
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
}
처음에는 Time.timeScale = 0으로 게임을 멈췄는데, 개인적으로 배경이 계속 동작하는 게 더 자연스러워서 제거했다.
5. 전체 구조: 누가 누구에게 알려주나
Enemy 죽음
→ DieAndDisappear()
→ GameManager.Instance.OnEnemyKilled()
→ killCount 증가 → 승리 조건 체크
Player 죽음
→ PlayerHealth.GameOver()
→ GameManager.Instance.SetGameOver()
→ 게임 상태를 GameOver로 변경
핵심: Enemy와 Player는 GameManager에 "알려만" 준다.
승리/패배 판정은 GameManager가 중앙에서 한다.
GameManager (중앙 관리자)
┌─────────────────┐
│ State: Playing │
│ killCount: 0 │
│ killsToWin: 1 │
└────────┬────────┘
│
┌──────────┼──────────┐
↓ ↓ ↓
Enemy Player UI
OnEnemyKilled() SetGameOver() UpdateUI()
6. Scene 재시작 (RestartGame)
public void RestartGame()
{
Time.timeScale = 1f;
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
현재 씬을 다시 로드하면 모든 오브젝트가 초기 상태로 돌아간다.
나중에 UI 버튼에 연결하면 "재시작" 기능이 된다.
7. Collider와 애니메이션의 불일치
적이 점프(공격) 애니메이션 중에도 총을 쏘면 맞는 현상을 발견했다.
눈에 보이는 것: 오리가 위에 있음 🐤 (애니메이션이 보여주는 위치)
실제 Collider 위치: 바닥에 그대로 📦 (Transform 기준)
애니메이션은 보이는 모습만 바꾸고, Collider는 Transform 위치에 있어서 발생하는 현상.
근본적으로 고치려면 Root Motion이 필요하지만 현재 작업에서는 이대로 두었다.
8. 이번 STEP에서 배운 핵심 정리
1. 싱글톤 = 딱 하나만 존재 + 어디서든 Instance로 접근
2. Awake()에서 중복 체크 → 이미 있으면 Destroy
3. enum으로 게임 상태 관리 (Playing, Victory, GameOver)
4. 상태 체크로 중복 이벤트 방지 (if State != Playing return)
5. 중앙 관리자 패턴: 각자 알려만 주고, 판정은 Manager가
6. Time.timeScale = 0 vs 스크립트 비활성화 = 상황에 따라 선택
7. SceneManager.LoadScene()으로 씬 재시작 가능
'Unity 궁둥이 쪼물쪼물' 카테고리의 다른 글
| 010. 플레이어 체력 & 게임오버 (0) | 2026.04.13 |
|---|---|
| 009. 적 체력(HP) 시스템 (0) | 2026.04.13 |
| 008. 넉백 코드 리팩토링 (0) | 2026.04.13 |
| 007. 넉백(Knockback) 구현 (0) | 2026.04.06 |
| 006. 총알에 오브젝트 풀링 적용하기 (0) | 2026.04.06 |