1011 Memory Pool

null·2023년 10월 11일

Unity Study

목록 보기
20/60

메모리풀 스크립트

using System.Collections;
using System.Collections.Generic;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;

//-----------------------------------------------------------------------------------------
// 메모리 풀 클래스
// 용도 : 특정 게임오브젝트를 실시간으로 생성과 삭제하지 않고,
//      : 미리 생성해 둔 게임오브젝트를 재활용하는 클래스입니다.
//-----------------------------------------------------------------------------------------

//  MonoBehaviour 상속 안받음. IEnumerable 상속시 foreach 사용 가능
//  System.IDisposable 관리되지 않는 메모리(리소스)를 해제 함

public class MemoryPool : IEnumerable, System.IDisposable
{
    class Item
    {
        public bool active; // 오브젝트가 사용하고 있는 중인지 판단하는 변수
        public GameObject gameObject; // 저장할 오브젝트
    }

    Item[] table;

    public IEnumerator GetEnumerator()
    {
        if (table == null)  // 만약 table이 객체화 되지 않았다면?
            yield break;    // 함수를 그냥 탈출

        // count는 table의 길이 (즉, 배열의 크기)
        int count = table.Length;

        for (int i = 0; i < count; i++) //총 count만큼 반복
        {
            Item item = table[i];
            // item에 table의 i위치에 해당하는 객체를 대입
            if (item.active)    // item이 사용중이면
            {
                yield return item.gameObject;   // 현 item의 오브젝트를 반환
            }
        }
    }

    //-------------------------------------------------------------------------------------
    // 메모리 풀 생성
    // original : 미리 생성해 둘 원본소스
    // count : 풀 최고 갯수
    //-------------------------------------------------------------------------------------
    
    public void Create(Object original, int count)
    {
        Dispose();  // 메모리풀 초기화
        table = new Item[count];    // count 만큼 배열을 생성

        for (int i = 0;i < count;i++)   // count 만큼 반복
        {
            Item item = new Item();
            item.active = false;
            item.gameObject = GameObject.Instantiate(original) as GameObject;
            // original을 GameObject 형식으로 item.gameObject에 저장
            item.gameObject.SetActive(false);
            // SetActive는 활성화 함수인데 메모리에만 올릴 것이므로 비활성화 상태로 저장
            table[i] = item;
        }
    }

    //-------------------------------------------------------------------------------------
    // 새 아이템 요청 - 쉬고 있는 객체를 반납한다.
    //-------------------------------------------------------------------------------------

    public GameObject NewItem() // GetEnumerator()와 비슷
    {
        if (table == null)
            return null;
        int count = table.Length;
        for (int i = 0; i < count; i++)
        {
            Item item = table[i];
            if (item.active == false)
            {
                item.active = true;
                item.gameObject.SetActive (true);
                return item.gameObject;
            }
        }

        return null;
    }
    
    //--------------------------------------------------------------------------------------
    // 아이템 사용종료 - 사용하던 객체를 쉬게한다.
    // gameOBject : NewItem으로 얻었던 객체
    //--------------------------------------------------------------------------------------

    public void RemoveItem(GameObject gameObject)
    {
        if (table == null || gameObject == null)
            return;

        int count = table.Length;

        for (int i = 0; i < count; i++)
        {
            Item item = table[i];
            // 매개변수 gameObject와 item의 gameObject가 같다면
            if (item.gameObject == gameObject)
            {
                // active 변수를 false로
                item.active = false;
                // 그리고 게임오브젝트를 비활성화 시킨다.
                item.gameObject.SetActive(false) ;
                break;
            }
        }
    }

    //--------------------------------------------------------------------------------------
    // 모든 아이템 사용종료 - 모든 객체를 쉬게한다.
    //--------------------------------------------------------------------------------------

    public void ClearItem()
    {
        if(table == null) return;

        int count = table.Length;

        for (int i = 0;i < count; i++)
        {
            Item item = table[i];
            if (item != null & item.active)
            {
                item.active = false;
                item.gameObject.SetActive(false) ;
            }
        }
    }

    //--------------------------------------------------------------------------------------
    // 메모리 풀 삭제 (게임 종료 or 플레이어 사망)
    //--------------------------------------------------------------------------------------

    public void Dispose()
    {
        if (table == null) return;

        int count = table.Length;

        for (int i = 0; i < count; i++)
        {
            Item item = table[i];
            GameObject.Destroy(item.gameObject);
            // 메모리 풀을 삭제하는 것이기 때문에 모든 오브젝트를 Destroy 처리
        }
        table = null;
    }
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}


플레이어 스크립트

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

public class CsPlayerFire : MonoBehaviour
{
    //투사체가 사라져야 할 조건에서 Collider를 비활성화를 시키고
    //그걸 캐치해서 미사일을 돌려보내는 방법

    public GameObject BoltPrefab;   // 복제할 투사체 오브젝트
    public Transform BoltMuzzle;    // 총구 위치

    public bool FireState;  // 투사체 발사 준비 여부
    public float FireDelay; // 투사체 발사 딜레이

    public int BoltMaxPool; // 메모리풀에 저장할 투사체 갯수
    MemoryPool MPool;   // 메모리풀
    GameObject[] BoltArray; // 메모리풀과 연동하여 사용할 투사체 배열

    private void OnApplicationQuit()
    {
        //  메모리풀 청소
        MPool.Dispose();
    }

    void Start()
    {
        //  메모리풀 준비(초기화)
        MPool = new MemoryPool();
        //  Bolt투사체를 MaxPool만큼 생성
        MPool.Create(BoltPrefab, BoltMaxPool);
        //  배열 준비(초기화) 이때 모든 값은 null이 됨
        BoltArray = new GameObject[BoltMaxPool];
    }

    IEnumerator FireCycleControl()
    {
        // 처음에 FireState를 false로 만들고
        FireState = false;
        // FireDelay초 후에
        yield return new WaitForSeconds(FireDelay);
        // FireState를 true로 만든다.
        FireState = true;
    }

    void Shoot()
    {
        if (FireState == true)
        {
                StartCoroutine(FireCycleControl());

            // 미사일 풀에서 발사되지 않은 미사일을 찾아서 발사
            for (int i = 0; i < BoltMaxPool; i++)
            {
                // 만약 미사일배열[i]가 비어있다면
                if (BoltArray[i] == null)
                {
                    // 메모리풀에서 미사일을 가져온다.
                    BoltArray[i] = MPool.NewItem();
                    // 해당 미사일의 위치를 미사일 발사지점으로 맞춘다.
                    BoltArray[i].transform.position = BoltMuzzle.transform.position;
                    // 발사 후에 for문을 바로 빠져나간다.
                    break;
                }
            }
                //SoundManager.Instance.FireSound()
        }

        // 미사일이 발사될때마다 미사일을 메모리풀로 돌려보내는 것을 체크한다.
        for (int i = 0;i < BoltMaxPool;i++)
        {
            // 만약 미사일[i]가 활성화 되어있다면
            if (BoltArray[i])
            {
                // 미사일[i]의 Collider2D가 비활성 되었다면
                if (BoltArray[i].GetComponent<Collider2D>().enabled == false)
                {
                    // 다시 Collider2D를 활성화 시키고
                    BoltArray[i].GetComponent <Collider2D>().enabled = true;
                    // 미사일을 메모리로 돌려보내고
                    MPool.RemoveItem(BoltArray[i]);
                    // 가리키는 배열의 해당 항목도 null(값 없음)로 만든다.
                    BoltArray[i] = null;
                }
            }
        }
    }


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

투사체 스크립트

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

public class CsPlayerBolt : MonoBehaviour
{
    public GameObject explosiveEffect;
    public float bulletSpeed;

    private void OnTriggerEnter2D(Collider2D collision)
    {	// 충돌한 물체의 태그가 Enemy일 경우
        if (collision.gameObject.tag == "Enemy")
        {
        	// 투사체의 Collider 제거
            this.gameObject.GetComponent<Collider2D>().enabled = false;
            // 폭발 이펙트 생성
            Instantiate(explosiveEffect, transform.position * 1.1f, transform.rotation);
        }
    }

    void Update()
    {
    	// 발사체 up 방향으로 실시간 이동
        transform.Translate(Vector2.up * bulletSpeed * Time.deltaTime);
    }
}

0개의 댓글