[Unity XLua]热更新XLua入门(一)-基础篇

article/2025/9/28 2:40:57

无意中发现了一个巨牛巨牛的人工智能教程,忍不住分享一下给大家。教程不仅是零基础,通俗易懂,小白也能学,而且非常风趣幽默,还时不时有内涵段子,像看小说一样,哈哈~我正在学习中,觉得太牛了,所以分享给大家。点这里可以跳转到教程!

更多精品文章

http://dingxiaowei.cn/ (手动复制到浏览器)

Aladdin_XLua

前言

前段时间腾讯开源了一个内部热更框架XLua在Unity开发群里引起一阵热议,也受到广大开发者的热捧,然后我当然也抱着好奇的心去学习学习。后面也会将扩展之后的工程放在git上,大家一起学习交流!在此感谢XLua作者创造出这么好用的框架!

相关链接

  1. XLua源码

  2. C#->Lua开源工具
    可以将C#转化成lua并且具有

  3. 相关介绍文章
    https://www.oschina.net/news/80638/c-net-lua-unity3d

  4. 知乎热议

更多教程

http://dingxiaowei.cn

个人对XLua看法

  1. 简洁易用,容易上手
  2. 可扩展性高,添加自定义的CS模块或者第三方插件非常方便
  3. 大厂维护,可靠
  4. 特色:HotFix
    关于这个HotFix是其他热更lua框架所不具备的,也是他最大的优势和特色之一,原理就是通过特性标记然后在IL逻辑层判断修改逻辑,使程序支持热更的lua逻辑代码而不是走之前的C#逻辑

自己扩展XLua支持NGUI开发

现在开源热更Lua框架都很少支持NGUI了,可能现在趋势都是用原生的UGUI,但估计还有一些NGUI粉喜欢用NGUI开发,毕竟NGUI用了很长时间,XLua例子里面已经支持了lua使用UGUI,这里我就自己补充让它支持NGUI开发。后续我也会多添加一些UGUI的例子。先看看扩展的NGUI做的界面效果图,然后下面再讲解怎么让XLua支持第三方插件。

效果图

这里写图片描述

快速上手

在学习一个东西之前最好先学习一下XLua作者辛辛苦苦写的那些多教程文档,包括案例以及wiki和issu,如果还有什么不明白的地方可以在加入文章最后的群我们一起交流学习。

1.自定义C#类供Lua访问

这里可以学习一下作者写的Test中的例子,还是比较详细,但我还是想记录一下我自己尝试的过程。

(1)特性方式

XLua支持使用特性标签让自定义的类可供Lua访问
C#:

public class CSModelWithAttribute
{public static void SayHello1(){Debug.Log("Hello Aladdin_XLua, I am static model function");}public void SayHello2(){Debug.Log("Hello Aladdin_XLua, I am model function");}public void SayHello3(string s){Debug.Log("Hello Aladdin_XLua, I am model function whih param:" + s);}public string SayHello4(string s){Debug.Log("Hello Aladdin_XLua, 我是具有返回值的CS方法:" + s);return "你好,我获得了lua,我是C#";}public void SayHelloWithRefParam(ref string s){Debug.Log("传入的参数是:" + s);s = "Hello 我是C#";}public string SayHelloWithRefParamAndReturnString(ref string s){Debug.Log("传入的参数是:" + s);s = "Hello 我是C#";return "我是返回的字符串";}public void SayHelloWithOutParam(out string s){s = "Hello,我是C#";Debug.Log("Hello Aladdin_XLua, I am model function whih out param:" + s);}
}

添加上特性标签之后,程序在启动的会自动查找具有特性标记的类然后搜集进入lua栈,使得我们可以用Lua访问自定义的C#类和方法。

Lua访问:

using UnityEngine;
using System.Collections;
using XLua;
public class SelfExampleSrc : MonoBehaviour
{LuaEnv luaenv = new LuaEnv();void Start(){luaenv.DoString(@"print('Lua访问特性标记的对象方法')local luaM2 = CS.CSModelWithAttributelocal luaO2 = luaM2()luaM2:SayHello1()luaO2:SayHello2()luaO2:SayHello3('我是阿拉丁')  --读者反馈新增一个C#方法 PC不需要重新 Generate,安卓、ios需要--测试字符串返回local value = luaO2:SayHello4('你好,我是lua')print(value)                              --测试reflocal inputValue = '你好,我是lua'local outputValue = luaO2:SayHelloWithRefParam(inputValue)print(outputValue)                          --lua是通过字符串返回local outValue1,outValue2 = luaO2:SayHelloWithRefParamAndReturnString(inputValue)print(outValue1)print(outValue2)--测试outinputValue = '我是测试lua'outputValue = luaO2:SayHelloWithOutParam(inputValue)print(outputValue)local luaM3 = CS.CSModelTestlocal luaO3 = luaM3()luaO3.TestDelegate('lua中测试委托')luaO3.onClick = function(obj)print('hello 我是lua')print(obj)endluaO3.onClick('我是lua')");}
}
(2)wrap方式

如果是我们自己写的C#代码,我们可以通过第一种方式添加特性来让Lua支持也是比较方便,如果是用的开源第三方插件,我们如何快速的让XLua支持,就可以用过Generate Wrap的方式,这一点也是其他lua框架所采取的策略。

a)有命名空间的类

C#:

namespace Aladdin_XLua
{public class CSModel{public void SayHello(){Debug.Log("Hello Aladdin_XLua");}}
}

Lua:

print('Lua访问有命名空间的对象/静态方法')
local luaModel = CS.Aladdin_XLua.CSModel
local luaObj = luaModel()
luaObj:SayHello()

luaModel是C#类,下面luaObj是类生成的对象,最后是访问对象方法,关于Lua冒号调用和点调用的区别:冒号调用默认传入了self参数,不清楚的可以百度相关文章。

b)无命名空间的类

C#:

public class CSModelWidhoutNameSpace
{public void SayHello(){Debug.Log("Hello Aladdin_XLua without Namespace");}
}

Lua:

print('Lua访问无命名空间的对象方法')
local luaM = CS.CSModelWidhoutNameSpace
local luaO = luaM()
luaO:SayHello()

如果没有命名空间的话直接CS后面就是类名,其实CS也是一个更外面一层的命名空间,只不过是作者帮我们分装的。

3)委托类型

C#:

public class CSModelTest
{public SelfVoidDelegate onClick;public delegate void SelfVoidDelegate(GameObject go);void OnClick() { Debug.Log("测试"); }public Action<string> TestDelegate = (param) =>{Debug.Log("TestDelegate in c#:" + param);};
}

委托其实也是跟Class平级的,委托也是一种类型,所以我们也需要对它像对待类那样处理,通过添加特性标记或者通过Wrap方式处理,这里委托是放在类里面,其实也可以直接放在命名空间下面,.NET库是这样操作的,但我们看NGUI源码会发现,NGUI源码都是这样操作的,比如按钮的onClick事件,看它的委托类型VoidDelegate就会发现也是这样操作的,所以我这里例子也放在类的里面。

C#

public class CSModelTest
{public SelfVoidDelegate onClick;public delegate void SelfVoidDelegate(GameObject go);void OnClick() { Debug.Log("测试"); }public Action<string> TestDelegate = (param) =>{Debug.Log("TestDelegate in c#:" + param);};
}

Lua:

local luaM3 = CS.CSModelTest
local luaO3 = luaM3()
luaO3.TestDelegate('lua中测试委托')luaO3.onClick = function(obj)print('hello 我是lua')print(obj)
end
luaO3.onClick('我是lua')
4)带有ref out 参数的函数如何处理

因为Lua是弱类型没有C#那么多类型,有时候一些参数可能就不太好处理,比如C#的不同类型参数的重载,lua就不太好处理,这里可以查看XLua中的issues,作者有一个问题的相关解答。下面我举例ref和out参数类型的函数Lua如何访问。
C#:

public void SayHelloWithRefParam(ref string s)
{Debug.Log("传入的参数是:" + s);s = "Hello 我是C#";
}public string SayHelloWithRefParamAndReturnString(ref string s)
{Debug.Log("传入的参数是:" + s);s = "Hello 我是C#";return "我是返回的字符串";
}public void SayHelloWithOutParam(out string s)
{s = "Hello,我是C#";Debug.Log("Hello Aladdin_XLua, I am model function whih out param:" + s);
}

Lua:

--测试ref
local inputValue = '你好,我是lua'
local outputValue = luaO2:SayHelloWithRefParam(inputValue)
print(outputValue)                          --lua是通过字符串返回local outValue1,outValue2 = luaO2:SayHelloWithRefParamAndReturnString(inputValue)
print(outValue1)
print(outValue2)--测试out
inputValue = '我是测试lua'
outputValue = luaO2:SayHelloWithOutParam(inputValue)
print(outputValue)

一开始我测试的时候是本以为lua调用ref传入的参数,也会返回出修改的结果,但出乎我的意料,并没能修改,经过作者提示,lua是通过返回值返回的ref参数,如果函数本身就有返回值,那么最后一个参数是返回的ref或者out参数,这个读者可以尝试一下。

运行结果

这里写图片描述

关于Wrap

Wrap是C#跟Lua之间的一个桥梁,Lua想要访问C#必须要用过Wrap访问,相信看过其他Lua框架的这一点应该不陌生,XLua对生成Wrap也是非常方便。

我们只要新建一个类然后继承一个GenConfig的接口,下面是接口内容,关于这几个类型XLua文档中也有介绍,我们只需要把自定义的类添加到LuaCallCSharp集合中即可,然后点击Generate就会自动帮我们生成对应的Wrap文件

//注意:用户自己代码不建议在这里配置,建议通过标签来声明!!public interface GenConfig {//lua中要使用到C#库的配置,比如C#标准库,或者Unity API,第三方库等。List<Type> LuaCallCSharp { get; }//C#静态调用Lua的配置(包括事件的原型),仅可以配delegate,interfaceList<Type> CSharpCallLua { get; }//黑名单List<List<string>> BlackList { get; }}

当然作者也说了,我们自定的C#代码最好不要通过这种方式,我这里只是演示如何添加,下面会说第三方插件通过这话总方式支持。
C#:

public static class AladdinGenConfig
{//lua扩展第三方或者自定义类库public class LuaCallCSharpExtern : GenConfig{public List<Type> LuaCallCSharp{get{return new List<Type>(){typeof(CSModelWidhoutNameSpace),typeof(CSModel),typeof(CSModelTest),}}}}
}

2.NGUI扩展

正如上图所示的效果,下面讲述一下我是如何支持NGUI扩展的,也参考了作者UGUI的一个例子修改的。

a)生成Wrap接口

这一步上上面说的一样,只要把NGUI的组件类全部都添加到LuaCallCSharp列表中然后Generate一下即可,这里要注意的是组件中委托类型也需要添加进去。

b)搭建两个UI界面,UI逻辑接口用C#,Lua是调用逻辑调用界面中C#的方法。

这里写图片描述
这里写图片描述

C#:
购买

using UnityEngine;
using System.Collections;
using XLua;
public class AsyncBuy : MonoBehaviour
{LuaEnv luaenv = null;void Start(){luaenv = new LuaEnv();luaenv.DoString("require 'async_buy'");}// Update is called once per framevoid Update(){if (luaenv != null){luaenv.Tick();}}
}

Panel逻辑:

using UnityEngine;
using UnityEngine.UI;
using XLua;
using System.Collections.Generic;
using System;
using UnityEngine.Events;public class MessagePanel : MonoBehaviour
{/// <summary>/// 显示对话弹框/// </summary>/// <param name="message"></param>/// <param name="title"></param>/// <param name="onFinished"></param>public static void ShowAlertPanel(string message, string title, Action onFinished = null){Debug.Log("显示提示弹框");var rootPanel = GameObject.Find("Panel").transform;var alertPanel = rootPanel.Find("AlertPanel");if (alertPanel == null){alertPanel = (Instantiate(Resources.Load("AlertPanel")) as GameObject).transform;alertPanel.gameObject.name = "AlertPanel";alertPanel.SetParent(rootPanel);alertPanel.localPosition = Vector3.zero;alertPanel.localScale = Vector3.one;}alertPanel.Find("Title").GetComponent<UILabel>().text = title;alertPanel.Find("Content").GetComponent<UILabel>().text = message;alertPanel.gameObject.SetActive(true);if (onFinished != null){var buyBtn = alertPanel.Find("BtnBuy").gameObject;buyBtn.SetActive(true);var button = buyBtn.GetComponent<UIButton>();UIEventListener.Get(buyBtn).onClick = go =>{onFinished();alertPanel.gameObject.SetActive(false);};}}/// <summary>/// 显示确认弹框/// </summary>/// <param name="message"></param>/// <param name="title"></param>/// <param name="onFinished"></param>public static void ShowConfirmPanel(string message, string title, Action<bool> onFinished = null){var rootPanel = GameObject.Find("Panel").transform;var confirmPanel = rootPanel.Find("ConfirmPanel");if (confirmPanel == null){confirmPanel = (Instantiate(Resources.Load("ConfirmPanel")) as GameObject).transform;confirmPanel.gameObject.name = "ConfirmPanel";confirmPanel.SetParent(rootPanel);confirmPanel.localPosition = Vector3.zero;confirmPanel.localScale = Vector3.one;}confirmPanel.Find("Title").GetComponent<UILabel>().text = title;confirmPanel.Find("Content").GetComponent<UILabel>().text = message;confirmPanel.gameObject.SetActive(true);if (onFinished != null){var confirmBtn = confirmPanel.Find("BtnBuy").GetComponent<UIButton>();var cancelBtn = confirmPanel.Find("CancelBuy").GetComponent<UIButton>();UIEventListener.Get(confirmBtn.gameObject).onClick = go =>{onFinished(true);confirmPanel.gameObject.SetActive(false);};UIEventListener.Get(cancelBtn.gameObject).onClick = go =>{confirmPanel.gameObject.SetActive(false);};}}
}

Lua:
lua文件放在对应的Resources下即可
async_buy.lua


local util = require 'xlua.util'
local message_panel = require 'message_panel'-------------------------async_recharge-----------------------------
local function async_recharge(num, cb) --模拟的异步充值print('requst server...')cb(true, num)
endlocal recharge = util.async_to_sync(async_recharge)
-------------------------async_recharge end----------------------------
local buy = function()message_panel.alert("余额提醒","您余额不足,请充值!")if message_panel.confirm("确认充值10元吗?","确认框" ) thenlocal r1, r2 = recharge(10)print('recharge result', r1, r2)message_panel.alert("提示","充值成功!")elseprint('cancel')message_panel.alert("提示","取消充值!")end
endCS.UIEventListener.Get(CS.UnityEngine.GameObject.Find("BtnBuy").gameObject).onClick = util.coroutine_call(buy)

message_panel.lua


local util = require 'xlua.util'local sync_alert = util.async_to_sync(CS.MessagePanel.ShowAlertPanel)
local sync_confirm = util.async_to_sync(CS.MessagePanel.ShowConfirmPanel) --构造alert和confirm函数
return {alert = function(title, message)if not message thentitle, message = message, titleendsync_alert(message,title)end;confirm = function(title, message)local ret = sync_confirm(title,message)return ret == trueend;}

运行的结果就如第一张图所示

后续计划

  • 添加资源热更
  • 添加一个小游戏Demo
  • 添加UGUI的案例

欢迎加群交流

1.unity游戏开发群
QQ群
unity3d unity 游戏开发

2.专门探讨XLua的程序群:437645698


下载地址

https://git.oschina.net/dingxiaowei/Aladdin_XLua.git
Github同步:https://github.com/dingxiaowei/Aladdin_XLua
关注后续更新请点start或者fork,感谢!

更多内容 欢迎访问独立博客 http://dingxiaowei.cn


http://chatgpt.dhexx.cn/article/4vRmOrcO.shtml

相关文章

Xlua

有一个项目做完快上线了,不是lua写的,能热更新的东西就特别少,如果遇到bug也很难在第一时间热修复,所以我就接入了Xlua这个插件点击打开链接 原本只是想热修复一下的,后来领导要求把逻辑系统的C#代码全部换成了Lua,至于为什么,因为他们习惯了每天都更新和修改的开发模式...所以…

xLua热更新(二)实现热更新

一、环境配置 要实现热更新功能&#xff0c;我们首先需要开启热更新的宏。操作方法是在「File->Build Settings->Player Settings->Player->Other Settings->Scripting Define Symbols」选项中添加HOTFIX_ENABLE 开启后&#xff0c;在xLua的菜单中就出现了「…

Unity 热更新技术 |(六)xLua框架学习最新系列完整教程

🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN🙉 🎄 学习专栏推荐:Unity系统学习专栏 🌲 游戏制作专栏推荐:游戏制作 🌲Unity实战100例专栏推荐:Unity 实战100例 教程 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬…

xLua(九)——实战

一&#xff1a;使用xLua的步骤 ——导入xLua插件 其实xLua本质就是一个Unity工程&#xff0c;把Asset中的文件导入到Unity工程中就搞定了&#xff08;导入之后编辑器菜单栏会扩展出一个XLua选项&#xff09; ——添加宏File——Build Settings——Player Settings——Other Se…

【XLua】简单使用

文章目录 前言1 配置1.1 配置宏1.2 XLua配置 2 lua和C#相互调用2.1 XLua.CSharpCallLua2.2 XLua.LuaCallCSharp 3 加载器 前言 XLua本质上是为Unity提供了使用lua的能力&#xff0c;实际多用于热更新。 热更新&#xff0c;因为要给代码打标签才能生效&#xff0c;所以需要预测…

xLua介绍

xLua地址&#xff1a;传送门 Xlua是啥&#xff1f; 2016年 腾讯推出的 一种 unity下 lua 编成的解决方案 基本概念介绍&#xff1a; 1.模块 模块就是一个 程序库&#xff0c;可以通过 require 加载&#xff0c;得到了一个表示 table的全局变量 这个table 就像一个命名空间&am…

Lua快速入门篇(XLua教程)(Yanlz+热更新+xLua+配置+热补丁+第三方库+API+二次开发+常见问题+示例参考)

《Lua热更新》 ##《Lua热更新》发布说明&#xff1a; “Lua热更新”开始了&#xff0c;立钻哥哥终于开始此部分的探索了。 作为游戏发布迭代的重要技术&#xff1a;Lua热更新在网络游戏迭代更新中非常重要&#xff0c;特别是对于AppStore这样的平台&#xff0c;我们只需要定…

XLua加载

XLua加载lua文件的方式 LuaEnv.DoString(print("hello world")); //直接执行lua的语句&#xff0c;在函数体内的语句格式要符合lua的语法 LuaEnv.DoString("require byfile")//使用require lua文件名也可在unity中加载lua 但是在unity中需要把文件放置在…

XLua系列讲解_Helloworld

一、XLua简介 XLua是Unity3D下Lua编程解决方案&#xff0c;自2016年初推广以来&#xff0c;已经应用于十多款腾讯自研游戏&#xff0c;因其良好性能、易用性、扩展性而广受好评。现在&#xff0c;腾讯已经将xLua开源到GitHub。 二、Xlua的优点 简洁易用&#xff0c;容易上手可…

Unity XLua Hotfix热更新配置笔记

Unity XLUA Hotfix热更新配置笔记 目录 Unity XLUA Hotfix热更新配置笔记 配置热更新步骤&#xff1a; 下载XLUA下载压缩包解压 复制xlua 和plugins到assets开启热补丁特性 先添加宏 HOTFIX_ENABLE;INJECT_WITHOUT_TOOL报“This delegate/interface must add to CSharpCallLu…

xLua热更新(一)xLua基本使用

一、什么是xLua xLua为Unity、 .Net、 Mono等C#环境增加Lua脚本编程的能力&#xff0c;借助xLua&#xff0c;这些Lua代码可以方便的和C#相互调用。 xLua是用来实现Lua代码与C#代码相互调用的插件。我们可以借助这个插件来实现热更新方案。 那么为什么要选择Lua实现热更新呢&am…

Bug-CTF-秋名山老司机(正则匹配)

题目: 没有啥思路&#xff0c;意外地刷新了一下页面&#xff0c;发现数值变化了 再刷新一次试试&#xff0c;出来一个提示&#xff0c;大概意思是需要提交结果&#xff0c;这里也不知道该怎么传参&#xff0c;也不晓得怎么写这个脚本&#xff0c;只能参考其他大佬的思路了 解题…

BUGKU------秋名山老司机

看到这个就直接上python吧&#xff0c;用eval计算子式 import requests from bs4 import BeautifulSoup r requests.session() s r.get(http://123.206.87.240:8002/qiumingshan/) soup BeautifulSoup(s.text, "html.parser") a soup.find(div) d {"valu…

bugku秋名山车神

不断的刷新&#xff0c;发现表达式一直在变换&#xff0c;这种必须写脚本&#xff0c;才能跟上速度。直接上代码 import re import requests srequests.session() rs.get("http://123.206.87.240:8002/qiumingshan/") searchObj re.search(r^<div>(.*)\?;<…

【BugkuCTF】Web--秋名山老司机

Description: http://123.206.87.240:8002/qiumingshan/ 是不是老司机试试就知道。 Solution: 打开网页 2秒解决问题真是稳稳的写脚本……但是不知道提交啥&#xff0c;刷新网页看看提示让用POST方式传递一个value变量&#xff0c;构造脚本 import requests import re url htt…

CTF-web-秋名山老司机

前言&#xff1a;小编也是现学现卖&#xff0c;方便自己记忆&#xff0c;写的不好的地方还请包涵&#xff0c;也欢迎各位大佬多多批评指正。 网址&#xff1a;秋名山老司机 1.打开网址&#xff0c;提示需要两秒内计算出数值&#xff0c;手工几乎不可能实现。 2.思路:利用pyt…

秋名山车神

解题思路&#xff1a;看到这种题要在两秒类算出&#xff0c;人工肯定不可能&#xff0c;直接上脚本&#xff0c;由于我不会写python 脚本&#xff0c;直接在网上找了一篇大佬的脚本 import requests #安装requests库 import re url http://114.67.246.176:16847 #改为自己题…

爬虫笔记-Bugku秋名山老司机(入门)

记一次python爬虫笔记 题目&#xff1a;bugku-秋名山老司机 题目要求&#xff1a;两秒内提交一道很长的计算题答案&#xff0c;并且式子每次刷新都会变动 如&#xff1a; 多刷新几次可见题目提示&#xff0c;需要用post传入值&#xff0c;变量名为value 创建py文件&#xf…

ctf靶场-bugku-秋名山老司机,速度要快

页面快速计算(秋名山老司机) 1.靶场网址 http://123.206.87.240:8002/qiumingshan/ 2.脚本实现 import requests import re url"http://123.206.87.240:8002/qiumingshan/" srequests.Session() #储存session rs.get(url) #用此身份执行get请求 searchobjre.sea…

[bugku]-秋名山车神详解

解题 每一次刷新都不一样 post传参value 脚本1 import requests import re url http://114.67.175.224:10053/ s requests.Session() source s.get(url) expression re.search(r(\d[\-*])(\d), source.text).group() result eval(expression) post {value: result} prin…