Garbage Collector π³

νλͺ
: CodeSurfers
νμ: κΉνμ°, μ μμ±
κ°λ° νκ²½: Unity 2019.4 URP, Visual Studio, GitLab
μ μ κΈ°κ°: 2020.08.18 ~ 2020.09.15
YouTube: Garbage Collector Devlog
Index
νλ‘μ νΈ μκ°
λͺ©ν
Garbage Collectorλ ν λ¬ λμ μ§νν νλ‘μ νΈμ΄κ³ νκ² μ½ν μΈ λ₯Ό μ νκ³ μ΅λν λκ°μ΄ λ§λλκ±Έ λͺ©νλ‘ νμλ€.

νκ²μ λ³΄νΈ λ μ΄μ± κ²μμΈ βλ ν¬λ£¨ 2βμ΄λ€. μ΄ κ²μμμ μ΅λν λ°λΌ νλ €κ³ ν λͺ κ°μ§ ν¬μΈνΈκ° μλ€.
- μμν λ°λ€
- λ¬Ό μ΄ννΈ
- ν΅ν΅ νλ 보νΈ
- μ€νΌλν λλ
μΆκ°λ‘ λ°λ€μ λ λ€λλ λΆμ μ°λ κΈ°λ₯Ό μ²μν΄μ ν΄μ λλ¬Όλ€μ λμλ μμ΄λμ΄λ λ£μλ€.
μ€ν 리

ν볡νκ² ν€μμΉλ κ³ λ μλ¦¬λ€ λ°λ€κ° μ°λ κΈ°λ€λ‘ μ€μΌλμλ€.
μ¬νΌνλ μ리λ₯Ό μν΄ λ³΄νΈλ₯Ό μ΄μ νμ¬ μ ν΄μ§ μκ° λ΄μ μ΅λν λ§μ λΆμ μ°λ κΈ°λ₯Ό κ±΄μ Έλ΄μ!
ꡬ쑰
Garbage Collectorμ μ¬νν μμΌμ΄λ κ²μμ ꡬ쑰λ₯Ό κ°μ§λ€. νμ μμμ΄ λλ©΄ κΈ°λ‘μ΄ λ¨κ³ λ€μ νλ μ΄νλ μμΌλ‘ 리νλ μ΄μ±μ΄ μ§λ€.
: Main Flow
μν λΆλ΄ λ° κ°λ° μΌμ
: ν ꡬμ±μ
κΉνμ° | μ μμ± |
---|---|
|
|
κ°λ° μΌμ
νλ‘μ νΈ κ΅¬ν
1. Water System

λ¬Όμ νννκΈ° μν΄ λ€μν μλλ₯Ό ν΄λ³΄μλ€. ν΄λ¦¬κ³€ μκ° λ§μ νλ μΈμ μμ±ν΄ Vertexμ λμ΄λ₯Ό μ‘°μ νκ±°λ Shader Graph λ§μ μ΄μ©ν΄ μ¨μ΄λΈλ₯Ό μμ±νλ€. νμ§λ§ λ¬Όμ νννλκ²μ μ΄λ ΅κ³ μ λ§ λ§μ μμλ€μ΄ νμ νλ€λ κ²μ μκ² λμλ€.
- Wave : κ±°μ€λ μ¨μ΄λΈ
- Reflection : λ°μ¬
- Refraction : κ΅΄μ
- Caustic : λ°λ₯μ 컀μ€ν± ν¨κ³Ό
- Frasnel : μΉ΄λ©λΌ λ·° κ°μ λ°λΌ λ°μ¬ or κ΅΄μ
- Scattering : μ°λ (κ°κΉμ΄ λ°λ€)
- Absorption : ν‘μ (λ¨Ό λ°λ€) μ΄λμ 보μ
a. Mesh Base

Meshλ₯Ό λ°νμ μ€ μμ±ν΄μ Octave Waveλ₯Ό μ μ©νλ λ°©μμ΄λ€. Octave Waveλ₯Ό μ μ©ν λλ Mathf.PerlinNoise()
μ λ°ν κ°μ λ²ν
μ€μ λμ΄μ μ μ©νκ² λλλ° λ§€ νλ μ μμΉ κ°μ λ°κΏμ£Όκ³ Meshμ λ
Έλ©μ λ€μ κ³μ°ν΄ μ€μΌ ν΄μ μ°μ°λμ΄ λ§λ€. Meshκ° ν΄λ¦¬κ³€μ μκ° μ μΌλ©΄ λ¬Έμ κ° λμ§ μμ§λ§, ν΄λ¦¬κ³€μ μλ₯Ό μ‘°κΈλ§ λλ €λ νλ μ λλμ΄ λ°μν΄ λ°λ€λ₯Ό νννκΈ°μλ μ μ νμ§ μμλ€.
b. Shader Graph Base
![]() | ![]() |
![]() | ![]() |
Shader Graphλ₯Ό μ΄μ©ν΄ λ¬Όμ νννλ λ°©μμ λλΆλΆ λΉμ·νλ€. μλ‘ λ€λ₯Έ λ Έλ©λ§΅ νΉμ λ Έμ΄μ¦λ§΅μ μ»΄λ°μΈ μμΌ μκ°μ νλ¦μ λ°λΌ offsetμ μ£Όλ λ°©μμΌλ‘ λ¬Όκ²°μ λ§λ€μλ€. λ°μ¬ν¨κ³Όμ μ°λ ν¨κ³Όλ ννν μ μμμΌλ 무μΈκ° λ§μ΄ λΆμ‘±ν λλμ΄ μμλ€.
c. Unity Water System

κ²°κ΅ λ§μ§λ§μλ Unity BoatAttack μ€νμμ€μ Water Systemμ μ μ©νμλ€. μμ λ§ν λ¬Όμ νννκΈ° μν μμλ€μ λ€ λ€μ΄κ°μλ μλ¦λ€μ΄ λ¬Όμ΄μλ€. 물리μ μΌλ‘ μΈν°λ μ μ΄ λΆκ°λ₯νμ§λ§ Buoyancy Systemλ λ΄μ₯λμ΄ μμλ€. 무μ보λ€λ Unity Jobsystemκ³Ό Burst Compilerλ₯Ό μ΄μ©ν΄ λͺ¨λ°μΌμμλ λμ ν리ν°μ λ¬Ό ννκ³Ό μΈν°λ μ μ΄ κ°λ₯νλ€λ κ² ν₯λ―Έλ‘μ λ€.

μ΅μ νλ₯Ό μν΄ μ ννλ‘ μ€μμΌλ‘ κ°μλ‘ LODκ° λμ λ§€μ¬λ₯Ό μ¬μ©νκ³ νλ μ΄μ΄λ₯Ό κ³μ λ°λΌλ€λλ©΄ ν°κ° λκΈ° λλ¬Έμ μΌμ 거리 μ΄μ λ²μ΄μ§λ©΄ κ·Έλ νλ μ΄μ΄ μμΉλ‘ μκ°μ΄λ μν¨λ€.
2. Procedural Terrain
Procedural Terrainμ god like coder Sebastian Lagueμ Procedural Landmass Generationμ μ리μ¦μ μΌλΆλΆμ μ μ©νμλ€.
a. Height Map
![]() | ![]() |
Height Mapμ 0(Black)κ³Ό 1(White) μ¬μ΄μ κ°μΌλ‘ λμ΄λ₯Ό νννλ€. μΌλ°μ μΈ λ Έμ΄μ¦κ° μλλΌ Perline Noiseλ₯Ό μ£Όλ©΄ μ’ λ organic ν λλμ μ€μ μ°λ§₯μ΄λ λ¬Όμ νννλ λ° λ§μ΄ μ¬μ©λλ κ² κ°λ€. μμ±λ Height Mapμ κ°μ λ°λΌ μμ λνλ©΄ μ€λ₯Έμͺ½κ³Ό κ°μ κ²°κ³Όκ° λμ¨λ€.
b. Generate Mesh

Meshμ vertex λ₯Ό Height Mapμ λμ΄κ°μ λ°λΌ μ‘°μ νλ©΄ μ°λ§₯ λλμ λ§λ€ μ μλ€.
3. Prototype Result

μ¬κΈ°κΉμ§κ° Prototypeμ κ²°κ³Όλ€.
4. Garbage & Spawner

Garbageλ νλ μ΄μ΄κ° Grappling HookμΌλ‘ κ±΄μ Έλ΄λ©΄ μ μκ° μ¬λΌκ°λ€. λλ¬Έμ κ·Έλ₯ λ₯λ₯ λ λ€λλ©΄ λ무 μ½κ³ μ¬λ―Έκ° μλ€. μ‘°κΈμ 리νλ μ΄μ±μ μ£ΌκΈ° μν΄ λ€μκ³Ό κ°μ Life Cycleκ³Ό Stateλ₯Ό μ£Όμλ€.
a. ꡬ쑰
Life Cycle
Garbageλ Spawnerμ μν΄ μ€ν°λ Spawnerμ λ νΌλ°μ€λ₯Ό μ£Όμ
λ°κ³ νμ±νλλ€. Spawnerλ 미리 μ§μ λ μλ§ μμ μ μμμ 무μμ μ€ν°μνκ³ _count
λ‘ νμ±νλ Garbageμ μλ₯Ό κ΄λ¦¬νκ³ μλ€. νλ μ΄μ΄κ° Garbageλ₯Ό μ‘μΌλ©΄ μ‘ν Garbageλ μ°Έμ‘°νκ³ μλ Spawnerμ countλ₯Ό νλ μ€μ΄κ³ λΉνμ±νλλ€.
FSM
Garbageλ ꡬ ννμ Detect Rangeκ° μκ³ νλ μ΄μ΄κ° 거리 μμΌλ‘ λ€μ΄μ€λ©΄ νλ μ΄μ΄ λ°λ λ°©ν₯μΌλ‘ λλ§μΉλ€.
b. Garbage Pool

Garbage Poolμ λμ λ리μ νλ‘ κ΅¬ννμλ€.
ObjectPool.cs
[System.Serializable]
public class Pool
{
[SerializeField] private SpawnObjectTag tag = SpawnObjectTag.Garbage;
[SerializeField] private List<GameObject> prefabs = null;
[SerializeField] private int size = 0;
public SpawnObjectTag Tag { get => tag; set => tag = value; }
public List<GameObject> Prefabs { get => prefabs; set => prefabs = value; }
public int Size { get => size; set => size = value; }
}
prefabs
λ μ¬λ¬κ°μ§ Garbage Poolλ₯Ό λ΄κΈ° μν΄ λ¦¬μ€νΈλ‘ λ§λ€μλ€.
ObjectPool.cs
#region Initialize
private void InitVariables()
{
poolDict = new Dictionary<string, Queue<GameObject>>();
}
private void CreatePools()
{
foreach (Pool pool in pools)
{
Queue<GameObject> objectPool = new Queue<GameObject>();
for (int i = 0; i < pool.Size; i++)
{
int randomIndex = Random.Range(0, pool.Prefabs.Count - 1);
GameObject obj = Instantiate(pool.Prefabs[randomIndex]);
obj.SetActive(false);
objectPool.Enqueue(obj);
}
poolDict.Add(pool.Tag.ToString(), objectPool);
}
}
#endregion
Garbage Poolμ μμ± κ³Όμ μ 보면 λλ€μΌλ‘ prefabs
리μ€νΈμμ νλλ₯Ό κ°μ Έμ Instantiate()
νκ³ λμ
λ리 νμ λ΄λλ€.
b. Random Spawner

Random Spawnerλ₯Ό μνλ κ³³μ λ°°μΉνκ³ λ²μλ₯Ό μ ν μ μλ€. λ²μλ νλΈ ννμ΄λ€.
RandomAreaSpawner.cs
private void CalculateRandomPoint()
{
_randomRange = new Vector3(
Random.Range(-_range.x, _range.x),
Random.Range(-_range.y, _range.y),
Random.Range(-_range.z, _range.z));
_randomPoint = _origin + _randomRange;
}
_randomPoint
λ Random Spawnerμ position κ°μ _randomRange
offsetμ λν μμΉλ€. _range
λ transform.localScale / 2.0f
κ°μ κ°μ§λ€.
RandomAreaSpawner.cs
private void RandomPointSpawn()
{
GameObject obj2Spawn = ObjectPool.Instance.Spawn(spawnObject,
_randomPoint, Quaternion.identity);
obj2Spawn.GetComponent<IDependencyInjection>().Injection(gameObject);
}
Random Spawnerλ Garbage Poolλ₯Ό μμ±ν λ μμ μ λ νΌλ°μ€λ₯Ό λ΄μμ€λ€.
c. Garbage

Gizmosλ‘ λ³΄μ΄λ λ²μκ° Garbageμ Detect Rangeμ΄λ€. νλ μ΄μ΄κ° λ²μμμ λ€μ΄μ€λ©΄ Navmesh Agentκ° νμ±ν λκ³ νλ μ΄μ΄ λ°λ λ°©ν₯μΌλ‘ λλ§μΉλ€.

Bakeλ λͺ¨μ΅μ΄λ€. λ¬Ό Meshλ staticμ΄ μλκΈ° λλ¬Έμ staticμΈ plane νλλ₯Ό λ§λ€μ΄ Bakeλ₯Ό νκ³ Colliderμ Mesh Rendererλ₯Ό κΊΌλ²λ Έλ€.

νλ μ΄μ΄κ° Garbageλ₯Ό κ±΄μ Έλ΄λ λλμ μ΄ κ²μμ ν΅μ¬μ΄λ€.
GarbageController.cs
public void Captured()
{
_isCaptured = true;
RB.AddForce(Vector3.up * 9000, ForceMode.Impulse);
Instantiate(impactOnGarbage, transform.position, Quaternion.identity);
}
κ±΄μ Έλ΄λ λλμ λ μ΄λ¦¬κΈ° μν΄ Garbageκ° μ‘νλ μκ° Vector3.up
λ°©ν₯μΌλ‘ νμ΄ μ€λ₯΄κ² νλ€.
5. UI

UIλ ν¬κ² 4κ°μ§κ° μλ€; Main Menu UI, In Game UI, Result UI, Transition UI.
a. Main Menu UI

κ°λ¨ν μμκ³Ό μ’ λ£ UIμ΄λ€.
b. In Game UI

Circle Progress UIλ μλ μμ±λλ Garbageμ μ μ νλ μ΄μ΄κ° κ±΄μ ΈλΈ Garbageμ μλ₯Ό νννκΈ° μν UI μμΌλ νλ©΄μ λ무 λ§μ΄ μ‘μλ¨Ήμ΄μ νμ΄λ λ²μ μμλ λΉ μ‘λ€.

Circle Progress UIλ Slider UIλ₯Ό λκ°λ₯Ό μ΄μ©ν΄ λ§λ€μλ€. Sliderμ μμ‘μ΄λ κ²μ΄μ§μ μ 체κ°μ μ£ΌκΈ°μν΄ λ©μ§νκ² λ§λ€μκ³ μλ‘μ μμμ μΉ¨λ²νμ§ μκ² νκΈ° μν΄ Maskλ₯Ό μ»λ€.
![]() | ![]() |
UI κΈλ‘μ° ν¨κ³Όλ μ΄λ―Έμ§ λκ°λ₯Ό ν©μ³μ λ§λ€μλ€.
a. Resut UI

Result UIλ κ²μμ΄ νμμμ λλ©΄ μ¬μ©μμ ν€λ³΄λ μΈνμ λ§κ³ κ²°κ³Ό μ°½μ΄ λ¬λ€.
a. Transition UI

Transistion UIλ μ§μ κ·Έλ €μ μ¬μ©νμλ€.
LevelLoader.cs
public void LoadNextScene()
{
transitionUI.SetActive(true);
StartCoroutine(LoadSceneAsync(
SceneManager.GetActiveScene().buildIndex + 1));
}
IEnumerator LoadSceneAsync(int scene)
{
waveAnimator.SetTrigger("transition");
yield return new WaitForSeconds(transitionTime);
AsyncOperation operation = SceneManager.LoadSceneAsync(scene);
while (!operation.isDone)
{
float progress = Mathf.Clamp01(operation.progress / .9f);
yield return null;
}
}
μλ‘μ΄ μ¬μ΄ λ‘λλ λ νΈμΆλλ€.
6. Final Result
