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

ToLua使用

[复制链接]
发表于 2022-1-20 13:41 | 显示全部楼层 |阅读模式
阅读配合目录来读



下载地址
https://github.com/jarjin/LuaFramework_NGUI
https://github.com/jarjin/LuaFramework_UGUI
环境搭建

(1) 生成Wrap类

打开这个工程,生成注册文件:



这一步将Unity常用的C#类生成Warp类并注册到lua虚拟机中,这样在lua中就可以调用这些C#类了
这一步等效于,在Unity中的



完成这一步后,在Assets/LuaFramework/ToLua/Source/Generate目录看到许多Wrap类,
主要还是在CustomSetting.cs中生成的类



同理:增加自己的C#类也是这样
(2) 生成Lua Delegates和LuaBinder



生成的Warp类需要要LuaBinder中注册到lua虚拟机中,生成的lua委托需要在DelegateFactory中注册到lua虚拟机中,直接自动生成



执行逻辑
    根据CustomSetting中的customDelegateList,生成lua委托并在DelegateFactory中注册到lua虚拟机中根据CustomSettings中的customTypeList,生成Wrap类在LuaBinder中生成Wrap类的注册逻辑


(3)解决报错问题

    一个报错定位到代码处


在Generate All一下



    定位到每个报错位置


    另外几个依次如此
    为防止下次Generate All重新生成这个报错,将这个脚本放到Assets/ToLua/BaseType下,并且在CustomSettings对应的_GT对应的类型注释掉
    打包,注意这一步是为了跑main场景而做的工作,如果不想跑main场景,自己实现AB的话,下面的步骤可以不用解决



但是会有问题,都是一些找不到属性或者赋值给一个只读属性的问题,找到引用,不让这个属性注册进去即可,并且将对应的方法注释掉
例如:



打包成功后,示例场景main就可以跑起来了
主要脚本介绍

LuaBinder

用来自动注册生成的Wrap类
在这里插入图片描述
DelegateFactory

委托注册到lua虚拟机中
LuaState

lua虚拟机(解释器),
LuaLooper

为了实现Unity中MonoBehaviour中的生命周期函数。
注意:如果没有LuaLooper,则lua的协程无法执行

Lua库

tolua库,使用时tolua.xxxx

gettime

获取系统时间
typename

获取对象的类型名称
setpeer(a,b)

像继承,a继承b,可以实现Lua中table b扩展C#类a
实际上封装了setmetatable,可以给C#中的类和Lua的类做继承关系。
为C#a对象设置一个lua代理b(table),所有调用a的方法,都会调用b的同名函数,访问或者设置a的属性也会访问b属性
getpeer

获取替身
getfunction

获取函数
initset initget

初始化get,set“访问器”,为一个table的成员变量设置get和set访问器,实际上都返回了一个表,命名为gettable和settable,当访问table的变量时,会调用gettable里的同名函数(如果有),当设置table的变量时,会调用settable里的变量
int64 uint64

生成一个int64,uint64对象,相当于调用了int64库和uint64库的new方法。可以进行正常的加减乘除的运算。还有tomun2函数,返回两个数,第二个数是右移32为的值,第一个数是剩下的值(&0xFFFFFFFF)
UTF8,Lua使用

utf8.len(“字符串”)–返回长度
utf8.byte_indices(“字符串”)–返回的时迭代函数,配合for可以进行遍历字符串
utf8.count(“字符串”,“指定字符串”)–返回字符串中出现指定字符串的次数
utf8.replace(“原字符串”,“老的”,“新的”)–将字符串中老的字符串替换成新的
操作系统OS

os.clock()–返回当前运行时间

C#中使用lua

参考示例场景



1,简单使用

(1) 创建一个Lua解释器:LuaState lua = new LuaState();
(2) 创建全局变量lua[“num”] = 2;
(3) 创建表:lua.NewTable(“tableName”)
获取表:var tb = lua.GetTable(“tableName”);
配置表:tb[“a”] = 111;
执行Lua脚本

DoFile是执行已经写好的Lua文件
DoString执行当前语句
执行lua中的函数

注意,如果lua函数中使用了self的话,调用时需要把表传递过去,
例如:
xp.lua脚本:
XP={}
XP:Init()
self.age = 24
self.height = 178
end
Unity调用:
var lua = new LuaState()
lua.DoFile(xp.lua)
var table = lua.GetTable(“XP”)
table.GetLuaFunction(“Init”).Call(table);
//调用他不会隐式的把自身传过去,需要手动传递过去
var func = lua.GetFunction(“函数名”);
func.Invoke<参数类型,返回类型>(“参数”);//只会返回一个参数
func.Call(“参数”);//无返回值
func.LazyCall(参数);//返回多个参数,用object[]存起来了,但是会产生gc alloc
func.BeginPCall();
func.Push(参数);
func.PCall();
func.CheckNumber()//获取参数1
func.CheckString()//获取参数2
其实Call和Invoke底层也是这样实现的,类比后,也可以实现多个参数多个返回
在Lua中创建Unity对象

反射调用方式不推荐使用,因为效率太慢,推荐使用wrap类反射模式调用,
去反射最大的弊端在于提前需要把C#的类导入Lua中,如果上线发现有些类没有导入,反射就可以通过临时的调用未wrap的类,进行使用,当大版本更新时,再将此类加入warp,这时候反射就是解决这种情况出现,但是概率小
反射调用:

string s = @"
tolua.loadassembly(‘Assembly-CSharp’) --加载程序集
local BindingFlags = require ‘System.Reflection.BindingFlags’–引入枚举
local t = typeof(‘Test’) --获得Type类
local func = tolua.getmethod(t, ‘方法名’)–得到对应的方法
func:Call(“参数名”);–调用方法
local obj = tolua.createinstance(t, 构造函数参数)–创建实例
local property = tolua.getproperty(t, ‘属性名’)–得到属性
local num = property:Get(obj,null)–得到属性对应的值
property:Set(obj,444,null)–设置属性值
local filed = tolua.getfield(t,‘字段名’)–获得字段反射
num =filed:Get(obj)–获得对应值
field:Set(obj,222)–设置值
lua.DoString(s);
非反射:

都要CustomSetting中添加这个类,然后生成对于的Warp类,在调用之前先注册绑定
LuaBinder.Bind(lua)
string s = @"
local GameObject = UnityEngine.GameObject–获取类
local MeshRenderer = UnityEngine.MeshRenderer–获取类
local newGo = GameObject(‘obj’)–创建对象
local go = GameObject.Find(“Main”)–调用静态查找方法
newGo:AddComponent(typeof(MeshRenderer))–添加组件
";
在lua中使用Unity携程

先在场景中添加LuaLooper脚本,在设置对应的lua虚拟机
looper = gameObject.AddComponent()
looper.luaState = lua
LuaCoroutine.Register(lua, this);
在Lua中使用Unity字典,枚举,List
  1. 在CustomSetting中添加对于的类型,在生成对应的Warp类,在Binder一下
复制代码
在Lua中使用Unity中的委托

先在DelegateFactory的Reginster方法中注册对应的委托和委托方法
在调用DelegateTraits<委托类型>.Init(委托方法);
在调用DelegateFactory.Init();方法
委托方法参考:



Lua调用C#的扩展方法

在生成Warp类时做手脚
比如扩展类A对B类做了扩展方法XPMethod
在CustomSetting中BindTypes中修改一下
_GT(typeof(B)).AddExtendType(typeof(A))
这样生成Warp类时就会带上XPMethod这个方法
在Lua使用JSON

先引入模块cjson,在使用
local json = require ‘cjson’
x = json.decode(“json字符串”)–反序列化
jsonStr = json.encode(x)–序列化
在Lua使用C#的String类型

跟调用其他类型,比如GameObject类型差不多,
如何查看在lua中能调用哪些方法?
在对应的Warp查看
在Lua中使用C#中的结构体



像Vector2,Vector3,byte这种其实LuaState内容已经实现了这种类似的机制



没有定义的结构体跟着样子定义即可

在Lua中实现MonoBehaviour以及互相通信

C#主动调用Lua

现在lua中定义一个创建类的函数class
在类中定义new方法生成对象
在lua中调用class函数生成Lua行为类
在Lua行为类定义好周期函数
在Unity中的Awake方法生成lua虚拟机以及加载需要的lua脚本
并在Awake方法中获取Lua行为类并调用new方法获得Lua行为对象存起来
在Unity的周期函数中,通过Lua行为对象获取其中的周期函数并调用。
注意调用时一定要把Lua行为对象传递进去
  1. public class TestBehaviour : MonoBehaviour
  2. {
  3.     LuaState lua;
  4.     LuaTable luaObject;
  5.     LuaFunction startMethod, updateMethod;
  6.     private void Awake()
  7.     {
  8.         lua = new LuaState();
  9.         lua.Start();
  10.         LuaBinder.Bind(lua);
  11.         
  12.         lua.DoString(@"
  13.         function class(classname,super)
  14.             local cls = {}
  15.             local superType = type(super)
  16.             
  17.             if(superType~='table') then
  18.                 superType = nil
  19.                 super = nil
  20.             end
  21.       
  22.             if(super) then
  23.               setmetatable(cls,{__index = super})
  24.               cls.super = super
  25.             end
  26.             
  27.             cls.name = classname
  28.             cls.__index = cls
  29.             
  30.             function cls:new()
  31.                instance = setmetatable({},cls)
  32.                instance.class = cls
  33.                instance:ctor()
  34.                return instance
  35.             end
  36.             
  37.             return cls
  38.         end");
  39.         lua.DoString(@"
  40.         LuaBehaviour = class('LuaBehaviour')
  41.         function LuaBehaviour:ctor()
  42.          --这里可以增加实例化GameObject
  43.          --并给这个GameObject增加C#Lua行为类
  44.          --并调用里面的方法将self这个对象传递过去
  45.         end
  46.         
  47.         function LuaBehaviour:Start(obj)
  48.             self.gameObject = obj
  49.             print('Start方法' .. self.gameObject.name)
  50.         end
  51.         
  52.         function LuaBehaviour:Update()
  53.             print('Update方法' .. self.gameObject.name)
  54.         end");
  55.     }
  56.     void Start()
  57.     {
  58.         var classL = lua.GetTable("LuaBehaviour");
  59.         luaObject = classL.GetLuaFunction("new").Invoke<GameObject,LuaTable>(gameObject);
  60.         startMethod = luaObject.GetLuaFunction("Start");
  61.         if (startMethod != null)
  62.             startMethod.Call(luaObject,gameObject);
  63.     }
  64.     void Update()
  65.     {
  66.         if (updateMethod != null)
  67.             updateMethod.Call(luaObject);
  68.         else
  69.             updateMethod = luaObject.GetLuaFunction("Update");
  70.     }
  71. }
复制代码
将上面的脚本挂载在场景中即可,也可以通过DoString中的字符串放入文件中,通过DoFile也可以,如果要实现lua脚本与lua脚本之间的通信,将luaObject弄成public的,在将这个类注册一下生成Warp类以及bind,在Lua脚本中通过GameObject.Find(‘Test’):GetComponent(typeof(TestBehaviour)).luaObject
获取对应的对象,得到这个Lua行为对象后,操作这个对象就可以实现lua脚本之间的通信
Lua来控制

上面那个方法有个弊端,那就是lua行为对象是在Unity周期函数中主动调用的去获取,而不是被动的获取,一般场景中开始是没有具体的行为的,具体的行为是通过Lua来创建GameObject时传递对象给C#。
稍微改变上面脚本,C#Lua行为类开始时就不要挂载在场景中,通过Lua脚本去调用生成具体的行为,也不要在周期函数获取对象了,在类中添加一个设置lua行为对象的方法


在生成Warp类以及Bind
然后在Unity的资源加载完成,并将lua脚本加载进去
将lua Lua行为类的构造函数修改一下
改变调用Lua方式
将下面这个脚本挂载在场景中
  1. public class Test : MonoBehaviour
  2. {
  3.     LuaState lua;
  4.     private void Awake()
  5.     {
  6.         lua = new LuaState();
  7.         lua.Start();
  8.         LuaBinder.Bind(lua);
  9.         lua.DoString(@"
  10.         function class(classname,super)
  11.             local cls = {}
  12.             local superType = type(super)
  13.             if(superType~='table') then
  14.                 superType = nil
  15.                 super = nil
  16.             end
  17.             if(super) then
  18.               setmetatable(cls,{__index = super})
  19.               cls.super = super
  20.             end
  21.             cls.name = classname
  22.             cls.__index = cls
  23.             function cls:new()
  24.                instance = setmetatable({},cls)
  25.                instance.class = cls
  26.                instance:ctor()
  27.                return instance
  28.             end
  29.             return cls
  30.         end");
  31.         lua.DoString(@"
  32.         LuaBehaviour = class('LuaBehaviour')
  33.         function LuaBehaviour:ctor()
  34.              self.gameObject = UnityEngine.GameObject('test') --这里实例化GameObject
  35.              self.behaviour =  self.gameObject:AddComponent(typeof(TestBehaviour))--添加脚本
  36.              self.behaviour:SetLuaObject(self)--传递表过去
  37.         end
  38.         function LuaBehaviour:Start()
  39.             print('Start方法' .. self.gameObject.name)
  40.         end
  41.         function LuaBehaviour:Update()
  42.             print('Update方法' .. self.gameObject.name)
  43.         end
  44. ");
  45.         lua.DoString(@"
  46.         --创建
  47.           luaBehaviour = LuaBehaviour:new()
  48.          
  49. ");
  50.     }
  51. }
复制代码
这个脚本开始的时候就不要添加到场景了,通过lua脚本去自动添加,记得要生成对应的Warp类以及Bind
  1. public class TestBehaviour : MonoBehaviour
  2. {
  3.     LuaTable luaObject;
  4.     LuaFunction startMethod, updateMethod;
  5.     void Start()
  6.     {
  7.         startMethod = luaObject.GetLuaFunction("Start");
  8.         if (startMethod != null)
  9.             startMethod.Call(luaObject,gameObject);
  10.     }
  11.    
  12.     // Update is called once per frame
  13.     void Update()
  14.     {
  15.         if (updateMethod != null)
  16.             updateMethod.Call(luaObject);
  17.         else
  18.             updateMethod = luaObject.GetLuaFunction("Update");
  19.     }
  20.     public void SetLuaObject(LuaTable luable)
  21.     {
  22.         this.luaObject = luable;
  23.     }
  24. }
复制代码

本帖子中包含更多资源

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

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

本版积分规则

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

GMT+8, 2025-5-14 09:38 , Processed in 0.139438 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2025 Discuz! Team.

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