Unity 对象池《TakeOver City》笔试级代码

引言:

以前写c++ IOCP服务器的投递对象回收池,大量的对所谓对象池概念的泛滥接触,随便找一个游戏demo就见到某某入门初学者贴出一个:该游戏具有对象池概念…之前在学一个以介绍委托事件监听的demo为主没有用对象池的2D项目,被一个自以为很了解对象池,且自己很能的zz给“说教了”:你还有很长的路要走…

本次游戏里面有两个需要用到对象池优化缓存的地方:
1.连线物体
2.小兵对象
下面以小兵对象为例

1.先贴一下使用的初级代码(某年某大厂笔试题),后面再展示一下,一个仅仅靠装模作样写出来的继承关系代码(c#抽象接口模板虚函数)

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

public class SoldierPool 
{
    public static SoldierPool _instance = new SoldierPool();

    public static SoldierPool Instance
    {
        get
        {
            if (_instance == null) _instance = new SoldierPool();

            return _instance;
        }
        
    }

    Queue<GameObject> pool = new Queue<GameObject>();
    //concurrennt_queue
    GameObject PoolRoot;

    //代替生成函数Instantiate,接口GameObject s = SoldierPool.Instance.GetSoilder(),然后改变一下Transform
    public GameObject GetSoilder()
    {
        GameObject result = null;

        if(pool.Count<=0)
        {
            GameObject prefab = Resources.Load<GameObject>("Prefabs/Cylinder");
            if (prefab != null)
            {
                result = GameObject.Instantiate(prefab);
                result.SetActive(false);
                pool.Enqueue(result);
            }
            else
            {
                Debug.LogError("load error Prefabs/Cylinder");
            }
        }
        else
        {
            result = pool.Dequeue();
           // Debug.Log("load one prefab from pool!");
        }

        result.SetActive(true);
 
        return result;

    }

    //代替Destroy函数,接口 SoliderPool.Instance.ResetSoldier(s)
    public void ResetSoldier(GameObject soldier)
    {
        soldier.SetActive(false);
        PoolRoot = GameObject.Find("PoolRoot");
        if(PoolRoot == null)
        {
            PoolRoot = new GameObject("PoolRoot");
        }
        soldier.transform.SetParent(PoolRoot.transform);
        pool.Enqueue(soldier);

       // Debug.Log("pool count:"+pool.Count);
    }
}

在代码片段里面的使用
1.代替Instantiate

 foreach (int i in Determinations)
        {
            Debug.Log(ID + "->" + i);

            //从对象池获得一个prefabs
            GameObject s = SoldierPool.Instance.GetSoilder();

            //保证了prefab兵种阵营和初生大楼保持一致
            s.GetComponent<Fighter>().faction = this.faction;

            //初始化了兵线位置,和from起始位置保持一致
            s.transform.position = transform.position - new Vector3(0, transform.position.y, 0) + new Vector3(0, 50, 0);

            //设置了兵线的起始和终点,在Fighter.cs中,将使用rigidbody调用velocity进行移动
            s.GetComponent<Fighter>().from = MapManager.instance.maptr[ID];
            s.GetComponent<Fighter>().to = MapManager.instance.maptr[i];
            s.GetComponent<Renderer>().material = MapManager.instance.lm[faction];

            if (this.type == skyscraperType.ATTACK) s.GetComponent<Fighter>().rate = 2.0f;//to = MapManager.instance.maptr[i];
        }

2.代替Destroy

  SoldierPool.Instance.ResetSoldier(this.gameObject);

下面是自我创作部分

算是一种尝试,c#接口,模板,抽象,虚函数,重新,set、get,单例…
但是比较偏形式主义,稍微有一点c#多态性,其实是想把本次游戏里面的两种对象池使用模板进行编码调用,泛型编程

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

public interface IPool
{
    void FindOrCreatePoolRoot();
}

public abstract class Pool<T> : IPool
{
    protected Queue<T> pool = new Queue<T>();

    protected GameObject PoolRoot;

    protected T obj;

    public Pool(T t)
    {
        obj = t;
        FindOrCreatePoolRoot();      
    }
    

    public void FindOrCreatePoolRoot()
    {
        PoolRoot = GameObject.Find("PoolRoot");

        if (PoolRoot == null)
            PoolRoot = new GameObject("PoolRoot");
    }

    public abstract T GetPrefab();

    public virtual void ResetPrefab(T obj)
    {
        pool.Enqueue(obj);
    }

    //单例类设计冗余 
    private static Pool<T> _instance ;

    public static Pool<T> Instance
    {
        get
        {
            return _instance;
        }

        set
        {
            
        }
    }

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

public sealed class Test_Pool:Pool<GameObject>
{
    public Test_Pool(GameObject t) : base(t)
    {
        base.obj = t;
        if (Instance == null) Instance = this;
    }

    public override GameObject GetPrefab()
    {
        if(pool.Count>0)
        return base.pool.Dequeue();
        else
        {
            GameObject res = null;
         
            if(obj!=null)
            {
                res = GameObject.Instantiate(obj);

                res.transform.SetParent(base.PoolRoot.transform);

                base.pool.Enqueue(res);
                return res;
            }
            else
            {
                Debug.LogError("load error :");
                return null;
            }       
        }

    }

    public override void ResetPrefab(GameObject obj)
    {
        obj.transform.SetParent(base.PoolRoot.transform);
        obj.gameObject.SetActive(false);
        base.ResetPrefab(obj);
    }

}
public class test_application : MonoBehaviour
{
    Test_Pool sp;
    Test_Pool st;
    
  private void  Awake()
    {
        GameObject t = Resources.Load<GameObject>("Prefabs/Cylinder");
        st = new Test_Pool(t);
        GameObject s = Resources.Load<GameObject>("Prefabs/line_Empty");
        sp = new Test_Pool(s);
    }

    void Update()
    {
        GameObject res = sp.GetPrefab();
        //Test_Pool.Instance.ResetPrefab(res);
        GameObject ts = st.GetPrefab();
    }

}

但是基类的单例设计其实是完全没有必要的,这样就完成了使用一个基类完成多个对象池的多态性实现
如果改写Test_Pool,把T设置为其他泛型比如:.cs,Transform等,就可以更好发挥c#的多态性!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity 中的对象池是一种资源管理技术,用于在游戏运行过程中高效地创建、管理和回收对象。对象池的主要目的是减少频繁创建和销毁对象带来的性能开销,尤其是在有大量短期使用对象(如小敌人、项目等)的情况下。 下面是使用 Unity 对象池的基本步骤: 1. 创建对象池:首先,你需要创建一个包含所需对象类型的新对象池。这通常是一个静态类或专用脚本,负责管理对象的生命周期。 ```csharp public class ObjectPool<T> where T : Component { private List<T> poolObjects; private Stack<T> availableObjects; // 初始化方法 public ObjectPool(int initialSize) { poolObjects = new List<T>(); for (int i = 0; i < initialSize; i++) { T obj = Instantiate<T>(); obj.SetActive(false); // 设置对象为非活动状态,直到需要时才激活 poolObjects.Add(obj); } availableObjects = new Stack<T>(poolObjects); } // 获取对象 public T BorrowObject() { if (availableObjects.Count > 0) { T obj = availableObjects.Pop(); obj.SetActive(true); return obj; } else { T obj = Instantiate<T>(); return obj; } } // 归还对象 public void ReturnObject(T obj) { obj.SetActive(false); availableObjects.Push(obj); } } ``` 2. 使用对象池:当你需要一个新对象时,从池中借用一个,用完后记得归还。这样,当对象不再被使用时,它会被放回池而不是直接销毁,以便后续其他地方可能需要它。 ```csharp private ObjectPool<MyObject> objectPool; void Start() { objectPool = new ObjectPool<MyObject>(10); } void Update() { MyObject newObj = objectPool.BorrowObject(); // 使用 newObj ... newObj.gameObject.SetActive(false); // 当对象不再需要时,归还给池子 // 如果对象池已满,考虑创建更多对象 if (objectPool.availableObjects.Count == 0 && poolSize < MaxPoolSize) { // 添加新对象到池中 objectPool.poolObjects.Add(Instantiate<MyObject>()); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值