当前位置: 首页 > news >正文

ToLua框架

ToLua 是一个用于在 Unity 中为 Lua 提供 C# 语言绑定的框架。通过 ToLua,你可以方便地将 C# 代码暴露给 Lua 脚本,并在 Lua 脚本中调用 C# 类、方法和属性。

更新流程

原理:使用AssetBundle进行资源的更新,而由于lua运行时才编译的特性,所以lua文件也可以被看成是一种资源文件(与fbx、Image等一样)可以打进ab包中

流程:

游戏运行时从服务器下载files.txt清单文件,与本地的files.txt清单文件进行对比。如果新下载的files里面的md5值与本地files的md5值不一样,或者本地清单里没有对应文件,那么就从服务器下载AB包,然后解压进行初始化

LuaState

C#能调用lua的原理就是创建了一个lua虚拟机,LuaState封装了对lua 主要数据结构 lua_State 指针的各种堆栈操作。

去反射

旧版本中lua调用C#函数是基于反射的,而现阶段是基于去反射,意思就是:把所有的c#类的public成员变量、成员函数,都导出到一个相对应的Wrap类中。而这些成员函数通过特殊的标记,映射到lua的虚拟机中,当在lua中调用相对应的函数时候,直接调用映射进去的c# wrap函数,然后再调用到实际的c#类,完成调用过程

Lua虚拟栈

在这里插入图片描述

C#会告诉虚拟机传入的参数和需要返回的参数,然后虚拟机开始访问栈,从栈中取出对应函数传送给Lua编译器。然后lua程序调用Lua文件全局表(Global table)查找对应函数,然后将返回参数入栈,C#再从栈中读取数据。

  1. 可以按1N的顺序从栈底向上,也可以从-1-N从栈顶向下
  2. 栈中可以存储任何数据,函数、table等,空的位置用nil表示

前置准备

使用Tolua的相关类和方法都需要调用命名空间LuaInterface,LuaState.Start 需要在tolua代码加载到内存后调用。如果使用assetbunblde加载lua文件,调用Start()之前assetbundle必须加载好。

调用lua脚本必须先创建一个lua虚拟机, 一般对于客户端,推荐只创建一个LuaState对象。如果要使用多State需要在Unity中设置全局宏 MULTI_STATE,创建步骤为:

LuaState lua = new LuaState();

加载脚本

重要方法lua.AddSearchPath ,通过此方法添加lua文件的路径,只有添加了文件路径之后,在该路径上的lua文件才可以被读取。这样DoFile跟Require函数可以只用文件名,无需写全路径。

在C#中运行一段lua脚本最简单的方法就是lua.DoString,该方法声明如下:

public object[] DoString(string chunk, string chunkName = "LuaState.DoString")

注意dofile需要扩展名, 可反复执行, 后面的变量会覆盖之前的DoFile加载的变量这里加载了就会从开始逐句执行。

LuaState.Require

public void Require(string filename)

因为Require 读取文件是会检查该文件是否被加载过,如果被加载过,则直接返回一个索引,否则则加载并返回一个索引

  • 使用完lua虚拟机之后记得要销毁,具体操作如下:

    • 先进行lua虚拟机的判空,具体做法为lua.CheckTop
    • 析构掉lua虚拟机,具体做法为:lua.Dispose

C#调用lua函数

调用lua方法

我们可以把lua中的函数看成一个对象,在虚拟机初始化完成后,加载对应的lua文件,接着需要创建一个LuaFunction类型的对象来表示这个lua函数,可以通过调用:lua.GetFunction("方法名"); 来获取对应的函数对象

获取了之后就需要在C#中调用了,主要方式有两种:

  1. 直接调用LuaFunction类型的对象的func.Call 方法,完整声明为:
public object[] Call(params object[] args)

这种调用方法比较简单,但是有一个缺点,lua对象的内存无法被自动释放,所以当使用完这个lua函数对象之后,我们需要手动的调用LuaFunction类型的对象的func.Dispose();方法,释放掉垃圾内存,否则会造成内存泄漏!

不过选择都采用的是委托的方式,需要先进行DelegateFactory.Init();

int num = luaFunc.Invoke<int,int>(arg);

或者是

Func<int, int> Func = luaFunc.ToDelegate<Func<int, int>>();
num = Func(123456);
  1. 如同案例当中的CallFunc()
int CallFunc()
{        luaFunc.BeginPCall();                luaFunc.Push(123456);luaFunc.PCall();        int num = (int)luaFunc.CheckNumber();luaFunc.EndPCall();return num;                
}

首先我们必须先以func.BeginPCall;开始,通过func.Push(参数)来给方法传参–通过众多重载函数来解决参数转换的gc,然后需要通过func.PCall();来运行,接着通过对应的func.Checkxxx()方法来获取返回值,最后通过func.EndPCall();结束–清楚函数调用导致的堆栈变化。整个流程比较繁琐且不易封装,不过优点是不会有垃圾内存,所以不用手动释放GC。

获取lua中的变量

创建全局变量

在lua虚拟机创建完成且初始化完毕(调用Start方法)之后,可以直接声明一个lua虚拟机的全局变量:

LuaState lua = new LuaState();
lua.Start();
lua["Objs2Spawn"] = 5;

获取全局变量格式是相同的,这点和lua相同,没有就创建否则就读取。值得注意的是对于获取的函数需要强制转换为LuaFunction:LuaFunction func = lua["TestFunc"] as LuaFunction;

获取与创建lua的table

通过调用虚拟机的方法lua.GetTable 来获取lua中的table,用LuaTable类型来储存lua中的Table,通过调用Luatable的成员方法table.AddTable来创建lua中的table , 除了通过虚拟机的GetTable方法访问之外,直接通过 LuaTable 型变量按字典的类似方法也可以调用table

LuaTable table1 = lua.GetTable("varTable");
Debug.Log("Read VarTable from lua name:{0}", table1[map.name]);//会将map.name作为整体识别为键,值为nil,不会有任何输出
table1["map.name"] = "new";// table字符串只能为key,现在为“new”
LuaTable table2 = (LuaTable)table["newmap"];

对于lua元表也能使用LuaTable的GetMetaTable()方法来获取

对于变量的获取有一点值得注意:

如同注释中的那样,[]中的string会作为整体。lua语法中,如果有一个table A,那么A.x代表的是A[“x”],表示的是由字符串 “x”索引的表, 而a[x]表示的是变量x对应的值索引的表

a = {}
x = "y"
a[x] = 10
a.x -->nil 字段“x”的值,未定义
a.y -->10, 字段“y”的值

因此如果想要C#正确获取的话,需要像下面这样:

LuaTable table = lua.GetTable("varTable");
LuaTable map = (LuaTable)table["map"];
Debugger.Log("Read varTable from lua,  name: {0}", map["name"]);

对LuaTable类型的变量,在使用完后需要手动释放内存,否则会因为内存未自动释放造成内存泄漏,具体方法为调用LuaTable对象的方法table.Dispose();

GameObject

private string script =@"                                    local GameObject = UnityEngine.GameObject          local ParticleSystem = UnityEngine.ParticleSystem            local go = GameObject('go')go:AddComponent(typeof(ParticleSystem))local node = go.transformnode.position = Vector3.one      print('gameObject is: '..tostring(go))    GameObject.Destroy(go, 2)                        ";LuaState lua = null;void Start()
{        lua = new LuaState();lua.LogGC = true;lua.Start();LuaBinder.Bind(lua);lua.DoString(script);            
}void Update(){lua.CheckTop();lua.Collect();
}

LuaBinder.Bind(); ——该方法需要传入一个 LuaState 类型参数。该方法会将 C# 代码中定义的类、方法、属性等绑定到该 Lua 模块中。这样,Lua 脚本就可以调用 C# 代码中定义的函数和属性,而 C# 代码也可以通过 Lua 脚本调用 Lua 中的函数和属性。

lua.Collect();——垃圾回收, 对于被自动gc的LuaFunction, LuaTable, 以及委托减掉的LuaFunction, 延迟删除的Object之类。等等需要延迟处理的回收, 都在这里自动执行

lua调用C#函数

如同 案例中的那样,

local ParticleSystem = UnityEngine.ParticleSystem
local GameObject = UnityEngine.GameObjectlocal 
go = GameObject('go')
go:AddComponent(typeof(ParticleSystem))
local node = go.transform
node.position = Vector3.one                  
print('gameObject is: '..tostring(go))

对于UnityEngine下的一部分方法,lua中可以直接引用。

自定义脚本

对于自己实现的脚本比较麻烦,首先需要调用 LuaBinder.Bind(LuaState lua)

其次需要将自定义的类写入CustomSetting.cs文件中,你将你的自定义类加入到customTypeList数组中的末尾,如果是静态类就还需加入staticClassTypes中。然后依次点击Lua菜单中的Clear wrap files 和Gen All

自定义委托

只要需要使用到delegate字段,都要调用一次DelegateFactory.Init(),如果有自定义的委托还是要添加到CustomSetting.cs中的customDelegateList

如何给按钮动态添加监听事件? 首先我们可以定义一个LuaHelper的静态类,在其中实现一个AddButtonClick的静态函数,NGUI中给按钮添加事件代码示例:EventDelegate.Add(UIButton.onclick, Call call)

public static void AddButtonClick(GameObject g,LuaFunction callback)
{EventDelegate.Add(g.GetComponent<UIButton>().onClick, callback.Call);
}

在lua代码中只需找到对应的游戏物体

local helper = LuaHelper
local btn = UnityEngine.GameObject.Find("Button")local function Onclick()print("OnClick")
end
helper.AddButtonClick(btn,Onclick)

Lua调用C#的规范就是不在Start周期函数中去做初始化,而是包装好初始化相关的函数,在lua中获取该函数在调用

获取C#数组元素

lua想要访问C#数组有几种方法,

  • 通过下标访问——这与C#中一致,而且还可以通过array.Length获取数组大小
  • 通过迭代器访问——array:GetEnumerator()获取数组的迭代器,通过iter.Current获得当前的迭代器所指向的元素值,通过iter:MoveNext()将迭代器所指位置移至下一个
  • 调用 array:ToTable() 实现将数组对象转化为对应的lua中的Table表的形式进行访问,注意lua数组下标从0开始

C#中获得lua函数的返回值,可以通过func.Checkxxx()来获得

比如

double arg1 = func.CheckNumber();//获得func函数double类型的返回值
string arg2 = func.CheckString();//获得func函数string类型的返回值
// 调用通用函数时需要转换类型,避免拆成多个参数
object[] objs = func.Invoke((object)array);
if (objs != null)
{Debugger.Log("return is {0} {1} {2}", objs[0], objs[1], objs[2]);
}

ToLua框架UML

在这里插入图片描述

相关文章:

ToLua框架

ToLua 是一个用于在 Unity 中为 Lua 提供 C# 语言绑定的框架。通过 ToLua&#xff0c;你可以方便地将 C# 代码暴露给 Lua 脚本&#xff0c;并在 Lua 脚本中调用 C# 类、方法和属性。 更新流程 原理&#xff1a;使用AssetBundle进行资源的更新&#xff0c;而由于lua运行时才编…...

Golang-常见数据结构Map

Map map 是一种特殊的数据结构&#xff1a;一种元素对&#xff08;pair&#xff09;的无序集合&#xff0c;pair 的一个元素是 key&#xff0c;对应的另一个元素是 value&#xff0c;所以这个结构也称为关联数组或字典。这是一种快速寻找值的理想结构&#xff1a;给定 key&…...

基于空间矢量脉宽调制(SVPWM)的并网逆变器研究(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

介绍tcpdump在centos中的使用方法

tcpdump是一款强大的命令行数据包分析器&#xff0c;支持多种过滤和抓包参数。下面将介绍tcpdump的常用抓包参数。当需要监控CentOS系统的网络流量或者进行网络故障排查时&#xff0c;可以使用tcpdump来捕获数据包并进行分析。 下面介绍在CentOS中使用tcpdump的方法&#xff1…...

机器学习实战:Python基于DT决策树模型进行分类预测(六)

文章目录 1 前言1.1 决策树的介绍1.2 决策树的应用 2 Scikit-learn数据集演示2.1 导入函数2.2 导入数据2.3 建模2.4 评估模型2.5 可视化决策树2.6 优化模型2.7 可视化优化模型 3 讨论 1 前言 1.1 决策树的介绍 决策树&#xff08;Decision Tree&#xff0c;DT&#xff09;是一…...

操作系统之进程同异步、互斥

引入 异步性是指&#xff0c;各并发执行的进程以各自独立的、不可预知的速度向前推进。 但是在一定的条件之下&#xff0c;需要进程按照一定的顺序去执行相关进程&#xff1a; 举例说明1&#xff1a; 举例说明2: 读进程和写进程并发地运行&#xff0c;由于并发必然导致异步性…...

你了解这2类神经性皮炎吗?常常预示着这5类疾病!

神经性皮炎属于慢性皮肤病&#xff0c;患者皮肤可出现局限性苔藓样变&#xff0c;同时伴有阵发性瘙痒。神经性皮炎易发生在颈部两侧和四肢伸侧&#xff0c;中年人是高发人群。到目前为止神经性皮炎病因还并不是很明确&#xff0c;不过一部分病人发病前常常出现精神神经方面异常…...

二叉搜索树【Java】

文章目录 二叉搜索树的性质二叉搜索树的操作遍历查找插入删除 二叉搜索树又称为二叉排序树&#xff0c;是一种具有一定性质的特殊的二叉树&#xff1b; 二叉搜索树的性质 若它的左子树不为空&#xff0c;则左子树上结点的值均小于根节点的值&#xff1b; 若它的右子树不为空&a…...

二叉树的遍历方式

文章目录 层序遍历——队列实现分析Java完整代码 先序遍历——中左右分析递归实现非递归实现——栈实现 中序遍历——左中右递归实现非递归实现——栈实现 后续遍历——左右中递归实现非递归实现——栈加标志指针实现 总结 层序遍历——队列实现 给你二叉树的根节点 root &…...

SpringCloud01

SpringCloud01 微服务入门案例 实现步骤 导入数据 实现远程调用 MapperScan("cn.itcast.order.mapper") SpringBootApplication public class OrderApplication {public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args);}…...

SpringBoot整合Redis实现点赞、收藏功能

前言 点赞、收藏功能作为常见的社交功能&#xff0c;是众多Web应用中必不可少的功能之一。而redis作为一个基于内存的高性能key-value存储数据库&#xff0c;可以用来实现这些功能。 本文将介绍如何使用spring boot整合redis实现点赞、收藏功能&#xff0c;并提供前后端页面的…...

【Java入门合集】第一章Java概述

【Java入门合集】第一章Java概述 博主&#xff1a;命运之光 专栏&#xff1a;JAVA入门 学习目标 1.理解JVM、JRE、JDK的概念&#xff1b; 2.掌握Java开发环境的搭建,环境变量的配置&#xff1b; 3.掌握Java程序的编写、编译和运行&#xff1b; 4.学会编写第一个Java程序&#x…...

Android无线调试操作说明

1.首先通过手机机蓝牙将jackpal.androidterm-1.0.70.apk(终端模拟器)传的设备上安装 链接: https://pan.baidu.com/s/151SzEgsX0b_VTWowzfUrsA?pwdrn75 提取码: rn75 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 2.打开这个终端模拟器&#xff0c;输入以下命…...

什么是 Python ?聊一聊Python程序员找工作的六大技巧

最近我一直在思考换工作的事情。因此&#xff0c;这段时间我会看一些题目&#xff0c;看一些与面试相关的内容&#xff0c;以便更好地准备面试。我认为无论你处于什么阶段&#xff0c;面试中都会有技术面试环节。无论是初级职位还是高级职位&#xff0c;都需要通过技术面试来检…...

RabbitMQ 01 概述

什么是消息队列 进行大量的远程调用时&#xff0c;传统的Http方式容易造成阻塞&#xff0c;所以引入了消息队列的概念&#xff0c;即让消息排队&#xff0c;按照队列进行消费。 它能够将发送方发送的信息放入队列中&#xff0c;当新的消息入队时&#xff0c;会通知接收方进行处…...

面经|曹操出行供需策略运营

1.自我介绍 面试官表示看了简历之后&#xff0c;表示对专业能力比较放心。想了解下对于专业能力之外&#xff0c;关于其他方面的介绍。 2.策略运营&#xff0c;除了工具之外&#xff0c;还有哪些能力是需要具备的 回答&#xff1a;主要是从做项目的维度逻辑先去回答的。 分析思…...

【Python】selenium工具

目录 1. 安装 2. 测试 3. 无头浏览器 4. 元素定位 5. 页面滑动 6. 按键、填写登录表单 7. 页面切换 Selenium是Web的自动化测试工具&#xff0c;为网站自动化测试而开发&#xff0c;Selenium可以直接运行在浏览器上&#xff0c;它支持所有主流的浏览器&#xff0c;可以接…...

实验六~Web事件处理与过滤器

1. 创建一个名为exp06的Web项目&#xff0c;编写、部署、测试一个ServletContext事件监听器。 BookBean代码 package org.example.beans;import java.io.Serializable;/*** Created with IntelliJ IDEA.* Description:* User: Li_yizYa* Date: 2023—04—29* Time: 18:39*/ Su…...

刷题4.28

1、 开闭原则软件实体&#xff08;模块&#xff0c;类&#xff0c;方法等&#xff09;应该对扩展开放&#xff0c;对修改关闭&#xff0c;即在设计一个软件系统模块&#xff08;类&#xff0c;方法&#xff09;的时候&#xff0c;应该可以在不修改原有的模块&#xff08;修改关…...

做了一年csgo搬砖项目,还清所有债务:会赚钱的人都在做这件事 !

前段時间&#xff0c;在网上看到一句话&#xff1a;有什么事情&#xff0c;比窮更可怕&#xff1f; 有人回答说&#xff1a;“又忙又窮。” 很扎心&#xff0c;却是绝大多数人的真实写照。 每天拼死拼活的996&#xff0c;你有算过你的時间值多少钱&#xff1f; 我们来算一笔…...

线性回归模型(7大模型)

线性回归模型&#xff08;7大模型&#xff09; 线性回归是人工智能领域中最常用的统计学方法之一。在许多不同的应用领域中&#xff0c;线性回归都是非常有用的&#xff0c;例如金融、医疗、社交网络、推荐系统等等。 在机器学习中&#xff0c;线性回归是最基本的模型之一&am…...

VP记录:Codeforces Round 868 (Div. 2) A~D

传送门:CF A题:A-characteristic 构造一个只有 1 , − 1 1,-1 1,−1的数组,满足乘积为 1 1 1的数对的个数为 k k k. 发现 n n n的范围很小,考虑直接暴力枚举数组中 1 1 1的个数,记为 i i i,那么对于1的所有数对来说,我们有 i ∗ ( i − 1 ) / 2 i*(i-1)/2 i∗(i−1)/2个,然后…...

【VQ-VAE-2论文精读】Generating Diverse High-Fidelity Images with VQ-VAE-2

【VQ-VAE-2论文精读】Generating Diverse High-Fidelity Images with VQ-VAE-2 0、前言Abstract1 Introduction2 Background2.1 Vector Quantized Variational AutoEncoder3 Method3.1 Stage 1: Learning Hierarchical Latent Codes3.2 Stage 2: Learning Priors over Latent C…...

并发编程基石:管程

大家好&#xff0c;我是易安&#xff01; 如果有人问我学习并发并发编程&#xff0c;最核心的技术点是什么&#xff0c;我一定会告诉他&#xff0c;管程技术。Java语言在1.5之前&#xff0c;提供的唯一的并发原语就是管程&#xff0c;而且1.5之后提供的SDK并发包&#xff0c;也…...

电路中噪声来源

电路包括不同的部件和芯片&#xff0c;所有都有可能成为噪声的来源。例如&#xff0c;电阻会带来热噪声&#xff0c;这个噪声为宽频噪声&#xff0c;几乎涵盖所有频率范围&#xff1b;运算放大器其芯片内部会产生噪声&#xff1b;而 ADC产生的量化噪声相较于其他器件&#xff0…...

JAVASE的全面总结

&#xff08;未完待续&#xff09; 五、子类与继承 5.1 子类与父类 继承是一种由已有的类创建新类的机制。利用继承&#xff0c;我们可以先创建一个共有属性的一般类&#xff0c;根据该一般类再创建具有特殊属性的新类&#xff0c;新类继承一般类的状态和行为&#xff0c;并…...

关于repeater录制的流量子调用的identity中带有~S的情况

前段时间同事问我&#xff0c;我们录制的流量中&#xff0c;尤其是dubbo的子调用显示经常他的末尾会带上一个小尾巴这个是什么意思呢&#xff0c;其实之前我没有太在意这个事情&#xff0c;只是同事这么疑问了&#xff0c;确实激起了好奇心&#xff0c;所以就差了下 到底是什么…...

Java面试题队列

Java中的队列都有哪些&#xff0c;有什么区别 1. ArrayDeque, &#xff08;数组双端队列&#xff09; 2. PriorityQueue, &#xff08;优先级队列&#xff09; 3. ConcurrentLinkedQueue, &#xff08;基于链表的并发队列&#xff09; 4. DelayQueue, &#xff08;延期…...

大型Saas系统的权限体系设计(二)

X0 上期回顾 上文《大型Saas系统的权限体系设计(一)》提到2B的Saas系统的多层次权限体系设计的难题&#xff0c;即平台、平台的客户、客户的客户&#xff0c;乃至客户的客户的客户如何授权&#xff0c;这个可以通过“权限-角色-岗位”三级结构来实现。 但这个只是功能权限&am…...

HTML(四) -- 多媒体设计

目录 1. 视频标签 2. 音频标签 3. 资源标签&#xff08;定义媒介资源 &#xff09; 1. 视频标签 属性值描述autoplayautoplay如果出现该属性&#xff0c;则视频在就绪后马上播放。controlscontrols表示添加标准的视频控制界面&#xff0c;包括播放、暂停、快进、音量等…...

网站实名制注册怎么做/各网站收录

要遍历Java中的数组&#xff0c;只需使用for循环即可。循环应一直到数组的长度才能显示所有元素。示例现在让我们来看一个遍历数组的示例-public class Demo {public static void main(String args[]) {int myArray[] new int[5];myArray[0] 230;myArray[1] 110;myArray[2] …...

郑州小程序/厦门百度整站优化服务

国外的源访问很慢&#xff0c;可以用国内的源进行下载&#xff0c;这里以清华的源为例&#xff0c;命令如下&#xff1a; pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple 要安装的库的名称例如我要安装PyQt5&#xff0c;那么命令就是&#xff1a; pip3 install -…...

vs和dw做网站的区别/怎么制作一个网站

最近3d相册可谓是很火&#xff0c;小编的朋友圈就被刷屏了。再加上后台很多小可爱留言说想要关于3d旋转相册&#xff0c;但是大多数人又不会编程&#xff0c;代码什么的敲敲打打太费劲了&#xff0c;考虑到这些&#xff0c;小编找朋友做了这个相册的代码&#xff0c;一共有两个…...

网站设计大概多少钱/产品推广找哪家公司

苹果在WWDC上展示了15英寸Retina Macbook Pro&#xff0c; 让大家对13英寸Retina Macbook Pro 期待有加&#xff0c;分析师也普遍认为更小尺寸的MacBook Pro是众望所归&#xff0c;因为这样的产品既方便携带&#xff0c;又可以降低消费压力。今天苹果上个月&#xff08;6.29&am…...

wordpress代码缩进/seo人工智能

点击上方 蓝色文字&#xff0c;选择置顶或星标第一时间关注 Python 技术干货&#xff01;阅读文本大概需要 5 分钟。工欲善其事必先利其器的道理相信大家都懂。而作为经常要和各大网站做拉锯战的爬虫工程师们&#xff0c;则更需要利用好身边的一切法器&#xff0c;以便更快的攻…...

郑州网站建设招聘/泰州seo外包公司

持久化的简介RDBAOFRDB与AOF的区别持久化应用场景对于持久化这个功能点&#xff0c;其实很简单没有那么复杂演示环境centos7.0redis4.0redis存放目录&#xff1a;/usr/local/redisredis.conf存放目录&#xff1a;/usr/local/redis/data1. 持久化简介redis的所有数据都是保存在内…...