EasyCastleUNITY

드래곤 플라이트 모작 개발일지2 (총알발사와 오브젝트 풀링) 본문

2D 프로젝트 개발 일지(드래곤 플라이트 모작)

드래곤 플라이트 모작 개발일지2 (총알발사와 오브젝트 풀링)

EasyCastleT 2023. 9. 13. 17:40

총알발사 

오브젝트 풀링을 적용해보기 전에, 

총알을 특정시기마다 복제하고 발사하는 방식을 먼저 구현해보겠습니다. 

이 시점 부터는 GameScene 자체를 관리하는

GameMain을 만들고 최상위에 두는 구조로 만들어 보겠습니다. 

전 포스트에서 작성한 플레이어와 드래곤도 GameMain에서 관리합니다. 


총알을 발사할때마다 복제하고 발사하는 기능 구현 (오브젝트 풀링 사용 X)

Bullet -> 총알 자체의 움직임을 제어하는 스크립트

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

public class Bullet : MonoBehaviour
{
    private float moveSpeed;
    // Start is called before the first frame update
    void Start()
    {
        this.moveSpeed = 10.0f;
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        this.transform.Translate(Vector2.up * this.moveSpeed * Time.deltaTime);
        this.SelfDestroy();
    }

    //테스트
    private void SelfDestroy()
    {
        if (this.transform.position.y > 5.86f)
        {
            Destroy(this.gameObject);
            Debug.Log("<color=red>총알 삭제</color>");
        }
    }
}

BulletGenerator -> 총알을 생성

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

public class BulletGenerator : MonoBehaviour
{
    [SerializeField]
    private Transform bulletInitPos; // 총알 생성 위치

    private float elapsedTime = 0.0f; //경과시간
    [SerializeField] private float intervelTime = 0.3f;
    [SerializeField] private GameObject bulletPrefab;
    
    public void Shoot()
    {
        this.elapsedTime += Time.deltaTime;
        if(this.elapsedTime > intervelTime)
        {
            GameObject go =Instantiate(this.bulletPrefab);
            go.transform.position = this.bulletInitPos.position;
            this.elapsedTime = 0.0f;
        }
    }
}

GameMain -> 게임 씬 전체를 관리

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

public class GameMain : MonoBehaviour
{
    [SerializeField] private Player player;
    [SerializeField] private Dragon[] dragons;
    [SerializeField] private BulletGenerator bulletGenerator;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        this.bulletGenerator.Shoot();
    }
}

총알의 생성위치는 빈 오브젝트를 생성하여, 플레이어의 자식으로 만들어서, 

플레이어가 움직이면, 총알의 생성위치도 같이 움직이도록 하였습니다. 

 

오브젝트 풀링

위에 방식으로 만들면, 만드는 순간마다 메모리를 사용하게 됨으로 성능의 저하를 일으킵니다.

그래서 이를 해결하기 위해 실행하는 순간에 미리 총알을 만들어 두고,

그 총알들을 불러오는 오브젝트 풀링을 구현하려고 합니다. 

BulletPoolManager -> 만들어둔 총알들을 관리하는 스크립트

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

public class BulletPoolManager : MonoBehaviour
{
    //총알을 미리 생성해 저장할 리스트
    [SerializeField]
    private List<GameObject> bulletPool = new List<GameObject>();
    //오브젝트 풀에 생성할 총알의 최대 개수 
    [SerializeField] private int maxBullets = 10;
    //싱글톤 인스턴스 선언 
    public static BulletPoolManager instance = null;
    //총알 프리팹
    [SerializeField] private GameObject bulletPrefab;

    #region 속성
    public List<GameObject> BulletPool { get => bulletPool; }
    #endregion

    private void Awake()
    {
        if(instance == null)
        {
            instance = this;
        }
        else if(instance != null)
        {
            Destroy(this.gameObject);
        }
        // 아직 게임 씬만 구성하지만
        // 추후에 새로운 씬도 추가할 수 있도록 사용 
        DontDestroyOnLoad(gameObject); 
    }
    private void Start()
    {
        this.CreateBulletPool();
    }
    //실행시 총알 만들기
    private void CreateBulletPool()
    {
        for(int i=0; i<maxBullets; i++)
        {
            GameObject bulletGo = Instantiate(this.bulletPrefab);
            bulletGo.SetActive(false);
            bulletGo.transform.SetParent(this.transform);
            bulletPool.Add(bulletGo);
        }
    }
    //풀에서 총알 받아가기 
    public GameObject GetBulletInPool()
    {
        foreach(GameObject bullet in this.bulletPool)
        {
            //활성화 상태가 아니면 받아옴
            if (bullet.activeSelf == false)
            {
                return bullet;
            }
        }
        return null;
    }
    //돌아오기 
    public void ReleaseBullet(GameObject bulletGo)
    {
        bulletGo.SetActive(false);
        bulletGo.transform.SetParent(this.transform);
    }
}

BulletGenerator -> 총알을 풀에서 받아오고 발사하는 스크립트,

                                풀에서 만들어둔 총알이 부족할 경우, 동적으로 생성하고 리스트에 추가

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

public class BulletGenerator : MonoBehaviour
{
    [SerializeField]
    private Transform bulletInitPos; // 총알 생성 위치

    private float elapsedTime = 0.0f; //경과시간
    [SerializeField] private float intervelTime = 0.3f;
    [SerializeField] private GameObject bulletPrefab;
    
    public void Shoot()
    {
        this.elapsedTime += Time.deltaTime;
        if(this.elapsedTime > intervelTime)
        {
            this.CallBullet();
            this.elapsedTime = 0.0f;
        }
    }

    private void CallBullet()
    {
        GameObject go = BulletPoolManager.instance.GetBulletInPool();
        if (go == null) //이미 만들어둔 총알을 다 사용하면
        {
            go = Instantiate(this.bulletPrefab);
            BulletPoolManager.instance.BulletPool.Add(go);
            go.transform.position = this.bulletInitPos.position;
            Debug.Log("<color=cyan>총알 동적 생성</color>");
        }
        else if(go != null) //이미 만들어둔 총알이라면
        {
            go.transform.SetParent(null); //매니저 밖으로 나옴
            go.transform.position = this.bulletInitPos.position;
            go.SetActive(true); // false였으니 true로 변환
        }
    }
}

Bullet -> 총알 자체의 움직임을 담당하는 스크립트, 일정 범위를 넘어가면 풀로 되돌아간다.

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

public class Bullet : MonoBehaviour
{
    private float moveSpeed;
    // Start is called before the first frame update
    void Start()
    {
        this.moveSpeed = 10.0f;
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        this.transform.Translate(Vector2.up * this.moveSpeed * Time.deltaTime);
        this.SelfComeback();
    }

    //테스트
    private void SelfComeback()
    {
        if (this.transform.position.y > 5.86f)
        {
            Debug.Log("<color=yellow>총알 풀로 되돌아감</color>");
            BulletPoolManager.instance.ReleaseBullet(this.gameObject);
        }
    }
}

메인은 동일합니다. 

오브젝트 풀링 적용 결과

이번 개발일지에서는 총알을 동적으로 생성하여 발사하는 방법과, 오브젝트 풀링을 적용하는 방법을 해보았습니다.

다음 개발일지에서는 총알과 드래곤의 충돌처리를 다루겠습니다.