스파르타 게임개발종합반(Unity)/TIL - 본캠프 매일 공부 기록

2024.06.25 TIL - Photon에서 오브젝트풀링 사용하기

테크러너 2024. 6. 25.

Photon Pun2 사용과 더불어 오브젝트 풀링 사용

using Photon.Pun;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;

[System.Serializable]
public class Pool
{
    public GameObject Prefab;
    public int MinSize;
    public int MaxSize;
}

[RequireComponent(typeof(PhotonView))]
public class ObjectPoolManager : Singleton<ObjectPoolManager>
{
    public List<Pool> Pools;
    private Dictionary<string, ObjectPool<GameObject>> poolDic;

    protected override void Awake()
    {
        base.Awake();
        poolDic = new Dictionary<string, ObjectPool<GameObject>>();
        foreach (var pool in Pools)
        {
            ObjectPool<GameObject> objectPool = new ObjectPool<GameObject>(
                createFunc: () =>
                {
                    GameObject newObj = PhotonNetwork.Instantiate("Items/"+pool.Prefab.name, Vector3.zero, Quaternion.identity);
                    newObj.SetActive(false);
                    return newObj;
                },
                actionOnGet: (obj) => obj.SetActive(true),
                actionOnRelease: (obj) => obj.SetActive(false),
                actionOnDestroy: (obj) => PhotonNetwork.Destroy(obj),
                collectionCheck: false,
                defaultCapacity: pool.MinSize,
                maxSize: pool.MaxSize
            );
            poolDic.Add(pool.Prefab.name, objectPool);
        }
    }

    public GameObject GetObject(string rcode)
    {
        if (poolDic.ContainsKey(rcode))
        {
            return poolDic[rcode].Get();
        }
        Debug.LogWarning("No pool with prefab name: " + rcode);
        return null;
    }

    public void ReleaseObject(string rcode, GameObject obj)
    {
        if (poolDic.ContainsKey(rcode) && obj != null)
        {
            poolDic[rcode].Release(obj);
        }
        else
        {
            Debug.LogWarning("No pool with prefab name: " + rcode);
        }
    }
}

기존의 오브젝트풀링에서 `Photon Pun2` 서버를 사용하게 되니까 코드 수정이 약간 필요했다.

 

1. PhotonView 컴포넌트 추가

[RequireComponent(typeof(PhotonView))]

오브젝트가 모두의 눈에 보이게 동기화하려면 `PhotonView` 컴포넌트를 부착해야한다.

 

2. 오브젝트 생성

GameObject newObj = PhotonNetwork.Instantiate("Items/"+pool.Prefab.name, Vector3.zero, Quaternion.identity);

오브젝트를 생성할 때는 그냥 `Instantiate`보다는 `PhotonNetwork.Instantiate`를 사용해야 한다.

주의할 점은 그냥 `Instantiate`는 매개변수에 프리팹을 넣었지만, `PhotonNetwork.Instantiate`는 프리팹의 이름을 넣어야한다.

 

3. 오브젝트 파괴

PhotonNetwork.Destroy(obj)

오브젝트를 파괴할 때도 `PhotonNetwork`로!

 

 

Photon & 오브젝트 풀링 & 아이템 스폰 & 아이템 줍기 의 복합적인 문제

보통 포톤 서버를 사용할 때 아이템 스폰은 오브젝트를 `PhotonNetwork.Instantiate`로 생성하고, `PhotonNetwork.Destroy`로 파괴한다.

그런데 아이템이 점차 많아질 수록 최적화가 필요했기 때문에 오브젝트 풀링을 사용했다.

다만 오브젝트 풀링은 파괴가 아니라 `SetActive` `True` `False`로 관리를 하다보니 문제가 생겼다.

 

`PhotonNetwork.Instantiate`과 `PhotonNetwork.Destroy`를 사용하면 모든 클라이언트들 눈에 아이템이 생기고 사라지는 것이 동기화가 잘 된다.

그런데 `SetActive` `True` `False`는 동기화를 직접 설정해줘야 한다.

 

그래서 아래와 같은 흐름으로 코드를 작성하여 문제를 해결했다.

 

1. 아이템 주울 때 모든 클라이언트에게 알려주기

photonView.RPC("OnPickedUp", RpcTarget.All);

 

2. 아이템 스폰할 때 모든 클라이언트에게 알려주기

    [PunRPC]
    public void OnPickedUp()
    {
        ObjectPoolManager.Instance.ReleaseObject(Item.Rcode, gameObject);
        Spawner.photonView.RPC("SpawnItem", RpcTarget.All);
    }
    [PunRPC]
    public void SpawnItem()
    {
        if (!PhotonNetwork.IsMasterClient)
            return;
        Vector3 spawnPos = ReturnRandPos();
        Pool pool = ReturnRandPool();

        if (pool != null)
        {
            string poolName = pool.Prefab.name;
            GameObject spawnedObject = ObjectPoolManager.Instance.GetObject(poolName);
            if (spawnedObject != null)
            {
                spawnedObject.transform.position = spawnPos;
                var itemPickUp = spawnedObject.GetComponent<ItemPickUp>();
                itemPickUp.Spawner = this;
            }
            else
            {
                Debug.LogWarning("Failed to get object from pool: " + poolName);
            }
        }
    }

 

3. 아이템 스폰은 마스터 클라이언트가 해주기

        if (!PhotonNetwork.IsMasterClient)
            return;

스폰을 해줄 때 마스터 클라이언트인지 체크를 해준다.

반응형

댓글