Unity 对象池

您所在的位置:网站首页 unity获取子物体并调用 Unity 对象池

Unity 对象池

2023-03-20 17:48| 来源: 网络整理| 查看: 265

毕设也总算做到对象池部分了,希望能做完,悲。

先说一下对象池是干什么的好了,就是我们集中创建一大堆需要复用物体的管理对象。在每次用的时候都从这个对象里面拿,而不是每次都去实例化和销毁新的物体

比如我们玩家就是钓鱼佬,每次钓完鱼都需要把鱼丢回去,下次掉的还是这条鱼,而不是把这条🐟直接做掉,买新的丢进去给自己掉。

而丢🐟和钓🐟这个步骤在Unity中可以通过gameobjet.SetActive(false)gameobjet.SetActive(true)来实现,即将物体隐藏和出现

好,理论完毕那怎么创建对象池呢;

下面是我对对象池的需求

1.由于我做的游戏很小那么我只需要一个对象池,所以我的对象池继承自单例;

2.由于我的怪物也很少,所以需要通过改变怪物的颜色来代表不同怪物,所以用了组件来代替gameobject

3.由于是rougelike游戏,所以一般一个房间的怪物不会超过对象池的上限,所以这里没有用数组而是用队列来完成

单例:

using UnityEngine; public abstract class SingletonMonobehaviour : MonoBehaviour where T : MonoBehaviour { private static T instance; public static T Instance { get { return instance; } } protected virtual void Awake() { if (instance == null) { instance = this as T; } else { Destroy(gameObject); } } }

2.第一件事我们需要一个字典和一个结构体,同时我们需要我们的对象池不需要重复的脚本挂接

[DisallowMultipleComponent] public class PoolManager : SingletonMonobehaviour { //对象池数组 [SerializeField] private pool[] poolArray = null; private Transform objectPoolTransform; //对象池根节点,也就是我们同一组对象的父节点位置 private Dictionary poolDictionary = new Dictionary(); [Serializable] public struct pool { public int PoolSize; public GameObject prefab; public string componentType; }

然后我们脚本就是这个样子了

3.对象池需要有以下这些操作函数

3.1在游戏开始时将我所需要的物体加入对象池并生成,同时这些物体都是隐藏的

3.2 调用对象池中的物体

3.3在对象池中的物体在调用时需要重置他的位置

好,上代码,第一步调用CreatePool();//在游戏开始时生成对象池

private void Start() { objectPoolTransform = this.gameObject.transform; for(int i = 0; i < poolArray.Length; i++) { CreatePool(poolArray[i].prefab, poolArray[i].PoolSize, poolArray[i].componentType); } }

下为CreatePool()的具体实现

这里字典的Key值为每个物体创建时unity自带的Id

private void CreatePool(GameObject prefab, int poolSize, string componentType) { //字典Key值为Unity物体创建时自带的Id int poolKey=prefab.GetInstanceID(); //给物体的父亲上名字 string prefabName=prefab.name; GameObject parentGameObject = new GameObject(prefabName + "Anchor"); parentGameObject.transform.SetParent(objectPoolTransform); //如果物体还没加入到字典中 if (!poolDictionary.ContainsKey(poolKey)) { //在字典中加入所需要挂接的脚本(其实也就是物体) poolDictionary.Add(poolKey, new Queue()); //生成物体 for(int i = 0; i < poolSize; i++) { GameObject newgameObject=Instantiate(prefab,parentGameObject.transform); //将物体设为隐藏 newgameObject.SetActive(false); //从新生成的物体中获取我们需要的脚本加入到队列中 poolDictionary[poolKey].Enqueue(newgameObject.GetComponent(Type.GetType(componentType))); } } }

第二步:从对象池中调用脚本ReuseComponent();

public Component ReuseComponent(GameObject prefab,Vector3 position,Quaternion rotation) { int poolKey = prefab.GetInstanceID(); //如果需要用到的物体已经存在 if (poolDictionary.ContainsKey(poolKey)) { Component componentToReuse = GetComponentFromPool(poolKey); //重新设置这个物体的位置逻辑 ResetObject(prefab,position, rotation, componentToReuse); return componentToReuse; } else { return null; } } private Component GetComponentFromPool(int poolKey) { //将这个物体从队列删除,并重新从队列尾加入 Component componentToReuse = poolDictionary[poolKey].Dequeue(); poolDictionary[poolKey].Enqueue(componentToReuse); //将这个物体设置为隐藏状态 if (componentToReuse.gameObject.activeSelf == true) { componentToReuse.gameObject.SetActive(false); } return componentToReuse; } private void ResetObject(GameObject prefab, Vector3 position, Quaternion rotation, Component componentToReuse) { componentToReuse.transform.position = position; componentToReuse.transform.rotation = rotation; componentToReuse.gameObject.transform.localScale = prefab.transform.localScale; } }

到这一步为止我们的对象池管理就已经彻底做完了

那么下一步就是做敌人的脚本组件,这里需要包含两个数据分别是

public class EmenyAntion : MonoBehaviour { Animator animator; SpriteRenderer spriteRenderer; // Start is called before the first frame update void Awake() { animator = GetComponent(); spriteRenderer = GetComponent(); } public void SetAnimation(RuntimeAnimatorController animatorController,Color spriteColor) { animator.runtimeAnimatorController = animatorController; spriteRenderer.color = spriteColor; } }

在下一步就是写一个敌人自己的生成器:

整体逻辑很简单基本只需要看GetEmentExample就行  

public class EnemyPoolTest : MonoBehaviour { [SerializeField] private EnemyAnimationDetails[] enemyAnimationDetailsl; [SerializeField] GameObject enemyprefabs; private float timer = 1f; [Serializable] public struct EnemyAnimationDetails { public RuntimeAnimatorController controller; public Color spriteColor; } //因为暂时还没写好逻辑,只是测试,就每1s生成一个 private void Update() { timer-=Time.deltaTime; if (timer < 0f) { GetEmentExample(); timer = 1f; } } private void GetEmentExample() { //一二句可以不用管,基本就是随机获得我预设的怪物出生地,直接给改成位置就行 Room room = GameManager.Instance.GetCurrentRoom(); Vector3 spawnPosition=new Vector3(Random.Range(room.lowerBounds.x, room.upperBounds.x),Random.Range(room.lowerBounds.y,room.upperBounds.y),0f); //这一句就是调用对象池中的对象,赋予需要的预制体,位置,和旋转,旋转直接归零就行 EmenyAntion emenyAntion = (EmenyAntion)PoolManager.Instance.ReuseComponent(enemyprefabs, HelperUtilities.GetSpawanPositionNearestToPlayer(spawnPosition), Quaternion.identity); //这句话主要就是看我预设了多少种怪物模式 int randomIndex = Random.Range(0, enemyAnimationDetailsl.Length); //生成怪物 emenyAntion.gameObject.SetActive(true); //赋予动画控制器和颜色, emenyAntion.SetAnimation(enemyAnimationDetailsl[randomIndex].controller, enemyAnimationDetailsl[randomIndex].spriteColor); } }

到这一步,其实整个就完成了

,下图为效果 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3