您现在的位置是:首页 > 正文

Unity ToLua 使用教程

2024-04-01 02:57:49阅读 4

tolua下载地址
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中生成的类
![在这里插入图片描述](https://img-blog.csdnimg.cn/8f5f5813fefd430e8a288bd1e1d55af5.png

同理:增加自己的C#类也是这样

(2) 生成Lua Delegates和LuaBinder

在这里插入图片描述

生成的Warp类需要要LuaBinder中注册到lua虚拟机中,生成的lua委托需要在DelegateFactory中注册到lua虚拟机中,直接自动生成
在这里插入图片描述

执行逻辑

  1. 根据CustomSetting中的customDelegateList,生成lua委托并在DelegateFactory中注册到lua虚拟机中
  2. 根据CustomSettings中的customTypeList,生成Wrap类
  3. 在LuaBinder中生成Wrap类的注册逻辑
    在这里插入图片描述

(3)解决报错问题

  1. 一个报错定位到代码处
    在这里插入图片描述

在Generate All一下
在这里插入图片描述

  1. 定位到每个报错位置
    在这里插入图片描述
    另外几个依次如此

  2. 为防止下次Generate All重新生成这个报错,将这个脚本放到Assets/ToLua/BaseType下,并且在CustomSettings对应的_GT对应的类型注释掉

  3. 打包,注意这一步是为了跑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

在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行为对象传递进去

public class TestBehaviour : MonoBehaviour
{
    LuaState lua;
    LuaTable luaObject;
    LuaFunction startMethod, updateMethod;
    private void Awake()
    {
        lua = new LuaState();
        lua.Start();
        LuaBinder.Bind(lua);
        
        lua.DoString(@"
        function class(classname,super)
            local cls = {}
            local superType = type(super)
            
            if(superType~='table') then
                superType = nil
                super = nil
            end
       
            if(super) then
              setmetatable(cls,{__index = super})
              cls.super = super
            end
            
            cls.name = classname
            cls.__index = cls
            
            function cls:new()
               instance = setmetatable({},cls)
               instance.class = cls
               instance:ctor()
               return instance
            end
            
            return cls
        end");
        lua.DoString(@"
        LuaBehaviour = class('LuaBehaviour')
        function LuaBehaviour:ctor()
         --这里可以增加实例化GameObject
         --并给这个GameObject增加C#Lua行为类
         --并调用里面的方法将self这个对象传递过去
        end
        
        function LuaBehaviour:Start(obj)
            self.gameObject = obj
            print('Start方法' .. self.gameObject.name)
        end
        
        function LuaBehaviour:Update()
            print('Update方法' .. self.gameObject.name)
        end");
    }
    void Start()
    {
        var classL = lua.GetTable("LuaBehaviour");
        luaObject = classL.GetLuaFunction("new").Invoke<GameObject,LuaTable>(gameObject);
        startMethod = luaObject.GetLuaFunction("Start");
        if (startMethod != null)
            startMethod.Call(luaObject,gameObject);
    }
    void Update()
    {
        if (updateMethod != null)
            updateMethod.Call(luaObject);
        else
            updateMethod = luaObject.GetLuaFunction("Update");
    }
}

将上面的脚本挂载在场景中即可,也可以通过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方式

将下面这个脚本挂载在场景中

public class Test : MonoBehaviour
{
    LuaState lua;
    private void Awake()
    {
        lua = new LuaState();
        lua.Start();
        LuaBinder.Bind(lua);
        lua.DoString(@"
        function class(classname,super)
            local cls = {}
            local superType = type(super)
            if(superType~='table') then
                superType = nil
                super = nil
            end
            if(super) then
              setmetatable(cls,{__index = super})
              cls.super = super
            end
            cls.name = classname
            cls.__index = cls
            function cls:new()
               instance = setmetatable({},cls)
               instance.class = cls
               instance:ctor()
               return instance
            end
            return cls
        end");
        lua.DoString(@"
        LuaBehaviour = class('LuaBehaviour')
        function LuaBehaviour:ctor()
             self.gameObject = UnityEngine.GameObject('test') --这里实例化GameObject
             self.behaviour =  self.gameObject:AddComponent(typeof(TestBehaviour))--添加脚本
             self.behaviour:SetLuaObject(self)--传递表过去
        end
        function LuaBehaviour:Start()
            print('Start方法' .. self.gameObject.name)
        end
        function LuaBehaviour:Update()
            print('Update方法' .. self.gameObject.name)
        end
");
        lua.DoString(@"
        --创建
          luaBehaviour = LuaBehaviour:new()
          
");
    }
}

这个脚本开始的时候就不要添加到场景了,通过lua脚本去自动添加,记得要生成对应的Warp类以及Bind

public class TestBehaviour : MonoBehaviour
{
    LuaTable luaObject;
    LuaFunction startMethod, updateMethod;
    void Start()
    {
        startMethod = luaObject.GetLuaFunction("Start");
        if (startMethod != null)
            startMethod.Call(luaObject,gameObject);
    }
   
    // Update is called once per frame
    void Update()
    {
        if (updateMethod != null)
            updateMethod.Call(luaObject);
        else
            updateMethod = luaObject.GetLuaFunction("Update");
    }
    public void SetLuaObject(LuaTable luable)
    {
        this.luaObject = luable;
    }
}

网站文章

  • stringRedisTemplate中ValueOperations设置值

    stringRedisTemplate中ValueOperations设置值

    stringRedisTemplate中ValueOperations设置值

    2024-04-01 02:57:08
  • TZ_13_微服务场景Eureka

    1.搭建Eureka的注册中心 Eureka服务中心 application.yaml文件的配置#配置自己的端口号server: port: 10086#配置注册中心让自己也能注册到自己 Client就不会因为注册不到自己而报错了eureka: client: service-url: defaultZone: http://...

    2024-04-01 02:57:01
  • springboot自动装配的原理

    @EnableAutoConfiguration实现的关键在于引入了AutoConfigurationImportSelector,其核心逻辑为selectImports方法, 逻辑大致如下:从spr...

    2024-04-01 02:56:54
  • 阿里云海外服务器需要备案吗?海外服务器地域节点分布表

    阿里云海外服务器需要备案?云吞铺子:当然不需要备案!阿里云地域节点覆盖全球,云吞铺子分享海外服务器地域节点分布表:海外服务器需要备案吗?购买阿里云海外服务器需要备案吗?不需要备案!海外服务器地域节点分布表随着时间推移,阿里云会支持越来越多的地域节点,精准信息请以官方文档为准:地域和可用区 - 阿里云地域名称所在城市Region ID可...

    2024-04-01 02:56:48
  • 【深度学习笔记1.1】人工神经网络(内含模型保存与恢复介绍)

    【深度学习笔记1.1】人工神经网络(内含模型保存与恢复介绍)

    线性阈值单元 线性阈值单元(LTU):输入和输出是数字(而不是二进制开/关值),并且每个输入连接都与权重相连。LTU计算其输入的加权和(z = W1×1 + W2×2 + … + + WN×n = W...

    2024-04-01 02:56:08
  • JDK17遇到报错 module java.base does not “opens java.util“ to unnamed module 问题解决 热门推荐

    JDK17遇到报错 module java.base does not “opens java.util“ to unnamed module 问题解决 热门推荐

    在Java 9及以上版本运行应用程序时,在各种情况下都会发生此异常。 详细可以参考module java.base does not &quot;opens java.lang&quot; to un...

    2024-04-01 02:56:00
  • NoSQL 中 MongoDB 和 Redis 的入门知识

    今天学习了 NoSQL 中 MongoDB 和 Redis 的一些入门知识,因为并不想深入了解学习(只是想了解学习 SQL 之外的),所以只是简单了解了入门的一些基础知识与命令,在此总结记录下。 文档...

    2024-04-01 02:55:54
  • 【c++STL——第五讲】queue系列 (常用知识点总结)

    【c++STL——第五讲】queue系列 (常用知识点总结)

    大家好,我是quicklysleep,欢迎大家光临我的博客,算法学习笔记系列持续更新中~ 文章目录一、前言二、queue的定义三、queue的常用函数四、vector的遍历方法最后 一、前言 在 C+...

    2024-04-01 02:55:08
  • 算法 -归并排序

    归并排序 归并介绍归并排序复杂度直观的说 为什么归并排序要比冒泡等排序快?例题及变种:本周打卡记录归并介绍1)整体是递归,左边排好序+右边排好序+merge让整体有序2)让其整体有序的过程里用了排外序...

    2024-04-01 02:55:00
  • Java文件上传实现 热门推荐

    Java文件上传实现 热门推荐

    (1)准备好前台页面Upload.html 表单 action=上传文件后台接口 method=“post”enctype=“multipart/form-data” 文件输入框&lt;input t...

    2024-04-01 02:54:18