Unity3D中通过缓存对象对性能进行优化,


这次虽然由于时间以及复杂度的问题,自己做游戏的笔记写了一部分就没写了。不过如果什么都不留下的话,想想也觉得不对,所以决定还是偶尔记一记比较必要点的问题吧(话说这好像是第一次写博客.....)。

在Unity3D中,据说是用Instantiate实例化一个物体的话,将会异常消耗性能,如果在游戏过程中,经常使用到这个方式创建对象,那么将对性能造成很大的影响。

我查看了下自己的代码,对于实现方式,有几个地方貌似都是这样用的。。。比如实例化NPC、实例化在游戏世界中的物品实体,还比如NPC头顶的血条、名字等等。为此,必须得改造下了,毕竟或许一个两个地方倒没什么,如果数量达到一定程度,那对性能的影响就比较大了。

这儿我先新建一个名为“ObjTempManager”的脚本,用于存放所有的缓存对象的实例。这儿先慢慢来,最开始我就只定义一个用于存放物品实体的List,代码如下:

//**********************************************************

//Copyright (c) 2014 wangjiaying. All Rights Reserved.

//类名:ObjTempManager.cs

//UnityVersion:4.5.4f1

//作者:CWH

//日期:

//联系方式:cwhisme@gmail.com

//cwhisme@qq.com

//功能:

//**********************************************************

using UnityEngine;

using System.Collections;

using System.Collections.Generic;

/// <summary>

/// 用于存放游戏中所有的需要缓存对象

/// 避免过多的Instaniate、Destroy等等,优化游戏性能

/// </summary>

public class ObjTempManager : MonoBehaviour {

 

    private List<GameObject> m_itemEntityList = new List<GameObject>();//在游戏世界中的物品的缓存list   

}

因为我将游戏世界中所有的物品实例都统一表现为一个相同的模型,决定于它们不同的只是其上存放的一个“Item”对象而已。(如果每个物品都是使用符合该物品特征的不同的模型,其非常花费时间的,现在毕业之际,最差的就是时间了,为此我还是决定使用同样的模型),就是一个宝箱的样子:


决定其内在的,主要是其上的“ItemInstanceEntity”脚本,这这个脚本暂时无关,就先不提。
我原本使用的方式是:
当玩家或者NPC触碰到这个宝箱时,玩家或者NPC就会获得该物品,而这个用于存放物品对象的宝箱实体也就会随之“Destroy”掉。
不过,如果窒息想一想的话,玩家杀死敌人大多情况下都会掉落物品,以及场景中随时可能放置的一些“彩蛋(隐藏物品)”都是属于这个模型,如果一直Instantiate然后Destroy,这个可不是一个小的消耗!
原本我再“ItemManager”类中,对外提供实例化宝箱的时候使用的方法是这样的:

    /// <summary>

    /// 在游戏世界中实例化一个物品的实体

    /// </summary>

    /// <param name="position">位置</param>

    /// <param name="item">物品</param>

    public static void ItemInstanceAEntity(Vector3 position, ItemCore item)

    {

        GameObject obj = Instantiate(ObjManager.GetInstance.GetItemEntity, position, Quaternion.Euler(0,Random.Range(0,360),0)) as GameObject;

        obj.GetComponent<ItemInstanceEntity>().SetItem = item;

        obj.transform.Translate(Vector3.forward,Space.Self);

    } 

它通过实例化一个宝箱模型,然后再设置改模型身上的“ItemInstanceEntity”脚本。

这儿稍稍变通一下,方法名之类的可以不变,只需要将这个方法中实例化模型的操作,改为向适才创建的“ObjTempManager ”中获取相应的模型即可,如果模型不够,再由“ObjTempManager ”负责创建,缓存即可。

而作为缓存游戏对象的这个“ObjTempManager ”,应当是一个单例类,以使得该类全局可用,按照惯例,首先定义单例类的方法:

    private static ObjTempManager m_instance = null;

    public static ObjTempManager GetInstance

    {

        get

        {

            if (m_instance == null)

            {

                GameObject obj = new GameObject();

                obj.name = "_TempManager";

                DontDestroyOnLoad(obj);

                m_instance = obj.AddComponent<ObjTempManager>();

            }

            return m_instance;

        }

    }

接着,可以定义个方法,这个方法的主要作用是:返回缓存中可用的对象,如果缓存的所有对象都不可用,那么才创建一个对象返回。

代码如下:

    /// <summary>

    /// 在缓存列表中获取一个游戏世界中的物品实体(如无,将创建该物体)

    /// </summary>

    public ItemInstanceEntity GetItemEntityModel

    {

        get

        {

            for (int i = 0; i < m_itemEntityList.Count; i++)

            {

                if (m_itemEntityList[i].gameObject.activeSelf)//如果该对象是激活的,那么表示该对象是在使用中的,那么跳过

                {

                    print(m_itemEntityList[i].gameObject.activeSelf);

                    continue;

                }

                m_itemEntityList[i].SetActive(true);//激活

                return m_itemEntityList[i].GetComponent<ItemInstanceEntity>();//返回对象

            }

            //如果代码能执行到这儿的话,就说明当前所有的对象都在使用,那么就重新创建一个吧

            GameObject obj = Instantiate(ObjManager.GetInstance.GetItemEntity) as GameObject;

            obj.transform.parent = m_instance.transform;//将该对象设置为自身子物体

            m_itemEntityList.Add(obj);//添加对象至缓存

            return obj.GetComponent<ItemInstanceEntity>();//创建一个物品实体,并返回

        }

    }

这样的话,只要战斗不是太过于激烈,一般被实例化的就几个物体而已。

然后修改下上文中提到的“ItemManager”中对外提供创建宝箱的方法“ItemInstanceAEntity”:

    /// <summary>

    /// 在游戏世界中实例化一个物品的实体

    /// </summary>

    /// <param name="position">位置</param>

    /// <param name="item">物品</param>

    public static void ItemInstanceAEntity(Vector3 position, ItemCore item)

    {

        //GameObject obj = Instantiate(ObjManager.GetInstance.GetItemEntity, position, Quaternion.Euler(0,Random.Range(0,360),0)) as GameObject;

        ItemInstanceEntity it = ObjTempManager.GetInstance.GetItemEntityModel;

        //obj.GetComponent<ItemInstanceEntity>().SetItem = item;

        it.SetItem = item;

        it.transform.position = position;//设置坐标

        it.transform.Rotate(0,Random.Range(0,360),0);//随机旋转移动,造成随机的位置

        it.transform.Translate(Vector3.forward,Space.Self);

    } 

注释掉原本的那些直接Instantiate创建宝箱的代码,改为直接向“ObjTempManager”类中获取,然后操作。

最后,还有一个需要注意的地方就是宝箱本身的那个类“ItemInstanceEntity”,里边原本的代码就是直接摧毁自身,现在也应该改一改,变成将自身Active属性设置为false即可:

主要代码如下:

    void OnTriggerEnter(Collider other)

    {

        if (item == null)

        {

            gameObject.SetActive(false);

            return;

        }  

       if (ItemManager.AddItem(other.GetComponent<AICreature>(), item))//如果玩家成功获得物品,就禁用该物品

            {

                gameObject.SetActive(false);

            }

    }

就这样,关于宝箱的缓存就做好了。

运行游戏,效果如下:


基本上这个被实例化的物品很少了,除非特殊情况,总共也就实例化一两个,却可以在有需要的情况下重复利用。

其它的一些对象优化方式也差不多,这儿就不多赘述了。










相关内容

    暂无相关文章