UIManager와 팀원들의 코드를 병합하면서 문제가 발생했습니다.

게임 오버가 되거나, 설정창을 누른 후 저 집모양의 아이콘을 클릭하면 TitleScene으로 되돌아가게끔 만들었습니다.
하지만 되돌아간 후에 버튼이 클릭이 안되는 문제가 발생했습니다.
Scene자체는 TitleScene으로 넘어왔기 때문에 문제가 없지만 DontDestroyOnLoad항목에 AudioManager와 GameManager가 존재한 상태였습니다.
때문에 처음에는 이전 Scene의 매니저들과 TitleScene의 UI가 겹쳐 있는 상태라고 생각을 했기 때문에 EventSystem이 2개인건 아닌지, Inspector설정이 잘못 되어 있는 것은 아닌지 알아 보았습니다.
Inspector는 모두 정상이었기 때문에 내가 모르는 GameManager(팀원의 코드)에서 Scene을 Load하는데 문제가 생긴건 아닌가 해서 Scene을 로드할 때 잔류 객체를 정리할 수 있도록 코드를 추가해 주었습니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (scene.name == "TitleScene")
{
Time.timeScale = 1f;
if (Instance != null)
{
Destroy(Instance.gameObject);
Instance = null;
}
var audioManager = FindObjectOfType<AudioManager>();
if (audioManager != null)
{
Destroy(audioManager.gameObject);
}
foreach (var es in FindObjectsOfType<UnityEngine.EventSystems.EventSystem>())
Destroy(es.gameObject);
StartCoroutine(RecreateEventSystemAfterFrame());
return;
}
StartCoroutine(ReconnectManagerReferences());
}
private IEnumerator RecreateEventSystemAfterFrame()
{
yield return null;
new GameObject("EventSystem",
typeof(UnityEngine.EventSystems.EventSystem),
typeof(UnityEngine.EventSystems.StandaloneInputModule));
}
|
cs |
기존의 싱글톤 객체, EventSystem을 제거하고, 새 객체를 생성해 준 뒤 Canvas 등록을 대기해 주었습니다.
하지만 그럼에도 증상은 변하지 않았습니다.
때문에 Scene을 Load하는게 문제가 아니라, Scene으로 보내는 UIManager쪽의 문제를 보게 되었습니다.

GameManager안의 UIManager도 DontDestryoyOnLoad의 영향을 받아서, TitleScene에 있는 UIManager와 중첩, 겹쳐진 문제였습니다.
Unity는 상단의 Canvas가 클릭 이벤트를 가로채므로 TitleScene 버튼의 이벤트가 작동하지 않고 있었으며, EventSystem또한 이전의 것을 참조중인 상태로, 보이는 것만 TitleScene이지만 클릭 이벤트는 이전 씬의 UIManager에 묶여 있는 상태였습니다.
이 과정은 한 번에 알아낸게 아니라,
EventSystem이 잘 적용 되어 있는지에 관한 디버그,
클릭이 되는지에 대한 디버그,
Scene이 로드 되었는지에 대한 디버그,
타이틀로 돌아가지는지에 대한 디버그 등을 통해 막히는 부분을 찾아내서 알아냈습니다.
그 결과 EventSystem의 로그 출력 시 TitleScene의 EventSystem은 존재하지만, 로그로서는 TitleScene의 것은 존재하지 않는다가 나왔습니다.
결국 DontDestroyOnLoad된 UIManager와 EventSystem이 TitleScene 진입 후에도 남아서 UI입력을 차단하고 있었습니다.
때문에 UIManager의 GoTitle()을 수정해 주었습니다.
|
1
2
3
4
5
6
|
public void GoTitle()
{
Time.timeScale = 1f;
SceneManager.LoadScene("TitleScene");
}
|
cs |
이렇게 간단했던 코드를
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public void GoTitle()
{
Time.timeScale = 1f;
var eventSystems = FindObjectsOfType<UnityEngine.EventSystems.EventSystem>();
foreach (var es in eventSystems)
{
Destroy(es.gameObject);
}
Destroy(gameObject);
SceneManager.LoadScene("TitleScene");
}
|
cs |
이렇게 수정해 주었습니다.
넘어간 후에 제거하는 것이 아닌, 제거한 후에 넘어갑니다.
처음에 GameManager에서도 비슷한 방식으로 중첩이 될 만한 것을 제거했지만, 넘어간 다음에 제거하니 TitleScene의 것을 제거한 것 같습니다.
이번 일을 통해 동작 순서가 정말 중요하다는 것을 알았습니다.
코드 자체만으로는, 혼자 테스트 할 때는 멀쩡했던 것이 팀원과 리팩토링을 하는 과정에서 이러한 문제가 생기기 때문에, 앞으로도 모두 각 객체안에서 문제를 해결할 수 있게 해야 할 것 같습니다.
아래는 이 내용에 관한 걸 ChatGPT가 만들어준 트러블 슈팅입니다.
트러블 슈팅을 잘 쓰는 것 또한 실력을 올리는데 큰 기여를 한다고 생각하는데, AI가 너무 잘 써서 좀 .. 그렇습니다.
앞으로 아래와 같이 쓸 수 있게 좀 더 신경을 써야겠습니다.
Unity 트러블슈팅 : DontDestroyOnLoad 잔류로 인한 TitleScene 버튼 클릭 불가 문제
문제 요약
게임 중 옵션 → "타이틀로 돌아가기" 버튼 클릭 시, 씬은 정상적으로 전환되지만 UI 버튼(Select / Options / Exit 등)이 전혀 클릭되지 않는 현상 발생.
- Hierarchy 상에서
DontDestroyOnLoad아래에 여러 매니저 오브젝트가 남음 - 콘솔에는
EventSystem 없음!혹은Destroying assets is not permitted경고 다수 발생
발생 흐름
| 단계 | 동작 | 관련 코드 / 오브젝트 |
|---|---|---|
| 1 | TitleScene에서 Start 클릭 | UIManager → GameManager.StartGame() |
| 2 | 난이도 선택 | UIManager → GameManager.SetDifficulty() |
| 3 | GameScene 로드 |
SceneManager.LoadScene() |
| 4 | 플레이 중 옵션창 열기 | UIManager.ToggleSettings() |
| 5 | "타이틀로 돌아가기" 클릭 | UIManager.GoTitle() |
| 6 | 씬 로드 완료 | SceneManager.sceneLoaded |
| 7 | 버튼 클릭 불가 | EventSystem 존재하지만 입력 불가 |
근본 원인 분석
DontDestroyOnLoad의 구조
Unity는 DontDestroyOnLoad()가 호출된 오브젝트를 내부의 숨겨진 Scene인 DontDestroyOnLoadScene 으로 이동시킨다.
DontDestroyOnLoad(gameObject);
이 영역은 Unity가 자동으로 정리하지 않는다. 따라서 씬을 전환해도 이전 매니저들이 남아있어 이전 Scene의 입력 루프와 Update()를 계속 점유한다.
잘못된 시도들
1. EventSystem 재생성
foreach (var es in FindObjectsOfType<EventSystem>()) Destroy(es.gameObject);
new GameObject("EventSystem", typeof(EventSystem), typeof(StandaloneInputModule));
→ "재생성 완료" 로그 출력되지만 여전히 클릭 불가. EventSystem은 원인이 아니었다.
2. Resources.FindObjectsOfTypeAll 사용
foreach (var obj in Resources.FindObjectsOfTypeAll<GameObject>())
Destroy(obj);
→ 콘솔 경고 다수 출력:
Destroying assets is not permitted to avoid data loss.
원인: 이 API는 프로젝트의 Prefab / Asset까지 포함하므로, Unity가 에셋 삭제를 차단했다.
3. 단순 Scene Reload
SceneManager.LoadScene("TitleScene");
→ 씬만 다시 로드되고, DontDestroyOnLoad 객체는 그대로 남음 → 여전히 클릭 불가.
최종 해결 코드
public void GoTitle()
{
Time.timeScale = 1f;
AudioManager.Instance.Play(SoundKey.SFX_UI_UICLICK);
// ✅ DontDestroyOnLoad Scene에 존재하는 루트 오브젝트만 제거
var allObjects = GameObject.FindObjectsOfType<GameObject>(true);
foreach (var go in allObjects)
{
if (go.scene.name == null && go.transform.parent == null)
{
UnityEngine.Object.Destroy(go);
}
}
// ✅ TitleScene 완전 재로드
UnityEngine.SceneManagement.SceneManager.LoadScene("TitleScene");
}
---
내부 작동 원리
scene.name == null: DontDestroyOnLoad Scene에 속한 객체 탐색parent == null: 루트 오브젝트만 남김 (Canvas 하위는 제외)Destroy(): 런타임 인스턴스만 제거, Asset에는 영향 없음SceneManager.LoadScene(): EventSystem, Canvas 재초기화EventSystem.current: 자동으로 새 TitleScene으로 갱신- 결과 : 입력 루프 복원, 버튼 클릭 정상화
교훈
- 🔹 DontDestroyOnLoad는 관리되지 않는 전역 객체이므로 수동 정리 필요.
- 🔹 Resources.FindObjectsOfTypeAll은 Asset까지 포함되므로 Destroy에 부적절.
- 🔹 UI 클릭 불가는 대부분 이전 Scene 잔류 객체 때문.
- 🔹 안전한 삭제 조건은
scene.name == null && parent == null. - 🔹 Scene 전환은 “초기화”보다 “교체”가 안정적.
🧾 결론
이 문제는 단순한 UI 이벤트 오류가 아니라, Unity의 DontDestroyOnLoadScene 내부 객체가 EventSystem 입력 루프를 점유한 구조적 문제였다.FindObjectsOfType<GameObject>(true)와scene.name == null && parent == null조건을 적용해 런타임 루트 객체만 안전하게 제거함으로써 버튼 클릭 불가 현상이 완전히 해결되었다.
실행 결과 비교
| 항목 | 수정 전 | 수정 후 |
|---|---|---|
| 하이라키 구조 | DontDestroyOnLoad 남음 | 완전 정리됨 |
| EventSystem 개수 | 2개 (중복) | 1개 |
| 버튼 클릭 | ❌ 불가 | ✅ 가능 |
| 콘솔 경고 | Destroying assets 경고 다수 | 없음 |
| FPS / GC Alloc | 변화 없음 | 정상 |
AI진짜 개잘쓰네
'공부 > 게임 공부' 카테고리의 다른 글
| 쿠키런(?)은 전혀 안닮은 Run게임 (0) | 2025.11.14 |
|---|---|
| Run게임이 Map을 만드는 방식 (0) | 2025.11.03 |
| AudioManager와 EffectManager (0) | 2025.11.03 |
| 쿠키런을 만들어 보자 with Unity (0) | 2025.10.30 |
| Unity로 가상 공간을 만들어 보자. (0) | 2025.10.28 |