Abstract
이 에디션에서 "리소스 로딩"은 결국 Addressables.LoadAssetAsync 한 번입니다. 알아 둘 것은 그 호출이 언제 일어나고 언제 일어나지 않는가, 그리고 로드한 것이 언제 해제되는가입니다. 이 둘이 "첫 스폰은 한 박자 늦는데 두 번째부터는 즉시 뜬다", "적을 다 잡았는데 메모리가 그대로다" 같은 체감 동작을 결정합니다.
실무에서 이 페이지가 필요한 순간
•
ShowEntity 직후에 엔티티를 찾았는데 없을 때
•
첫 적이 등장하는 순간 화면이 멈칫할 때
•
주소를 틀렸는데 에러 없이 아무 일도 일어나지 않을 때
ToyboxNightmare 에서는
모든 스폰 결과를 ShowEntitySuccess / ShowEntityFailure 이벤트 핸들러에서 처리합니다(SurvivalGame). ShowEntity 다음 줄에서 결과를 기대하는 코드는 한 곳도 없습니다. 이 페이지가 설명하는 "풀에 있으면 즉시, 없으면 비동기"라는 성질 때문입니다.
ShowEntity 한 번이 로딩으로 이어지는 과정
sequenceDiagram
participant G as 게임 코드
participant E as EntityManager (코어)
participant P as 엔티티 그룹의 인스턴스 풀
participant R as ResourceManager
participant A as Addressables
G->>E: ShowEntity(id, "Zombunny", "Enemy")
E->>P: 풀에 "Zombunny" 인스턴스가 있나?
alt 있다
P-->>E: 인스턴스
E-->>G: 같은 호출 안에서 OnShow 까지 완료 (로딩 없음)
else 없다
E->>R: LoadAsset("Zombunny")
R->>A: LoadAssetAsync("Zombunny")
Note over G,A: 호출은 여기서 바로 돌아온다. 엔티티는 아직 없다
A-->>R: 프리팹 (몇 프레임 뒤)
R-->>E: 성공 콜백
E->>E: Instantiate → 풀에 등록 → OnInit → OnShow
E-->>G: ShowEntitySuccess 이벤트
endMermaid
복사
여기서 나오는 규칙
풀에 있으면 즉시, 없으면 비동기
같은 ShowEntity 호출인데도 상황에 따라 엔티티가 "호출 직후에 이미 존재"할 수도 "몇 프레임 뒤에 생길" 수도 있습니다. 어느 쪽인지 호출하는 쪽은 알 수 없습니다. 그래서 결과는 항상 이벤트(또는 로직의 OnShow)에서 이어 갑니다.
// ❌ 풀이 비어 있으면 null 이다
entity.ShowEntity(id, typeof(Enemy), "Zombunny", "Enemy", data);
Entity spawned = entity.GetEntity(id);
// ✅ 성공 이벤트에서 이어 간다
private void OnShowEntitySuccess(object sender, GameEventArgs e)
{
ShowEntitySuccessEventArgs ne = e as ShowEntitySuccessEventArgs;
if (ne == null)
return;
// ne.Entity 가 방금 뜬 엔티티다
}
C#
복사
새 인스턴스 하나당 로드 한 번, 핸들 하나
풀이 비어서 Zombunny 를 다섯 번 새로 만들었다면 Addressables 핸들이 다섯 개 잡힙니다. 프리팹 자체는 메모리에 하나만 있고 참조 카운트만 5 입니다.
핸들은 인스턴스가 파괴될 때 풀린다
HideEntity 로는 풀리지 않습니다. 인스턴스가 풀에 남기 때문입니다.
사건 | 인스턴스(GameObject) | 프리팹 에셋 |
HideEntity | 비활성화되어 풀에 보관 | 그대로 |
풀의 Expire Time 경과 (또는 Capacity 초과) | Destroy | 핸들 하나 해제 |
그 주소의 마지막 인스턴스까지 파괴 | 참조가 0 → Addressables 가 번들을 내린다 |
"적을 다 잡았는데 메모리가 안 줄어든다"는 정상입니다. 예제의 엔티티 그룹은 만료 60초 + 검사 주기 60초라, 스폰이 멈추고 1~2분이 지나야 실제로 내려갑니다.
로드 중에 HideEntity 를 불러도 안전하다
코어가 "로드가 끝나면 바로 버릴 것" 목록에 올려 두었다가, 콜백이 오면 에셋을 반납하고 엔티티를 만들지 않습니다. OnInit / OnShow 도 불리지 않습니다.
다른 모듈의 로딩
호출 | 로드되는 것 | 재사용 | 해제 시점 |
ShowEntity | 프리팹 | 엔티티 그룹의 인스턴스 풀 | 인스턴스가 풀에서 만료되어 파괴될 때 |
OpenUIForm | 프리팹 | UI 인스턴스 풀 | 위와 같다 |
PlaySound | 오디오 클립 | 없음 — 재생할 때마다 로드 요청 (이미 메모리에 있으면 즉시 완료) | 재생이 끝나 에이전트가 초기화될 때 |
SceneComponent.LoadScene | 씬 (Additive) | 없음 | UnloadScene |
ReadData (DataTable / Config / Localization) | TextAsset | 없음 | 파싱이 끝난 직후 |
첫 스폰의 끊김 줄이기 — 미리 데우기
첫 적이 등장할 때 한 박자 멈칫하는 것은 프리팹 로드와 첫 Instantiate 가 그때 일어나기 때문입니다. 두 가지 방법이 있습니다.
방법 1 — 에셋만 미리 잡아 둔다. 로드 지연은 없어지고 Instantiate 비용은 남습니다. 사운드처럼 인스턴스화가 없는 에셋에 맞습니다.
// 로딩 Procedure 에서
resource.LoadAsset("PlayerHurt", new LoadAssetCallbacks(
(assetName, asset, duration, userData) => { mPreloaded.Add(asset); },
(assetName, status, errorMessage, userData) => { Log.Warning("프리로드 실패: {0}", assetName); }));
// 판이 끝날 때 — 로드한 만큼 돌려준다
for (int i = 0; i < mPreloaded.Count; i++)
{
resource.UnloadAsset(mPreloaded[i]);
}
mPreloaded.Clear();
C#
복사
방법 2 — 한 번 띄웠다 숨겨 풀에 넣어 둔다. 로드와 Instantiate 를 모두 미리 치릅니다. 엔티티에 맞습니다.
// 로딩 Procedure 에서: 화면 밖에 한 번 띄운다
entity.ShowEntity(id, typeof(Enemy), "Zombunny", "Enemy", EnemyData.Create(id, 1, stats, OffscreenPosition));
// ShowEntitySuccess 핸들러에서: 바로 숨긴다 → 인스턴스가 풀에 남는다
entity.HideEntity(ne.Entity);
C#
복사
방법 2 는 풀의 Expire Time(예제 60초) 안에 쓰이지 않으면 다시 파괴되므로 사용 직전에 해야 합니다.
실패했을 때
주소가 없거나 번들을 받지 못하면 예외가 아니라 실패 이벤트가 옵니다. 화면에는 아무 변화가 없습니다.
호출 | 실패 이벤트 |
ShowEntity | ShowEntityFailureEventArgs |
OpenUIForm | OpenUIFormFailureEventArgs |
PlaySound | PlaySoundFailureEventArgs |
ReadData | LoadDataTableFailureEventArgs 등 |
두 가지를 챙겨야 합니다.
•
실패 이벤트를 구독해 로그를 남긴다. 구독하지 않으면 주소 오타를 찾을 단서가 없습니다. 예제의 ProcedureMain 은 OpenUIFormFailure 를 구독해 "Addressables 주소와 UITable 문자열이 같은지 확인할 것"이라고 남깁니다.
•
넘겼던 userData 를 직접 반납한다. ShowEntity 에 넘긴 엔티티 데이터는 엔티티가 뜨지 않으면 주인 없는 채로 남습니다. 실패 핸들러에서 ReferencePool.Release 해야 하고, 예제는 GameBase.ReleaseSpawnData 가 이 일을 합니다.
자주 걸리는 곳
•
에디터에서 엔티티가 하나도 안 뜨면 먼저 Addressables Groups 창의 Play Mode Script 를 확인하세요. Use Existing Build 인데 번들을 빌드한 적이 없으면 모든 로드가 실패합니다. 개발 중에는 Use Asset Database (fastest) 로 둡니다.
•
주소는 대소문자까지 정확히 같아야 합니다. Zombunny 와 ZomBunny 는 다른 주소입니다.
•
진행률 이벤트(ShowEntityUpdate 등)는 이 에디션에서 발생하지 않습니다. 로딩 바가 필요하면 "요청한 개수 대비 성공 이벤트가 온 개수"로 직접 셉니다.




