找回密码
 立即注册
查看: 211|回复: 0

Unity性能优化基础篇——Lua和Unity调用的优化

[复制链接]
发表于 2021-12-21 21:17 | 显示全部楼层 |阅读模式
这篇主要讲Lua+Unity互相调用时一些注意事项
先说gameobj.transform.position = pos
像gameobj.transform.position = pos这样的写法,在Unity中很常见。但是在Lua中,大量使用这种写法是非常糟糕,特别是循环去调用的时候。
在Lua中引用C#的Object,代价昂贵,对于lua中需要设置gameobject的postion的时候,我们可以传递三个int参数给C#,比如在C#中定义这样一个方法,然后去访问调用LuaUtil.SetPos(obj, pos.x, pos.y, pos.z),这样的性能会好非常多,因为省掉了transform的频繁返回,而且还避免了transform经常临时返回引起Lua的GC
class LuaUtil{
   static void SetPos(GameObject obj, float x, float y, float z)
   {
       obj.transform.position = new Vector3(x, y, z);
   }
}
GetComponent也是同样的道理,尽量在C#中去实现GetComponent,lua中去调用C#的方法
    public static Component SafeGetComponent(this GameObject go, string path, string componentType)
    {
        if (go == null ||
            string.IsNullOrEmpty(componentType))
            return null;

        Transform findTrans = go.transform;
        if (string.IsNullOrEmpty(path) == false)
            findTrans = go.transform.Find(path);
        if (findTrans == null)
            return null;

        return findTrans.GetComponent(componentType);
    }
还有Find方法也放到C#代码中
Lua中全局变量的危害
1. 在程序开发过程中,Lua语言中的全局变量不需要声明就可以使用,因此随着功能模块的增多,全局变量就会变得非常不稳定,稍有不慎便会重定义覆盖全局变量,产生各种不易查找的灾难性Bug。
2. 对于不是局部变量的访问,Lua会重定向到全局环境表_G。而局部变量始终具有优先访问权,因此如果一个全局变量和一个局部变量同名且同时存在,那么访问该变量时,得到的始终是局部变量的值。
3. 如果没有人为设置,Lua中通过模拟得到的全局变量,均会被_G表引用。但如果该全局变量引用了Unity中的Object对象,就会导致Unity中的Object对象被Destroy之后,_G表中的全局变量依然引用着该Object对象,引发泄露问题
情况一:Lua对象是全局变量,直接放在_G中
举例:
button = GameObject.Find("LoginButton")
情况二:Lua对象被一些全局的Table引用。
我们每个UI面板都对应MVC结构,用了面向对象的概念。其中view在面板关闭时会直接置空,但Ctrl和Model都不会,它们都放在一个全局的管理类(Table)。当Model中持有了面板上的对象时,会出现对象销毁了,但Model中的变量不为空的情况。
举例:
-- login 对象放在全局持有的UI对象管理器中
-- UI面板使用mvc结构,在UI销毁时,login的view字段会被赋值为空,而ctrl,model不会。
login.model.button = GameObject.Find("LoginButton")应对方法:
将持有C#对象的变量,定义在会赋值为空的对象中,可以将示例中的代码改为:
login.view.button = GameObject.Find("LoginButton")情况三:Lua对象的function字段被赋值给了C#的事件/委托。
比如UI控件的按钮点击事件。在LuaGC时,发现C#对象对其有引用,GC不掉。导致Lua中的对象通过Tolua引用住了C#对象。
举例:
--UGUI的Button组件提供了onClick事件
login.view.loginButton = GameObject:Find("LoginButton"):GetComponent("UntiyEngine.UI.Button")
login.view.onLoginButtonClicked = function()
-- 处理loginButton点击后的逻辑
end
login.view.loginButton.onClick:AddListener(login.view.onLoginButtonClicked)应对方法:
(1)对于每一个提供给Lua注册事件/委托的C#类,都继承一个IClear接口,该接口内实现清理事件/委托。
(2)在MonoBehavior的OnDestroy函数内,调用IClear的接口。但要注意的是,这并不能保证所有的组件都是清理完毕,因为deactvie状态的组件,是不会触发OnDestroy的。因此需要手动的调用清理。
(3)提供一个清理GameObject Lua事件/委托的接口,该接口会找到GameObject上所有继承于IClear接口的类,执行清理操作。需要手动清理的GameObject都需要调用该函数。
void ClearGameObject(UnityEngine.GameObject target)
{
    if(target == null) return;
    var list = target.GetComponentsInChildren<IClear>(true);
    foreach(var component in list)
    {
        component.Clear();
    }
}
(4)提供一个新的Destroy函数全局替换Unity原生的销毁GameObject接口。该函数在做真正销毁前,通过(3)清理所有注册的事件/委托。
参考链接:游戏开发:Unity中Lua造成的堆内存泄露问题

最后一下Xlua的一个坑点




经过试验将table = nil或者Image = nil,这个对象才会被释放掉
如果只LuaTable.Dispose(),C#那边释放掉了,但是lua的table没有释放
这一块还是有坑的,如果你的table引用了C#的对象,关闭UI时还得手动nil一下,只调用LuaTable.Dispose()是释放不掉的
分享2个Lua的性能检测工具:用Unity+Lua开发游戏,有什么好的办法进行性能检测?
关于 Lua 内存泄漏的检测 - yaukey - 博客园

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-6-1 20:38 , Processed in 0.217888 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表