【问题标题】:Unity3D: How to do object pooling without a spawner singletonUnity3D:如何在没有 spawner 单例的情况下进行对象池
【发布时间】:2021-01-16 01:04:47
【问题描述】:

通常,如果你使用对象池,你会像this 视频中那样制作一个单例。 看了this 的视频后,我才发现单身狗有多乱。有没有其他方法可以在不使用单例的情况下进行对象池?我想改用事件。

【问题讨论】:

  • 您想在视频中解释一下object poolingsingletons 的关系吗? 我宁愿不用看17 分钟的视频内嵌广告
  • 不,只是想知道一种不使用单例的对象池方法。
  • 单例仅用于获取引用的一种懒惰且简单的方式...您可以使用任何其他方式获取引用,包括静态类或 ScriptableObject 等...但大多数那么设置它的时间要么很棘手,要么无论如何你都必须使用某种与单例类似的模式(我说的是例如FindObjectOfType,它基本上只是假设一个单例模式而没有实际实现单例)

标签: c# unity3d events singleton object-pooling


【解决方案1】:

您需要将池保存在一个非单例类中,并根据您的事件处理您的游戏对象池。 关于用事件调用它们,“我想使用事件”不是一个非常具体的问题。您需要将事件设置为监听(方法订阅)并在代码中调用它们应该发生的任何地方,这就是调用方法。我建议如果您对此不清楚,请尝试使用统一事件(Update 中的 OnTriggerEnter、if(Input.GetMouseButtonDown(0)) 等),直到您深入研究主题以理解它们并让您拥有 c# 事件或需要时UnityEvents

找到两个模板脚本,一个池和一个事件处理程序来处理场景中的对象。您可以在一个空场景中使用您各自的两个游戏对象来附加它们,以及您想要在池中的对象,分别按“空格”和“A”从池中创建并返回到池中。

泳池管理员:

using System.Collections.Generic;
using UnityEngine;

public class PoolManager : MonoBehaviour
{
    private Queue<GameObject> objPool;
    private Queue<GameObject> activeObj;
    private int poolSize = 10;
    public GameObject objPrefab;

    void Start()
    {
        //queues init
        objPool = new Queue<GameObject>();  
        activeObj = new Queue<GameObject>();
        //pool init
        for (int i = 0; i < poolSize; i++) 
        {
            GameObject newObj = Instantiate(objPrefab);
            objPool.Enqueue(newObj);   
            newObj.SetActive(false);    
        }
    }

    public GameObject GetRandomActiveGO() {
        GameObject lastActive = default;
        if (activeObj.Count > 0)
            lastActive = activeObj.Dequeue();
        else {
            Debug.LogError("Active object queue is empty");
        }
        return lastActive;
    }

    //get from pool
    public GameObject GetObjFromPool(Vector3 newPosition, Quaternion newRotation)
    {
        GameObject newObject = objPool.Dequeue();
        newObject.SetActive(true);
        newObject.transform.SetPositionAndRotation(newPosition, newRotation);

        //keep actives to be retrieved
        activeObj.Enqueue(newObject);
        return newObject;
    }

    //return to pool
    public void ReturnObjToPool(GameObject go)
    {
        go.SetActive(false);
        objPool.Enqueue(go);
    }
}

事件处理程序:

using UnityEngine;

public class EventHandler : MonoBehaviour
{
    public delegate GameObject OnSpacePressed(Vector3 newPosition, Quaternion newRotation);
    public OnSpacePressed onSpacePressed;

    public delegate void OnAKeyPressed(GameObject go);
    public OnAKeyPressed onAKeyPressed;

    public PoolManager poolManager;

    void Start()
    {
        onSpacePressed = poolManager.GetObjFromPool;
        onAKeyPressed = poolManager.ReturnObjToPool;
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            onSpacePressed?.Invoke(new Vector3(0, 0, 0), Quaternion.identity);
        }

        //here I get a random active, however this would be called in the specific objects remove circumstances, 
        //so you should have a reference to that specific gameobje when rerunrning it to the pool.
        if (Input.GetKeyDown(KeyCode.A))
        { 
            GameObject go = poolManager.GetRandomActiveGO();
            onAKeyPressed?.Invoke(go);
        }
    }
}

编辑:单例模式

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    protected static T _instance;
    public static T instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = GameObject.FindObjectOfType<T>();
                if (_instance == null)
                {
                    _instance = new GameObject(typeof(T).Name).AddComponent<T>();
                }

            }
            return _instance;
        }
    }
}

【讨论】:

  • 您好,感谢您的回答,但在此示例中,您仍然需要创建 PoolManager 的单个实例,它是一个单例。如果您有多个级别具有相同的对象要池化,您仍然必须为每个级别创建一个 PoolManager。有没有办法避免这种情况?比如有一个静态类?
  • 我不明白你的意思。在单例模式中,单例实际上是静态的。在我的答案中找到更新的单例模式的代码。我认为您的问题是由于考虑不发生god object 模式的问题,因此将您的逻辑划分为不同的池+以避免默认情况下统一提供的事件
  • 有没有办法在不创建游戏对象的情况下进行对象池化?
  • 统一单体行为在游戏对象中“生活”。你不能做MonoBehaviour myMonoBhvr = new MonoBehaviour()。单一行为的实例需要附加一个游戏对象。严格来说,如果您不需要 monobehaviout 类提供的任何方法,您可以避免从它继承,因此创建游戏对象。即使您需要一些单一行为方法,也有一些变通方法。但是,在轮询或您需要在游戏期间可用的任何功能的情况下,创建相应的经理游戏对象是有意义的。
  • 好的,非常感谢,我会接受你的回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-25
  • 1970-01-01
  • 1970-01-01
  • 2019-12-16
  • 1970-01-01
相关资源
最近更新 更多