본문 바로가기
디지털노마드

[챗GPT게임개발35] GPT-4, GPT-4o 활용해서 오브젝트 풀링 방식으로 총알 관리하기(2)

by 인텔리노마드라이프 2024. 5. 25.
728x90

안녕하세요. 인텔리원스튜디오(IntelliOneStudio)입니다.

오늘은 지난 시간에 이어 GPT-4, GPT-4o 활용해서 오브젝트 풀링 방식으로 총알을 관리해볼거예요.

[오늘의 목표] GPT-4, GPT-4o 활용해서 오브젝트 풀링 방식으로 총알을 관리
1. BulletPool 클래스 만들기
2.PlayerBullet 클래스 만들기
3.PlayerController 클래스 수정하기

*아래 지난 기사 참고

2024.05.18 - [디지털노마드] - [챗GPT게임개발30] GPT-4, GPT-4o 활용해서 플레이어의 총알 발사 애니메이션 적용하기(1)

 

[챗GPT게임개발30] GPT-4, GPT-4o 활용해서 플레이어의 총알 발사 애니메이션 적용하기(1)

안녕하세요. 인텔리원스튜디오(IntelliOneStudio)입니다.오늘은 GPT-4, GPT-4o 활용해서 플레이어의 총알 발사 애니메이션 적용 중 총알을 발사할때 필요한 플레이어의 동작 애니메이션을 추가해볼께요

intellinomadlife.tistory.com

 

 ChatGPT 답변 내용 중 제가 별도로 표시한 파란색 내용을 기준으로 읽으시면 좀 더 이해가 쉽게 될거예요.

오브젝트 풀링 방식이란?

오브젝트 풀링 방식의 개념과 왜 오브젝트 풀링 방식을 사용해야 하는 지 살펴봐요.

 

다음은 한줄로 요약한 내용이예요.

[설명]
오브젝트 풀링은 자주 사용되는 오브젝트를 미리 생성해 두고 재사용하여 성능을 최적화하고 메모리 관리를 효율화하는 기법

[반드시 사용해야 하는 이유]
오브젝트 풀링은 성능 저하와 메모리 누수를 방지하여 안정적이고 효율적인 게임 개발을 가능

 

좀 더 상세히 살펴봐요.

오브젝트 풀링 방식에 대한 설명

**오브젝트 풀링(Object Pooling)**은 자원을 효율적으로 관리하기 위한 디자인 패턴으로, 주로 반복적으로 생성되고 파괴되는 오브젝트를 미리 생성해 두고 필요할 때 재사용하는 방식입니다. 이 방식은 게임 개발에서 자주 사용되며, 다음과 같은 단계를 포함합니다.

주요 개념 및 단계

  1. 초기화:
    • 게임 시작 시 일정 수량의 오브젝트를 미리 생성하고 비활성화 상태로 유지합니다.
  2. 사용 시 할당:
    • 오브젝트가 필요할 때, 미리 생성된 오브젝트를 활성화하여 사용합니다.
    • 오브젝트가 부족하면 새로운 오브젝트를 생성하여 풀에 추가합니다.
  3. 사용 후 반환:
    • 사용이 끝난 오브젝트는 비활성화 상태로 풀에 반환되어 재사용 대기 상태가 됩니다.

장점

  1. 성능 향상:
    • 오브젝트를 미리 생성해 두기 때문에 런타임 동안 자주 발생하는 오브젝트 생성 및 파괴로 인한 성능 저하를 방지할 수 있습니다.
    • 가비지 컬렉션 발생을 줄여 게임 성능을 최적화할 수 있습니다.
  2. 일관된 메모리 사용:
    • 오브젝트를 반복적으로 생성하고 파괴하는 대신 재사용함으로써 메모리 사용 패턴이 일관되어 메모리 관리가 쉬워집니다.
  3. 간편한 관리:
    • 미리 생성된 오브젝트를 관리하기 때문에 코드가 간결해지고 유지보수가 용이합니다.

단점

  1. 초기 메모리 사용량 증가:
    • 초기화 시 많은 오브젝트를 미리 생성하기 때문에 메모리 사용량이 증가할 수 있습니다.
  2. 유휴 자원 문제:
    • 사용되지 않는 오브젝트가 풀에 남아 있어 메모리를 차지할 수 있습니다.

반드시 사용해야 하는 이유

오브젝트 풀링을 사용해야 하는 이유는 주로 성능 최적화와 안정적인 메모리 관리에 있습니다. 다음은 오브젝트 풀링을 사용해야 하는 주요 이유들입니다.

1. 성능 최적화

게임에서는 총알, 적 캐릭터, 파티클 효과 등 여러 오브젝트가 빠르게 생성되고 파괴됩니다. 이러한 오브젝트의 빈번한 생성과 파괴는 CPU와 메모리에 큰 부하를 줄 수 있습니다. 오브젝트 풀링을 사용하면 이러한 부하를 줄이고 게임의 전반적인 성능을 향상시킬 수 있습니다.

  • 프레임률 안정화: 오브젝트 풀링을 통해 오브젝트 생성 및 파괴에 따른 성능 저하를 방지하여 일정한 프레임률을 유지할 수 있습니다.
  • CPU 부하 감소: 자주 생성되고 파괴되는 오브젝트를 미리 생성해 두고 재사용함으로써 CPU 부하를 줄일 수 있습니다.

2. 메모리 관리 효율화

  • 가비지 컬렉션 최소화: 반복적으로 생성되고 파괴되는 오브젝트는 많은 메모리 할당과 해제를 발생시키며, 이는 가비지 컬렉션을 자주 발생시켜 성능 저하를 일으킵니다. 오브젝트 풀링은 이러한 문제를 최소화합니다.
  • 메모리 사용량 예측 가능: 오브젝트 풀링을 사용하면 메모리 사용량이 일정하게 유지되므로, 메모리 사용량을 예측하고 관리하기가 용이합니다.

3. 코드의 간결성 및 유지보수 용이성

  • 재사용성: 오브젝트 풀링을 통해 오브젝트의 생성과 파괴를 일관되게 관리할 수 있어, 코드의 재사용성을 높일 수 있습니다.
  • 버그 감소: 일관된 오브젝트 관리로 인해 예기치 않은 버그 발생을 줄일 수 있습니다. 예를 들어, 사용 후 오브젝트가 제대로 정리되지 않아 발생하는 메모리 누수 문제를 방지할 수 있습니다.

이제 총알을 오브젝트 풀링 방식으로 관리하는 스크립트를 만들어요.

BulletPool 클래스 만들기

BulletPool 클래스는 총알 오브젝트를 미리 생성해 두고, 필요할 때마다 재사용할 수 있도록 관리하는 역할을 해요. 

주요 기능

  • 초기화: 풀의 초기 크기만큼 총알 오브젝트를 미리 생성하고 비활성화 상태로 큐에 저장합니다.
  • 총알 가져오기: 풀에서 사용 가능한 총알 오브젝트를 가져와 활성화합니다.
  • 총알 반환하기: 사용이 끝난 총알 오브젝트를 다시 비활성화하고 풀에 반환합니다.
using System.Collections.Generic;
using UnityEngine;

namespace Platformer.Mechanics
{
    public class BulletPool : MonoBehaviour
    {
        public GameObject bulletPrefab; // 총알 프리팹
        public int initialPoolSize = 10; // 초기 풀 크기

        private Queue<GameObject> pool = new Queue<GameObject>();

        void Awake()
        {
            // 초기화 시 지정된 크기만큼 총알 생성
            for (int i = 0; i < initialPoolSize; i++)
            {
                GameObject bullet = Instantiate(bulletPrefab);
                bullet.SetActive(false);
                pool.Enqueue(bullet);
            }
        }

        // 총알을 풀에서 가져오는 메서드
        public GameObject GetBullet()
        {
            if (pool.Count > 0)
            {
                GameObject bullet = pool.Dequeue();
                bullet.SetActive(true);
                return bullet;
            }
            else
            {
                // 풀에 사용 가능한 총알이 없으면 새로 생성
                GameObject bullet = Instantiate(bulletPrefab);
                return bullet;
            }
        }

        // 총알을 풀에 반환하는 메서드
        public void ReturnBullet(GameObject bullet)
        {
            bullet.SetActive(false);
            pool.Enqueue(bullet);
        }
    }
}

 

PlayerBullet 클래스 만들기

PlayerBullet 클래스는 총알이 특정 조건(충돌 또는 일정 시간 경과 등)에서 풀로 반환되도록 해요.

주요 기능

  • 총알 반환: 총알이 충돌하거나 일정 시간이 지나면 풀로 반환합니다.
using UnityEngine;
using Platformer.Core;
using Platformer.Mechanics;
using System.Collections;

namespace Platformer.Gameplay
{
    public class PlayerBullet : MonoBehaviour
    {
        public float lifeTime = 2f; // 총알의 수명을 나타내는 변수

        // 총알이 활성화될 때 호출되는 메서드
        private void OnEnable()
        {
            // 일정 시간 후에 총알을 풀로 반환하는 코루틴 시작
            StartCoroutine(ReturnToPoolAfterTime(lifeTime));
        }

        // 일정 시간이 지난 후 총알을 풀로 반환하는 코루틴
        private IEnumerator ReturnToPoolAfterTime(float time)
        {
            // 지정된 시간 동안 대기
            yield return new WaitForSeconds(time);
            // 총알을 풀로 반환
            ReturnToPool();
        }

        // 총알이 충돌할 때 호출되는 메서드
        private void OnCollisionEnter2D(Collision2D collision)
        {
            // 충돌한 객체가 플레이어인 경우, 아무 작업도 하지 않고 반환
            if (collision.gameObject.CompareTag("Player"))
            {
                return;
            }

            // 총알을 풀로 반환
            ReturnToPool();

            // 충돌한 객체가 적인 경우
            if (collision.gameObject.CompareTag("Enemy"))
            {
                // 적의 EnemyController 컴포넌트를 가져옴
                var enemy = collision.gameObject.GetComponent<EnemyController>();
                if (enemy != null)
                {
                    // 적이 타격을 입었을 때 처리
                    HandleEnemyHit(enemy);
                }
            }
        }

        // 총알을 풀로 반환하는 메서드
        private void ReturnToPool()
        {
            // 현재 씬에서 BulletPool을 찾아옴
            var bulletPool = FindObjectOfType<BulletPool>();
            if (bulletPool != null)
            {
                // 총알을 풀로 반환
                bulletPool.ReturnBullet(gameObject);
            }
            else
            {
                // BulletPool을 찾을 수 없는 경우 총알을 파괴
                Destroy(gameObject);
            }
        }

        // 적이 타격을 입었을 때 처리하는 메서드
        private void HandleEnemyHit(EnemyController enemy)
        {
            // 적의 Health 컴포넌트를 가져옴
            var enemyHealth = enemy.GetComponent<Health>();
            if (enemyHealth != null)
            {
                // 적의 체력을 감소시킴
                enemyHealth.Decrement();
                if (!enemyHealth.IsAlive)
                {
                    Debug.Log("적이 처치되었습니다.");
                    // 적이 사망한 경우 적의 사망 이벤트를 예약
                    Simulation.Schedule<EnemyDeath>().enemy = enemy;
                }
                else
                {
                    Debug.Log("적은 타격을 받았으나 아직 살아있습니다.");
                }
            }
            else
            {
                // 적의 Health 컴포넌트를 찾을 수 없는 경우 적의 사망 이벤트를 예약
                Simulation.Schedule<EnemyDeath>().enemy = enemy;
            }
        }
    }
}

 

PlayerController 클래스 수정하기

PlayerController 클래스는 플레이어가 총알을 발사할 때 BulletPool을 이용하여 총알을 관리하게 되요.

해당 부분을 추가해볼께요.

주요 기능

  • 총알 발사: 총알 발사 버튼이 눌리면 BulletPool에서 총알을 가져와 발사 위치에 배치하고, 속도를 설정합니다.
  • 총알 반환: 일정 시간이 지나거나 충돌 시 총알을 풀에 반환합니다.

public void SpawnBullet()
{
    // bulletPool이 null이 아닌 경우에만 총알을 생성
    if (bulletPool != null)
    {
        // 플레이어가 바라보는 방향에 따라 총알 스폰 위치를 설정
        Transform spawnPoint = spriteRenderer.flipX ? bulletSpawnLeft : bulletSpawnRight;
        
        // BulletPool에서 총알을 가져옴
        GameObject bullet = bulletPool.GetBullet();
        
        // 총알의 위치를 스폰 위치로 설정
        bullet.transform.position = spawnPoint.position;

        // 총알의 Rigidbody2D 컴포넌트를 가져옴
        Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
        
        // 플레이어가 바라보는 방향에 따라 총알의 속도를 설정
        float direction = spriteRenderer.flipX ? -1 : 1;
        rb.velocity = new Vector2(direction * projectileSpeed, 0);

        // 일정 시간 후에 총알을 풀로 반환하는 코루틴 시작
        StartCoroutine(ReturnBulletToPoolAfterTime(bullet, 2f)); // 2초 후에 총알을 풀로 반환
    }
    else
    {
        // BulletPool이 할당되지 않은 경우 경고 메시지 출력
        Debug.LogWarning("BulletPool is null. Please assign a BulletPool in the inspector.");
    }
}

private IEnumerator ReturnBulletToPoolAfterTime(GameObject bullet, float time)
{
    // 지정된 시간 동안 대기
    yield return new WaitForSeconds(time);
    
    // 총알을 풀로 반환
    bulletPool.ReturnBullet(bullet);
}

 

요약을 해볼께요.

위 내용들을 요약하면 다음과 같아요.

  1. BulletPool 클래스: 총알 오브젝트를 미리 생성해 두고 필요할 때마다 재사용할 수 있도록 관리합니다.
  2. PlayerController 클래스: 총알 발사 버튼이 눌리면 BulletPool에서 총알을 가져와 발사 위치에 배치하고 속도를 설정합니다.
  3. PlayerBullet 클래스: 총알이 충돌하거나 일정 시간이 지나면 총알을 BulletPool로 반환합니다.

이와 같은 방식으로 총알을 풀링하여 관리하면,

성능을 최적화하고 불필요한 오브젝트 생성을 줄여 메모리 사용을 효율적으로 관리할 수 있어요. 

 

다음 시간에는 위 클래스들을 활용해서 총알을 발사하고 적에게 피해를 입혀볼께요.

[인텔리노마드라이프와 함께 행복하세요 : 인텔리원스튜디오]

 

지금까지,

언제나 성장하는 인텔리원스튜디오(IntelliOneStudio)입니다.

감사합니다.

728x90