unity3d性能优化(tolua篇)
(2017-12-20 21:29:25)分类: unity |
tolua篇:
public class LuaState :
LuaStatePtr, IDisposable
{
public ObjectTranslator translator = new
ObjectTranslator();
public LuaReflection reflection = new
LuaReflection();
public int ArrayMetatable { get; private set;
}
public int DelegateMetatable { get; private set;
}
public int TypeMetatable { get; private set;
}
public int EnumMetatable { get; private set;
}
public int IterMetatable { get; private set; }
public int EventMetatable { get; private set;
}
//function ref
public int PackBounds { get; private set;
}
public int UnpackBounds { get; private set;
}
public int PackRay { get; private set; }
public int UnpackRay { get; private set; }
public int PackRaycastHit { get; private set; }
public int PackTouch { get; private set; }
public class
ObjectTranslator
{
private class DelayGC
{
public
DelayGC(int id, UnityEngine.Object obj, float time)
{
this.id = id;
this.time = time;
this.obj = obj;
}
public int
id;
public
UnityEngine.Object obj;
public
float time;
}
private class CompareObject :
IEqualityComparer
{
public new
bool Equals(object x, object y)
{
return
object.ReferenceEquals(x, y);
}
public int
GetHashCode(object obj)
{
return
RuntimeHelpers.GetHashCode(obj);
}
}
public bool LogGC { get; set; }
public readonly Dictionary
public readonly LuaObjectPool objects = new
LuaObjectPool();
private List gcList = new List();
private static ObjectTranslator _translator =
null;
public ObjectTranslator()
{
LogGC =
false;
_translator = this;
}
public int AddObject(object obj)
{
int index
= objects.Add(obj);
if
(!TypeChecker.IsValueType(obj.GetType()))
{
objectsBackMap[obj] =
index;
}
return
index;
}
public static ObjectTranslator Get(IntPtr
L)
{
return _translator;
return
LuaState.GetTranslator(L);
}
//fixed 枚举唯一性问题(对象唯一,没有实现__eq操作符)
void RemoveObject(object o, int udata)
{
int index
= -1;
if
(objectsBackMap.TryGetValue(o, out index) && index ==
udata)
{
objectsBackMap.Remove(o);
}
}
//lua gc一个对象(lua 库不再引用,但不代表c#没使用)
public void RemoveObject(int udata)
{
//只有lua
gc才能移除
object o =
objects.Remove(udata);
if (o !=
null)
{
if
(!TypeChecker.IsValueType(o.GetType()))
{
RemoveObject(o, udata);
}
if (LogGC)
{
Debugger.Log("gc object {0}, id {1}", o,
udata);
}
}
}
public object GetObject(int udata)
{
return
objects.TryGetValue(udata);
}
//预删除,但不移除一个lua对象(移除id只能由gc完成)
public void Destroy(int udata)
{
object o =
objects.Destroy(udata);
if (o !=
null)
{
if
(!TypeChecker.IsValueType(o.GetType()))
{
RemoveObject(o, udata);
}
if (LogGC)
{
Debugger.Log("destroy object {0}, id {1}", o,
udata);
}
}
}
//Unity Object 延迟删除
public void DelayDestroy(int id, float
time)
{
UnityEngine.Object obj = (UnityEngine.Object)GetObject(id);
if (obj !=
null)
{
gcList.Add(new DelayGC(id,
obj, time));
}
}
public bool Getudata(object o, out int
index)
{
index =
-1;
return
objectsBackMap.TryGetValue(o, out index);
}
public void Destroyudata(int udata)
{
objects.Destroy(udata);
}
public void SetBack(int index, object o)
{
objects.Replace(index, o);
}
bool RemoveFromGCList(int id)
{
int index
= gcList.FindIndex((p) => { return p.id == id; });
if (index
>= 0)
{
gcList.RemoveAt(index);
return true;
}
return
false;
}
//延迟删除处理
void DestroyUnityObject(int udata,
UnityEngine.Object obj)
{
object o =
objects.TryGetValue(udata);
if
(object.ReferenceEquals(o, obj))
{
RemoveObject(o, udata);
//一定不能Remove,
因为GC还可能再来一次
objects.Destroy(udata);
if (LogGC)
{
Debugger.Log("destroy object {0}, id {1}", o,
udata);
}
}
UnityEngine.Object.Destroy(obj);
}
public void Collect()
{
if
(gcList.Count == 0)
{
return;
}
float
delta = Time.deltaTime;
for (int i
= gcList.Count - 1; i >= 0; i--)
{
float time = gcList[i].time -
delta;
if (time <= 0)
{
DestroyUnityObject(gcList[i].id, gcList[i].obj);
gcList.RemoveAt(i);
}
else
{
gcList[i].time = time;
}
}
}
public void Dispose()
{
objectsBackMap.Clear();
objects.Clear();
_translator = null;
}
}
///
/// Lua文件的路径 //
///
public string luaFilePath;
///
/// 对应的LuaTable //
///
[HideInInspector]
public LuaTable luaTable;
[HideInInspector]
public LuaFunction
luaFunction_Awake;
public LuaTable GetLuaComponent()
{
return
luaTable;
}
void Awake()
{
luaTable =
LuaState.Require(luaFilePath);
luaFunction_Awake = luaTable.GetLuaFunction("Awake");
CallLuaFunction(luaFunction_Awake)
}
void OnDestroy()
{
ClearLua();
}
///
/// 彻底清理lua
///
protected void ClearLua()
{
if
(luaFunction_Awake != null)
{
luaFunction_Awake.Dispose();
luaFunction_Awake =
null;
}
if
(luaTable != null)
{
luaTable.Dispose();
luaTable = null;
}
}
public void CallLuaFunction(LuaFunction
luaFunction_Temp)
{
if(luaFunction_Temp != null)
{
luaFunction_Temp.BeginPCall();
luaFunction_Temp.Push(luaTable);
luaFunction_Temp.PCall();
luaFunction_Temp.EndPCall();
}
}
self.
btnHandler = function() end
self.btnMap [btn] = btnHandle
btn.onClick:AddListener(btnHandle)
for btn,listener in pairs(self.btnMap)
do
btn.onClick:RemoveListener(listener)
end
我们目前开发采用的是tolua,大家可能都知道c#到lua是需要导出接口给lua调用的,只要lua获取unity的对象,那么tolua都会根据id(唯一)保存在
LuaState中的 ObjectTranslator 中,每个lua变量都是唯一对应的,参考图
#define MISS_WARNING
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Text;
using UnityEngine;
namespace LuaInterface
{
具体可以参考tolua中的类ObjectTranslator.cs,
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace LuaInterface
{
#if !MULTI_STATE
#endif
#if !MULTI_STATE
#endif
#if !MULTI_STATE
#else
#endif
#if !MULTI_STATE
#endif
}
那么问题来了,当对应的c#对象被释放的时候,如果lua中还有引用,那么c#对象将释放不掉。目前大部分项目做法应该是用c#驱动lua,举一个简单的例子:
using UnityEngine;
using UnityEngine;
using
System.Collections;
using
System.Collections.Generic;
using
LuaInterface;
public class LuaComponent :
MonoBehaviour
{
}
如果这个脚本挂在一个物体gameobject上,那么上面挂在的lua对象就会被创建,c#中可以看出引用了lua文件中的function,当gameobject被destroy以后,如果这里面的lua对象没有被释放的话,那么lua对象中如果引用了gameObject对象的话,那么gameobject就不会被完全从内存中释放,所以在写框架的时候一定要考虑释放。
下面要说的就是button这一类的监听事件,eg:self.btn_login.onClick:AddListener(function()
self:DoSomething()
end)了解lua闭包的可能都知道,一旦这个临时function不能被释放,那么可以知道里面的self这个lua对象也将不会被释放,进而这个lua对象中引用的unity对象,比如上面提到的button这个gameObject也将不会被释放的,所以建议这样:
self.btn_login.onClick:AddListener(self.btnHandler)
当物体被销毁的时候调用self.btn_login.onClick:RemoveListener(self.
btnHandler )做后期清理(建议写框架的人写一个统一的方法封装一下)eg:
local Test = {}
function
Test:AddListener(btn,btnHandle)
end
function Test:ClearBtnListener(
)
end
所以这一点在写框架的时候一定要注意,还有一点就是,c#和lua之间的交互,大家知道lua和c#只要交互就会有gc(gc的话c#会遍历所有object,并且清理掉不被引用的object)发生,严重的情况会导致游戏很卡的,开发过程中,个人感觉交互比较多的就是加载资源和网络通信。我之前的做法就是每次发协议或者加载资源都传一个luafunction作为回调,这样的话,每次资源加载完成,都会有luafunction造成的gc,可以注册一个luafunction作为统一的回调,每次资源加载完成只需要统一调用回调即可,所有资源加载的对应关系可以放在lua层处理,这样可以减少很多gc,网络协议亦是如此。总之遵守一个原则,少交互,少gc。
还有一种情况就是在lua中一旦引用unity的object,可以做缓存,省的每次都屌用Find去查找,这也是另外一个地方消耗的地方,大家在使用的时候也是要特别注意的。在做优化的时候,有同事提到要不要把require的lua文件也及时的unload掉,大家都知道lua的require只会执行一次读操写作,然后就常驻内存,这个也是要看文件大小,比如一个配置文件太大,那么可以考虑在不使用的时候及时的unload,其他的逻辑代码,我个人建议不需要每次都清理,这样反而会带来很多gc,会一定程度上影响性能的,所以个人感觉没必要。
前一篇:lua检测没有赋值的全局变量
后一篇:unity3d性能优化(资源篇)