์„œ๋น„์Šค
๊ต์œก ์ฒ ํ•™

Resource Management System

Abstract

๐Ÿ“Œ
๋ฆฌ์†Œ์Šค ์‹œ์Šคํ…œ์€ "์ด๋ฆ„์„ ์ฃผ๋ฉด ์—์…‹์„ ๋น„๋™๊ธฐ๋กœ ๊ฐ€์ ธ๋‹ค ์ฃผ๋Š”" ์ฐฝ๊ตฌ์ž…๋‹ˆ๋‹ค. ์—”ํ‹ฐํ‹ฐยทUIยท์‚ฌ์šด๋“œยท์”ฌ ๋ชจ๋“ˆ์ด ์ „๋ถ€ ์ด ์ฐฝ๊ตฌ๋ฅผ ํ†ตํ•ด ์—์…‹์„ ์–ป์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ฒŒ์ž„ ์ฝ”๋“œ๊ฐ€ ์ง์ ‘ ๋ถ€๋ฅผ ์ผ์€ ๊ฑฐ์˜ ์—†์Šต๋‹ˆ๋‹ค โ€” ShowEntity("Girl", โ€ฆ) ์„ ๋ถ€๋ฅด๋ฉด ๊ทธ ์•ˆ์—์„œ ๋ฆฌ์†Œ์Šค ์‹œ์Šคํ…œ์ด ๋•๋‹ˆ๋‹ค. ์ด ์—๋””์…˜์—์„œ ๊ทธ ์ฐฝ๊ตฌ ๋’ค์— ์žˆ๋Š” ๊ฒƒ์€ Unity ์˜ Addressables ์ž…๋‹ˆ๋‹ค.
์‹ค๋ฌด์—์„œ ํ•„์š”ํ•œ ์ˆœ๊ฐ„
โ€ข
์ƒˆ ํ”„๋ฆฌํŒนยท์‚ฌ์šด๋“œยทUI ๋ฅผ ๊ฒŒ์ž„์— ์ถ”๊ฐ€ํ•  ๋•Œ โ€” ๋“ฑ๋ก ๋ฐฉ๋ฒ•๊ณผ ์ฃผ์†Œ ๊ทœ์น™์„ ์•Œ์•„์•ผ ํ•œ๋‹ค
โ€ข
์—”ํ‹ฐํ‹ฐ๋‚˜ UI ๊ฐ€ ์•„๋‹Œ ์—์…‹(ScriptableObject, ํ…์Šค์ฒ˜, ํ…์ŠคํŠธ)์„ ์ฝ”๋“œ๋กœ ๋ถˆ๋Ÿฌ์™€์•ผ ํ•  ๋•Œ
โ€ข
"์•„๋ฌด๊ฒƒ๋„ ์•ˆ ๋œฌ๋‹ค"๋ฅผ ๋””๋ฒ„๊น…ํ•  ๋•Œ
ToyboxNightmare ์—์„œ๋Š”
ResourceComponent ๋ฅผ ์ง์ ‘ ๋ถ€๋ฅด๋Š” ์ฝ”๋“œ๊ฐ€ ํ•œ ์ค„๋„ ์—†์Šต๋‹ˆ๋‹ค. ์—์…‹ 25๊ฐœ๊ฐ€ ์ „๋ถ€ ShowEntity / OpenUIForm / PlaySound ๋ฅผ ํ†ตํ•ด ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค.
ํ™•์žฅํ•˜๋ ค๋ฉด
โ€ข
์›๊ฒฉ ๋ฐฐํฌยทํŒจ์น˜ โ†’ Resource Updating(๋ฆฌ์†Œ์Šค ์—…๋ฐ์ดํŠธ)-Addressable
โ€ข
๋‚ด๋ถ€ ๊ตฌ์กฐ, ๋‹ค๋ฅธ ๋กœ๋”๋กœ ๊ต์ฒด โ†’ Resource Management(๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ)-Addressable

๋กœ๋”ฉ ๊ฒฝ๋กœ

graph LR
    A["ShowEntity / OpenUIForm / PlaySound"] --> B["์ฝ”์–ด ๋งค๋‹ˆ์ €<br>(EntityManager ๋“ฑ)"]
    B --> C["IResourceManager"]
    C --> D["ResourceManager"]
    D --> E["Addressables.LoadAssetAsync"]
Mermaid
๋ณต์‚ฌ
๊ฒŒ์ž„ ์ฝ”๋“œ๋Š” ๋งจ ์™ผ์ชฝ๋งŒ ์”๋‹ˆ๋‹ค. ๋‚˜๋จธ์ง€๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์—ฐ๊ฒฐํ•ด ๋‘” ๋ฐฐ๊ด€์ž…๋‹ˆ๋‹ค.

๊ธฐ์–ตํ•  ๊ทœ์น™์€ ํ•˜๋‚˜ โ€” ์—์…‹ ์ด๋ฆ„ = Addressables ์ฃผ์†Œ

ํ˜ธ์ถœ
๋„˜๊ธฐ๋Š” ๋ฌธ์ž์—ด
Addressables ์— ๋“ฑ๋ก๋œ ๊ฒƒ
ShowEntity(id, typeof(Enemy), "Zombunny", "Enemy", data)
"Zombunny"
Zombunny.prefab ์˜ ์ฃผ์†Œ Zombunny
OpenUIForm("HUDForm", "Default")
"HUDForm"
HUDForm.prefab ์˜ ์ฃผ์†Œ HUDForm
PlaySound("PlayerHurt", "Soundeffects", position)
"PlayerHurt"
Player Hurt.wav ์˜ ์ฃผ์†Œ PlayerHurt
SceneComponent.LoadScene("Assets/Scenes/Stage01.unity")
์ „์ฒด ๊ฒฝ๋กœ
์”ฌ์˜ ์ฃผ์†Œ (์”ฌ๋งŒ์€ Assets/ ๋กœ ์‹œ์ž‘ํ•˜๊ณ  .unity ๋กœ ๋๋‚˜์•ผ ํ•œ๋‹ค)
์ฃผ์†Œ๊ฐ€ ํ•œ ๊ธ€์ž๋ผ๋„ ๋‹ค๋ฅด๋ฉด(๋Œ€์†Œ๋ฌธ์ž ํฌํ•จ) ๋กœ๋“œ๊ฐ€ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ์‹คํŒจ๋Š” ์˜ˆ์™ธ๊ฐ€ ์•„๋‹ˆ๋ผ ์‹คํŒจ ์ด๋ฒคํŠธ๋กœ๋งŒ ์˜ค๊ณ , ํ™”๋ฉด์—๋Š” ์•„๋ฌด ์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
๐Ÿ’ก
์ฃผ์†Œ ๋ฌธ์ž์—ด์„ ์ฝ”๋“œ ๊ณณ๊ณณ์— ์ง์ ‘ ์ ์ง€ ๋ง๊ณ  ์ƒ์ˆ˜ ํด๋ž˜์Šค์— ๋ชจ์œผ์„ธ์š”. ์˜ˆ์ œ๋Š” UITable, SoundTable, EnemyTable ์— ๋ชจ์•„ ๋‘์—ˆ์Šต๋‹ˆ๋‹ค.

์ƒˆ ์—์…‹์„ ๊ฒŒ์ž„์— ์ถ”๊ฐ€ํ•˜๋Š” ์ ˆ์ฐจ

1.
Window โ†’ Asset Management โ†’ Addressables โ†’ Groups ๋ฅผ ์—ฐ๋‹ค.
2.
์—์…‹์„ ๊ทธ๋ฃน์œผ๋กœ ๋Œ์–ด๋‹ค ๋†“๋Š”๋‹ค.
3.
์ฃผ์†Œ๋ฅผ ์งง์€ ํ‚ค๋กœ ๋ฐ”๊พผ๋‹ค. (์—ฌ๋Ÿฌ ๊ฐœ ์„ ํƒ โ†’ ์šฐํด๋ฆญ โ†’ Simplify Addressable Names)
4.
์ƒ์ˆ˜ ํด๋ž˜์Šค์— ๊ทธ ๋ฌธ์ž์—ด์„ ์ถ”๊ฐ€ํ•˜๊ณ , ์ฝ”๋“œ์—์„œ ShowEntity / OpenUIForm / PlaySound ์— ๋„˜๊ธด๋‹ค.
์—”ํ‹ฐํ‹ฐ ํ”„๋ฆฌํŒน์ด๋ผ๋ฉด ๋กœ์ง ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ถ™์ด์ง€ ์•Š์€ ์ƒํƒœ๋กœ, UI ํผ ํ”„๋ฆฌํŒน์ด๋ผ๋ฉด ๋ฃจํŠธ์— UIFormLogic ์„ ๋ถ™์ธ ์ƒํƒœ๋กœ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

์ง์ ‘ ์—์…‹์„ ๋กœ๋“œํ•ด์•ผ ํ•  ๋•Œ

์—”ํ‹ฐํ‹ฐยทUIยท์‚ฌ์šด๋“œ๊ฐ€ ์•„๋‹Œ ์—์…‹์€ ResourceComponent ๋ฅผ ์ง์ ‘ ์”๋‹ˆ๋‹ค.
ResourceComponent resource = GameEntry.GetComponent<ResourceComponent>(); resource.LoadAsset("EnemyStats", new LoadAssetCallbacks( (assetName, asset, duration, userData) => { mStats = (EnemyStatsAsset)asset; }, (assetName, status, errorMessage, userData) => { Log.Error("๋กœ๋“œ ์‹คํŒจ: {0}", errorMessage); })); // ๋‹ค ์ผ์œผ๋ฉด ๋ฐ˜๋“œ์‹œ ๋Œ๋ ค์ค€๋‹ค. ๋กœ๋“œ 1ํšŒ๋‹น ์ •ํ™•ํžˆ 1ํšŒ resource.UnloadAsset(mStats);
C#
๋ณต์‚ฌ
๊ทœ์น™
์ด์œ 
๊ฒฐ๊ณผ๋Š” ์ฝœ๋ฐฑ์—์„œ ๋ฐ›๋Š”๋‹ค
๋กœ๋“œ๋Š” ๋น„๋™๊ธฐ๋‹ค. ํ˜ธ์ถœ ๋‹ค์Œ ์ค„์—์„œ๋Š” ์•„์ง ์—†๋‹ค
LoadAsset ํ•œ ๋ฒˆ์— UnloadAsset ํ•œ ๋ฒˆ
์ง์ด ์•ˆ ๋งž์œผ๋ฉด ์—์…‹์ด ๋ฉ”๋ชจ๋ฆฌ์—์„œ ๋‚ด๋ ค๊ฐ€์ง€ ์•Š๋Š”๋‹ค
์‹คํŒจ ์ฝœ๋ฐฑ์„ ๋น„์›Œ ๋‘์ง€ ์•Š๋Š”๋‹ค
์ฃผ์†Œ ์˜คํƒ€๋ฅผ ์ฐพ์„ ์œ ์ผํ•œ ๋‹จ์„œ๋‹ค

ResourceComponent ์˜ API

๋ฉ”์„œ๋“œ
์šฉ๋„
LoadAsset(์ฃผ์†Œ, callbacks[, userData])
์—์…‹ ํ•˜๋‚˜๋ฅผ ๋น„๋™๊ธฐ๋กœ ๋กœ๋“œ
UnloadAsset(asset)
๋กœ๋“œํ•œ ์—์…‹์„ ๋Œ๋ ค์ค€๋‹ค
LoadScene / UnloadScene
์”ฌ ๋กœ๋“œยท์–ธ๋กœ๋“œ. ๋ณดํ†ต SceneComponent ๋ฅผ ํ†ตํ•ด ์“ด๋‹ค
UnloadUnusedAssets(performGCCollect)
์•„๋ฌด๋„ ์ฐธ์กฐํ•˜์ง€ ์•Š๋Š” ์—์…‹ ์ •๋ฆฌ๋ฅผ ์˜ˆ์•ฝํ•œ๋‹ค. ์Šคํ…Œ์ด์ง€ ์ „ํ™˜ ์งํ›„์— ๋ถ€๋ฅธ๋‹ค
ForceUnloadUnusedAssets(performGCCollect)
๊ฐ„๊ฒฉ ์ œํ•œ ์—†์ด ๋‹ค์Œ ํ”„๋ ˆ์ž„์— ์ •๋ฆฌํ•œ๋‹ค
LoadAsset ์—๋Š” ํƒ€์ž…(assetType)๊ณผ ์šฐ์„ ์ˆœ์œ„(priority)๋ฅผ ๋ฐ›๋Š” ์˜ค๋ฒ„๋กœ๋“œ๋„ ์žˆ์ง€๋งŒ, ์ด ์—๋””์…˜์—์„œ๋Š” ๋‘˜ ๋‹ค ๋ฌด์‹œ๋˜๊ณ  ์ฃผ์†Œ๋งŒ์œผ๋กœ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

์›์ž‘ GameFramework ๋ฌธ์„œ๋ฅผ ํ•จ๊ป˜ ๋ณผ ๋•Œ

์›์ž‘์˜ ๋ฌธ์„œ๋‚˜ ์˜ˆ์ œ(StarForce ๋“ฑ)์—๋Š” ์ด ์—๋””์…˜์— ์—†๋Š” ๊ธฐ๋Šฅ์ด ๋‚˜์˜ต๋‹ˆ๋‹ค. ํ—ท๊ฐˆ๋ฆฌ์ง€ ์•Š๋„๋ก ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
์›์ž‘์— ๋‚˜์˜ค๋Š” ๊ฒƒ
์ด ์—๋””์…˜
์—์…‹ ์ด๋ฆ„์— ์ „์ฒด ๊ฒฝ๋กœ ("Assets/GameMain/Entities/Player.prefab")
Addressables ์ฃผ์†Œ ("Player")
๋ฆฌ์†Œ์Šค ๋ชจ๋“œ, ๋ฒ„์ „ ํ™•์ธ, ๋ฆฌ์†Œ์Šค ์—…๋ฐ์ดํŠธ Procedure
์—†์Œ. ํŒจ์น˜๋Š” Addressables API ๋กœ ์ง์ ‘ ํ•œ๋‹ค
Resource Editor / Builder / Analyzer ์ฐฝ
์—†์Œ. Addressables Groups ์ฐฝ์„ ์“ด๋‹ค
LoadBinary
๋ฏธ์ง€์›. TextAsset ์œผ๋กœ ๋“ฑ๋กํ•ด LoadAsset ์œผ๋กœ ์ฝ๋Š”๋‹ค
HasAsset ์œผ๋กœ ์กด์žฌ ํ™•์ธ
ํ•ญ์ƒ "์žˆ์Œ"์„ ๋Œ๋ ค์ฃผ๋ฏ€๋กœ ํ™•์ธ ์šฉ๋„๋กœ ์“ธ ์ˆ˜ ์—†๋‹ค
๋กœ๋”ฉ ์ง„ํ–‰๋ฅ ยท์˜์กด ์—์…‹ ์ด๋ฒคํŠธ
๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. ์„ฑ๊ณตยท์‹คํŒจ ์ด๋ฒคํŠธ๋งŒ ์˜จ๋‹ค

์‹คํ–‰ ์ „์— ํ™•์ธํ•  ๊ฒƒ

โš ๏ธ
Addressables Groups ์ฐฝ์˜ Play Mode Script ๋ฅผ Use Asset Database (fastest) ๋กœ ๋‘์„ธ์š”. ์ด ๊ฐ’์€ Library/ ์— ์ €์žฅ๋˜์–ด git ์œผ๋กœ ๊ณต์œ ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ํ”„๋กœ์ ํŠธ๋ฅผ ๋ฐ›์€ ์‚ฌ๋žŒ๋งˆ๋‹ค ํ•œ ๋ฒˆ์”ฉ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Use Existing Build ์ธ ์ฑ„๋กœ ๋ฒˆ๋“ค์„ ๋นŒ๋“œํ•œ ์ ์ด ์—†์œผ๋ฉด ๋ชจ๋“  ๋กœ๋“œ๊ฐ€ ์‹คํŒจํ•ด ์—”ํ‹ฐํ‹ฐ๊ฐ€ ํ•˜๋‚˜๋„ ๋œจ์ง€ ์•Š์Šต๋‹ˆ๋‹ค(01. ํ”„๋กœ์ ํŠธ ์„ธํŒ… โ€” ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ผœ์ง€๋Š” ์ตœ์†Œ ์กฐ๊ฑด).