
2D ํ์ด๋ธ๋ฆฌ๋ ํ์ ๋ํ์ค ๊ฒ์์ ํต์ฌ ์์คํ ๊ฐ๋ฐ ๊ณผ์
(์ง๊ธ๊น์ง์ ์์ ๋ฌผ์ AI๊ฐ ์์ฑํด์ค ๊ฐ๋ฐ์ผ์ง)
ํ์๋ฅผ ์ฌ๋ฌ ๊ฐ ๊ฑด์คํ๊ณ ์ ์ด ๋ชฐ๋ ค์ค๋ฉด ๋์ ๋๋ ๋ ์ด ๋ฐ์ํ๋ค.
์์ธ์ ๋ช
ํํ๋ค. BasicTower๊ฐ ๊ณต๊ฒฉํ ๋๋ง๋ค ๋ฐ์ฌ์ฒด๋ฅผ Instantiate()๋ก ์์ฑํ๊ณ , ๋ช
์คํ๋ฉด Destroy()๋ก ํ๊ดดํ๋ ๊ตฌ์กฐ์๊ธฐ ๋๋ฌธ์ด๋ค.
// ๊ธฐ์กด ์ฝ๋ - ๋งค ๊ณต๊ฒฉ๋ง๋ค ์์ฑ/ํ๊ดด ๋ฐ๋ณต
public override void Attack()
{
GameObject projectileObj = Instantiate(projectilePrefab, spawnPos, Quaternion.identity);
...
}
void HitTarget()
{
enemy.TakeDamage(damage);
Destroy(gameObject); // ๋งค๋ฒ ํ๊ดด
}
ํ์ 10๊ฐ๊ฐ ์ด๋น 1๋ฐ์ฉ ์๋ฉด, ๋งค ์ด๋ง๋ค 10๋ฒ์ Instantiate + 10๋ฒ์ Destroy๊ฐ ๋ฐ์ํ๋ค.
์ด ๊ณผ์ ์์ ๋ฉ๋ชจ๋ฆฌ ํ ๋น โ GC(๊ฐ๋น์ง ์ปฌ๋ ์
) ๋ฐ๋ โ ํ๋ ์ ๋๋์ด ์ผ์ด๋๋ค.
์ค๋ธ์ ํธ ํ๋ง์ ํต์ฌ์ ๊ฐ๋จํ๋ค.
ํ๊ดดํ์ง ๋ง๊ณ ์จ๊ธฐ๊ณ , ์์ฑํ์ง ๋ง๊ณ ๊บผ๋ด ์ด๋ค.
๊ฒ์ ์์ ์ ๋ฐ์ฌ์ฒด๋ฅผ ๋ฏธ๋ฆฌ ๋ง๋ค์ด๋๊ณ , ํ์ํ ๋ ํ์ฑํํด์ ์ฐ๊ณ , ๋ค ์ฐ๋ฉด ๋นํ์ฑํํด์ ํ์ ๋๋ ค๋๋๋ค.
๊ธฐ์กด ๋ฐฉ์:
Attack โ Instantiate() โ ์ด๋ โ ๋ช
์ค โ Destroy() โ GC ๋ถํ
ํ๋ง ๋ฐฉ์:
๊ฒ์ ์์ โ ๋ฏธ๋ฆฌ 20๊ฐ ์์ฑ (๋นํ์ฑํ)
โ
Attack โ Pool.Get() (ํ์ฑํ) โ ์ด๋ โ ๋ช
์ค โ Pool.Return() (๋นํ์ฑํ)
โ
๋ค์ Attack โ ๊ฐ์ ์ค๋ธ์ ํธ ์ฌ์ฌ์ฉ
public class ProjectilePool : MonoBehaviour
{
public static ProjectilePool Instance;
[Header("Pool Settings")]
public GameObject projectilePrefab;
public int initialPoolSize = 20;
private Queue<GameObject> pool = new Queue<GameObject>();
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
InitializePool();
}
else
{
Destroy(gameObject);
}
}
void InitializePool()
{
for (int i = 0; i < initialPoolSize; i++)
{
CreateNewProjectile();
}
}
void CreateNewProjectile()
{
GameObject obj = Instantiate(projectilePrefab, transform);
obj.SetActive(false);
pool.Enqueue(obj);
}
public GameObject Get(Vector3 position, Quaternion rotation)
{
if (pool.Count == 0)
{
CreateNewProjectile();
}
GameObject obj = pool.Dequeue();
if (obj == null)
{
CreateNewProjectile();
obj = pool.Dequeue();
}
obj.transform.SetParent(null);
obj.transform.position = position;
obj.transform.rotation = rotation;
obj.SetActive(true);
return obj;
}
public void Return(GameObject obj)
{
if (obj == null) return;
obj.SetActive(false);
obj.transform.SetParent(transform);
pool.Enqueue(obj);
}
}
Queue๋ฅผ ์ฌ์ฉํ ์ด์ ๋ FIFO(์ ์
์ ์ถ) ๊ตฌ์กฐ๋ผ์ ๊ฐ์ฅ ์ค๋ ์ฐ ์ค๋ธ์ ํธ๋ถํฐ ์ฌ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ด๋ค. List๋ก๋ ๊ฐ๋ฅํ์ง๋ง Queue๊ฐ ์๋ฏธ์ ์ผ๋ก ๋ ๋ง๋ค.
ํ์ด ๋น์์ ๋๋ CreateNewProjectile()๋ก ์๋ ํ์ฅ๋๋ค. ์ฒ์์ 20๊ฐ๋ก ๋ถ์กฑํ๋ฉด ์์์ ๋์ด๋๋๊น ํ ํฌ๊ธฐ๋ฅผ ์ ๋ฐํ๊ฒ ๊ณ์ฐํ ํ์๊ฐ ์๋ค.
public class Projectile : MonoBehaviour
{
private Transform target;
private float damage;
private float speed;
private bool isInitialized = false;
private float lifetime = 7f;
private float aliveTimer;
private Vector3 lastDirection;
public void Initialize(Transform target, float damage, float speed)
{
this.target = target;
this.damage = damage;
this.speed = speed;
this.isInitialized = true;
this.aliveTimer = 0f;
this.lastDirection = Vector3.right;
}
void Update()
{
if (!isInitialized) return;
// 7์ด ์ด๊ณผ ์ ์๋ ํ์
aliveTimer += Time.deltaTime;
if (aliveTimer >= lifetime)
{
ReturnToPool();
return;
}
// ํ๊ฒ์ด ์์ผ๋ฉด ์ถ์ , ์์ผ๋ฉด ๋ง์ง๋ง ๋ฐฉํฅ์ผ๋ก ์ง์ง
Vector3 direction;
if (target != null)
{
direction = (target.position - transform.position).normalized;
lastDirection = direction;
}
else
{
direction = lastDirection;
}
transform.position += direction * speed * Time.deltaTime;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0, 0, angle);
if (target == null) return;
float distanceToTarget = Vector3.Distance(transform.position, target.position);
if (distanceToTarget < 0.2f)
{
HitTarget();
}
}
void HitTarget()
{
Enemy enemy = target.GetComponent<Enemy>();
if (enemy != null)
{
enemy.TakeDamage(damage);
}
ReturnToPool();
}
void ReturnToPool()
{
isInitialized = false;
target = null;
if (ProjectilePool.Instance != null)
{
ProjectilePool.Instance.Return(gameObject);
}
else
{
Destroy(gameObject);
}
}
}
๋ณ๊ฒฝ ํฌ์ธํธ ๋ ๊ฐ์ง:
Destroy โ ReturnToPool: Destroy(gameObject) ๋์ ProjectilePool.Instance.Return()์ ํธ์ถํ๋ค. ์ํ(isInitialized, target)๋ฅผ ์ด๊ธฐํํ๊ณ ๋นํ์ฑํ๋ง ํ๋ฉด ๋.
ํ๊ฒ ์์ค ์ฒ๋ฆฌ ๊ฐ์ : ๊ธฐ์กด์๋ ํ๊ฒ์ด ์ฌ๋ผ์ง๋ฉด ๋ฐ์ฌ์ฒด๊ฐ ๋ฐ๋ก ์์ด์ก๋ค. ์ด๊ฑด ์๊ฐ์ ์ผ๋ก ์ด์ํ๋ค. ์ ์ด ์ฃฝ์๋๋ฐ ๋ ์๊ฐ๋ ํ์์ด ๊ฐ์๊ธฐ ์ฌ๋ผ์ง๋๊น. ์ด์ ๋ lastDirection์ ์ ์ฅํด์ ๋ง์ง๋ง ๋ฐฉํฅ์ผ๋ก ๊ณ์ ์ง์งํ๋ค๊ฐ 7์ด ํ ์๋ ํ์๋๋ค.
ํ๊ฒ ์ด์์์ โ ์ถ์ ์ด๋
ํ๊ฒ ์ฌ๋ผ์ง โ ๋ง์ง๋ง ๋ฐฉํฅ์ผ๋ก ์ง์ง
7์ด ๊ฒฝ๊ณผ โ ์๋ ํ ๋ฐํ
public override void Attack()
{
if (currentTarget == null) return;
if (projectilePrefab == null) return;
Vector3 spawnPos = firePoint != null ? firePoint.position : transform.position;
GameObject projectileObj = null;
if (ProjectilePool.Instance != null)
{
projectileObj = ProjectilePool.Instance.Get(spawnPos, Quaternion.identity);
}
else
{
// ํ์ด ์์ผ๋ฉด ๊ธฐ์กด ๋ฐฉ์ (ํด๋ฐฑ)
projectileObj = Instantiate(projectilePrefab, spawnPos, Quaternion.identity);
}
Projectile projectile = projectileObj.GetComponent<Projectile>();
if (projectile != null)
{
projectile.Initialize(currentTarget, damage, projectileSpeed);
}
OnAttackExecuted();
}
ProjectilePool.Instance๊ฐ ์์ผ๋ฉด ๊ธฐ์กด Instantiate ๋ฐฉ์์ผ๋ก ํด๋ฐฑํ๋ค. ํ์ ์ ๋ง๋ค์ด๋ ๊ฒ์์ด ๋์๊ฐ๋ค.
| ํญ๋ชฉ | ๊ธฐ์กด | ํ๋ง ์ ์ฉ ํ |
|---|---|---|
| ์์ฑ | ๋งค ๊ณต๊ฒฉ๋ง๋ค Instantiate() | ๊ฒ์ ์์ ์ 1ํ |
| ์ ๊ฑฐ | ๋งค ๋ช
์ค๋ง๋ค Destroy() | SetActive(false) |
| GC ๋ฐ์ | ๋งค ํ๋ ์ | ๊ฑฐ์ ์์ |
| ํ๊ฒ ์์ค | ์ฆ์ ํ๊ดด (์ด์ํจ) | ์ง์ง ํ 7์ด ๋ค ํ์ |
์ค๋ธ์ ํธ ํ๋ง์ Unity์์ ๋ฐ๋ณต ์์ฑ/ํ๊ดด๋๋ ์ค๋ธ์ ํธ(๋ฐ์ฌ์ฒด, ์ดํํธ, ์ ๋ฑ)์ ๊ฑฐ์ ํ์์ ์ผ๋ก ์ ์ฉํ๋ ํจํด์ด๋ค. ๊ตฌํ ์์ฒด๋ Queue ํ๋๋ก ๊ฐ๋จํ๋ฐ, ์ฑ๋ฅ ์ฐจ์ด๋ ํฌ๋ค.