TIL - 오브젝트 풀링과 Enemy 스폰, 충돌 처리

김보근·2025년 8월 15일

Unity

목록 보기
112/113
post-thumbnail

TIL – 오브젝트 풀링과 Enemy 스폰, 충돌 처리

1. Object Pool 분리 관리

기존에는 prefabs 배열 하나로 Bullet과 Enemy를 같이 관리했는데,
종류를 구분하기 위해 PoolKey Enum을 만들어 관리하도록 변경했다.

public enum PoolKey
{
    PlayerBullet,
    Enemy
}

ObjectPoolManager에서 Get(PoolKey key)로 종류를 명확히 지정하면,
총알과 적 프리팹을 구분해서 꺼내올 수 있다.

2. Bullet이 Enemy에 데미지를 직접 적용

기존 구조에서는 Enemy가 Bullet을 참조해서 데미지를 가져왔지만,
이러면 Enemy가 Bullet의 내부를 알아야 해서 의존성이 생긴다.

그래서 Bullet이 Enemy의 TakeDamage()를 직접 호출하게 변경했다.

private void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.CompareTag("Enemy"))
    {
        Enemy enemy = collision.GetComponent<Enemy>();
        if (enemy != null)
        {
            enemy.TakeDamage(damage);
        }
        gameObject.SetActive(false);
    }
}

3. GameManager 리팩터링 (Enemy 스폰)

GameManager에서 Enemy를 스폰할 때,
필드에 Enemy를 직접 들고 있지 않고 풀에서 꺼낸 Enemy에서 speed를 가져오는 방식으로 변경했다.

IEnumerator MonsterSpawn()
{
    GameObject monster = ObjectPoolManager.Instance.Get(PoolKey.Enemy);
    Enemy enemyScript = monster.GetComponent<Enemy>();
    Rigidbody2D rb = monster.GetComponent<Rigidbody2D>();

    monster.transform.position = enemySp.position;
    rb.velocity = enemySp.up * enemyScript.speed;

    yield return null;
}

Enemy 속도를 Enemy 스크립트에서 관리하므로 몬스터별 속도 설정 가능

GameManager는 언제, 어디서 스폰할지만 책임짐

4. Enemy가 Player와 충돌 시 멈추기

Enemy에 OnTriggerEnter2D()를 구현해서 Player에 닿으면 속도를 0으로 만든다.

private void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.CompareTag("Player"))
    {
        rb.velocity = Vector2.zero;
        Debug.Log($"{gameObject.name} → 플레이어 충돌, 이동 정지");
    }
}
  • Player 오브젝트에 Tag를 "Player"로 설정

  • Enemy 또는 Player 중 한쪽에는 Rigidbody2D 필요

  • 닿는 동안 멈추게 하려면OnTriggerStay2D()도 가능

  • 떨어졌을 때 다시 이동하려면 OnTriggerExit2D()에서 속도 부여

오늘 느낀 점

오브젝트 풀링은 종류별로 구분하면 나중에 관리가 훨씬 편하다.

다음에 해볼 것

  • Wave별로 Enemy 스폰 속도와 개수 증가시키는 라운드 시스템

작성한 코드

ObjectPoolManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum PoolKey
{
    PlayerBullet,
    //EnemyBullet,
    Enemy
}

public class ObjectPoolManager : Singleton<ObjectPoolManager>
{
    [System.Serializable]
    public class Pool
    {
        public PoolKey key;
        public GameObject prefab;
    }

    public Pool[] poolsConfig;

    private Dictionary<PoolKey, List<GameObject>> poolsDict;

    private void Awake()
    {
        poolsDict = new Dictionary<PoolKey, List<GameObject>>();

        foreach (var config in poolsConfig)
        {
            poolsDict[config.key] = new List<GameObject>();
        }
    }

    public GameObject Get(PoolKey key)
    {
        var list = poolsDict[key];
        GameObject select = null;

        foreach (GameObject item in list)
        {
            if (!item.activeSelf)
            {
                select = item;
                select.SetActive(true);
                return select;
            }
        }

        var prefab = System.Array.Find(poolsConfig, p => p.key == key).prefab;
        select = Instantiate(prefab, transform);
        list.Add(select);

        return select;
    }
}
profile
게임개발자꿈나무

0개의 댓글