这次虽然由于时间以及复杂度的问题,自己做游戏的笔记写了一部分就没写了。不过如果什么都不留下的话,想想也觉得不对,所以决定还是偶尔记一记比较必要点的问题吧(话说这好像是第一次写博客.....)。
在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);
}
}
就这样,关于宝箱的缓存就做好了。
运行游戏,效果如下:
基本上这个被实例化的物品很少了,除非特殊情况,总共也就实例化一两个,却可以在有需要的情况下重复利用。
其它的一些对象优化方式也差不多,这儿就不多赘述了。