Unity XLua 官方案例学习

Wesley13
• 阅读 977

1. Helloworld

 1 using UnityEngine;
 2 using XLua;
 3 
 4 public class Helloworld : MonoBehaviour {
 5     // Use this for initialization
 6     void Start () {
 7         LuaEnv luaenv = new LuaEnv();
 8         // 执行代码块,输出 hello world
 9         luaenv.DoString("CS.UnityEngine.Debug.Log('hello world')");
10         // 释放资源
11         luaenv.Dispose();
12     }
13 }

该案例实现了在 Unity 控制台输出 hello world。

2. U3DScripting

  Unity XLua 官方案例学习

  Unity XLua 官方案例学习

lua 代码如下:

 1 local speed = 10
 2 local lightCpnt = nil
 3 
 4 function start()
 5     print("lua start...")
 6     -- 访问环境变量
 7     print("injected object", lightObject)
 8     -- 查找 Light 组件
 9     lightCpnt= lightObject:GetComponent(typeof(CS.UnityEngine.Light))
10 end
11 
12 function update()
13     local r = CS.UnityEngine.Vector3.up * CS.UnityEngine.Time.deltaTime * speed
14     -- 绕y轴旋转
15     self.transform:Rotate(r)
16     -- 修改光线颜色
17     lightCpnt.color = CS.UnityEngine.Color(CS.UnityEngine.Mathf.Sin(CS.UnityEngine.Time.time) / 2 + 0.5, 0, 0, 1)
18 end
19 
20 function ondestroy()
21     print("lua destroy")
22 end

注意,如果要插入中文注释,需要将 txt 编码格式改为 UTF-8,否则无法执行。

  C# 代码如下:

 1 using UnityEngine;
 2 using XLua;
 3 using System;
 4 
 5 [System.Serializable]
 6 public class Injection
 7 {
 8     public string name;
 9     public GameObject value;
10 }
11 
12 [LuaCallCSharp]
13 public class LuaBehaviour : MonoBehaviour {
14     public TextAsset luaScript;             // lua脚本文件
15     public Injection[] injections;          // 需要注入到环境变量的物体
16 
17     internal static LuaEnv luaEnv = new LuaEnv(); //all lua behaviour shared one luaenv only!
18     internal static float lastGCTime = 0;
19     internal const float GCInterval = 1;//1 second 
20 
21     private Action luaStart;
22     private Action luaUpdate;
23     private Action luaOnDestroy;
24 
25     private LuaTable scriptEnv;
26 
27     void Awake()
28     {
29         scriptEnv = luaEnv.NewTable();
30 
31         LuaTable meta = luaEnv.NewTable();
32         meta.Set("__index", luaEnv.Global);
33         scriptEnv.SetMetaTable(meta);
34         meta.Dispose();
35 
36         // 配置环境变量,在lua代码里能直接调用
37         scriptEnv.Set("self", this);
38         foreach (var injection in injections)
39         {
40             scriptEnv.Set(injection.name, injection.value);
41         }
42         // 参数1:Lua代码的字符串
43         // 参数2:发生error时的debug显示信息时使用
44         // 参数3:代码块的环境变量
45         luaEnv.DoString(luaScript.text, "LuaBehaviour", scriptEnv);
46 
47         // 访问函数
48         Action luaAwake = scriptEnv.Get<Action>("awake");
49         scriptEnv.Get("start", out luaStart);
50         scriptEnv.Get("update", out luaUpdate);
51         scriptEnv.Get("ondestroy", out luaOnDestroy);
52 
53         // 执行事件
54         if (luaAwake != null)
55         {
56             luaAwake();
57         }
58     }
59 
60     // Use this for initialization
61     void Start ()
62     {
63         if (luaStart != null)
64         {
65             luaStart();
66         }
67     }
68     
69     // Update is called once per frame
70     void Update ()
71     {
72         if (luaUpdate != null)
73         {
74             luaUpdate();
75         }
76         if (Time.time - LuaBehaviour.lastGCTime > GCInterval)
77         {
78             // 清楚lua未手动释放的LuaBase对象,需定期调用,这里是1s调用一次
79             luaEnv.Tick();
80             LuaBehaviour.lastGCTime = Time.time;
81         }
82     }
83 
84     void OnDestroy()
85     {
86         if (luaOnDestroy != null)
87         {
88             luaOnDestroy();
89         }
90         luaOnDestroy = null;
91         luaUpdate = null;
92         luaStart = null;
93         scriptEnv.Dispose();
94         injections = null;
95     }
96 }

该场景实现了 lua 代码控制 U3D 物体,以实现物体的旋转和颜色变化。

三、UIEvent

   Unity XLua 官方案例学习

  Unity XLua 官方案例学习

lua 代码如下:

1 function start()
2     print("lua start...")
3     -- 给button添加事件
4     -- 点击输出 input 输入内容
5     self:GetComponent("Button").onClick:AddListener(function()
6         print("clicked, you input is '" ..input:GetComponent("InputField").text .."'")
7     end)
8 end

该场景实现了 lua 代码为 button 添加事件响应函数,以实现点击按钮输出输入框内容。

注意,lua 中 . 和 : 的区别:

    • 定义的时候:Class:test() 与 Class.test(self) 是等价的
    • 调用的时候:``object``:test() 与 object``.test(``object``) 等价

   在这里,调用类的方法使用 :,调用属性用 . 。

C# 代码还是上一场景的 LuaBehaviour.cs。

 四、InvokeLua

  C# 代码如下:

 1 using UnityEngine;
 2 using XLua;
 3 
 4 public class InvokeLua : MonoBehaviour
 5 {
 6     [CSharpCallLua]
 7     public interface ICalc
 8     {
 9         int Add(int a, int b);
10         int Mult { get; set; }
11     }
12 
13     [CSharpCallLua]
14     public delegate ICalc CalcNew(int mult, params string[] args);
15 
16     private string script = @"
17                 local calc_mt = {
18                     __index = {
19                         Add = function(self, a, b)
20                             return (a + b) * self.Mult
21                         end
22                     }
23                 }
24 
25                 Calc = {
26                     -- 多参数函数
27                     New = function (mult, ...)
28                         print(...)
29                         return setmetatable({Mult = mult}, calc_mt)
30                     end
31                 }
32             ";
33     // Use this for initialization
34     void Start()
35     {
36         LuaEnv luaenv = new LuaEnv();
37         Test(luaenv);//调用了带可变参数的delegate,函数结束都不会释放delegate,即使置空并调用GC
38         luaenv.Dispose();
39     }
40 
41     void Test(LuaEnv luaenv)
42     {
43         luaenv.DoString(script);
44         // 访问 lua 函数
45         CalcNew calc_new = luaenv.Global.GetInPath<CalcNew>("Calc.New");
46         ICalc calc = calc_new(10, "hi", "john"); //constructor
47         Debug.Log("sum(*10) =" + calc.Add(1, 2));   // (1+2)*10
48         calc.Mult = 100;
49         Debug.Log("sum(*100)=" + calc.Add(1, 2));   // (1+2)*100
50     }
51 }

  该场景实现了 C# 调用 lua 代码的函数,table。注意要加上  [CSharpCallLua] 。

 五、NoGc

  看不懂。

 六、Coroutine

  总共有四个代码文件,关键代码如下。

1. CoroutineTest.cs

1 LuaEnv luaenv = null;
2 // Use this for initialization
3 void Start()
4 {
5     luaenv = new LuaEnv();
6     // 执行 coruntine_test
7     luaenv.DoString("require 'coruntine_test'");
8 }

2. coruntine_test.lua

 1 local util = require 'xlua.util'
 2 
 3 local yield_return = (require 'cs_coroutine').yield_return
 4 
 5 local co = coroutine.create(function()
 6     print('coroutine start!')
 7     local s = os.time()
 8     -- 协程等待3秒
 9     yield_return(CS.UnityEngine.WaitForSeconds(3))
10     print('wait interval:', os.time() - s)
11     
12     local www = CS.UnityEngine.WWW('http://www.cnblogs.com/coderJiebao/p/unity3d22.html')
13     -- 协程加载网页
14     yield_return(www)
15     if not www.error then
16         print(www.bytes)
17     else
18         print('error:', www.error)
19     end
20 end)
21 
22 assert(coroutine.resume(co))

3. cs_coroutine.lua

 1 local util = require 'xlua.util'
 2 
 3 -- 新建物体
 4 local gameobject = CS.UnityEngine.GameObject('Coroutine_Runner')
 5 -- 设置不自动销毁
 6 CS.UnityEngine.Object.DontDestroyOnLoad(gameobject)
 7 -- 添加组件
 8 local cs_coroutine_runner = gameobject:AddComponent(typeof(CS.Coroutine_Runner))
 9 
10 local function async_yield_return(to_yield, cb)
11     cs_coroutine_runner:YieldAndCallback(to_yield, cb)        -- 调用 C# 函数
12 end
13 
14 return {
15     yield_return = util.async_to_sync(async_yield_return)
16 }

4. Coroutine_Runner.cs

 1 [LuaCallCSharp]
 2 public class Coroutine_Runner : MonoBehaviour
 3 {
 4     public void YieldAndCallback(object to_yield, Action callback)
 5     {
 6         // 开启协程,回调callback
 7         StartCoroutine(CoBody(to_yield, callback));
 8     }
 9 
10     private IEnumerator CoBody(object to_yield, Action callback)
11     {
12         if (to_yield is IEnumerator)
13             yield return StartCoroutine((IEnumerator)to_yield);
14         else
15             yield return to_yield;
16         callback();
17     }
18 }

该场景实现了协程等待3s和加载网页的功能。

调用流程为:CoroutineTest.Start -> coruntine_test(创建协程,调用 yield_return 方法)-> cs+coroutine.async_yield_return -> Coroutine_Runner.YieldAndCallback。

七、AsyncTest

继续看不懂,后期补上。

八、Hotfix

1. 使用方式

(1) 在 github 上下载 xlua 源码后,将 Asserts 文件夹内的文件以及 Tools 文件夹直接拖到工程,这时候会报错,删除 Tools 文件夹下的 System.dll 和 System.core.dll 即可。

(2) 添加 HOTFIX_ENABLE 和 INJECT_WITHOUT_TOOL 两个宏(在 File->Build Setting->Player Setting->Scripting Define Symbols)

(3) 执行XLua/Generate Code菜单

(4) 编写代码,注意在需要热更新的地方添加[Hotfix]标签

(5) 注入,构建手机包这个步骤会在构建时自动进行,编辑器下开发补丁需要手动执行"XLua/Hotfix Inject In Editor"菜单。注入成功会打印“hotfix inject finish!”或者“had injected!”。

2. 常用函数

xlua.hotfix(class, [method_name], fix)
  • 描述 : 注入lua补丁
  • class : C#类,两种表示方法,CS.Namespace.TypeName或者字符串方式"Namespace.TypeName",字符串格式和C#的Type.GetType要求一致,如果是内嵌类型(Nested Type)是非Public类型的话,只能用字符串方式表示"Namespace.TypeName+NestedTypeName";
  • method_name : 方法名,可选;
  • fix : 如果传了method_name,fix将会是一个function,否则通过table提供一组函数。table的组织按key是method_name,value是function的方式。
xlua.private_accessible(class)
  • 描述 : 让一个类的私有字段,属性,方法等可用
  • class : 同xlua.hotfix的class参数
util.hotfix_ex(class, method_name, fix)
  • 描述 : xlua.hotfix的增强版本,可以在fix函数里头执行原来的函数,缺点是fix的执行会略慢。
  • method_name : 方法名;
  • fix : 用来替换C#方法的lua function。
base(csobj)
  • 描述:子类 override 函数通过 base 调用父类实现
  • csobj:对象
  • 返回值:新对象

3. 打补丁

xlua 可以用 lua 函数替换 C# 的构造函数,函数,属性,事件的替换。

(1) 函数

可以指定一个函数,也可以传递由多个函数组成的 table。

1 -- 注入lua补丁,替换HotfixCalc.Add
2 xlua.hotfix(CS.HotfixCalc, 'Add', function(self, a, b)
3     -- 原来为 a-b
4     return a + b
5 end)

 1 -- 通过table提供一组函数
 2 -- table的组织按key是methodname,value是function的方式
 3 xlua.hotfix(CS.HotfixCalc, {
 4         Test1 = function(self)
 5         print('Test1', self)
 6         return 1
 7         end;
 8         Test2 = function(self, a, b)
 9             print('Test2', self, a, b)
10             return a + 10, 1024, b
11         end;
12         -- static 函数不需要加self
13         Test3 = function(a)
14         print(a)
15         return 10
16         end;
17         Test4 = function(a)
18         print(a)
19         end;
20         -- 多参数
21         Test5 = function(self, a, ...)
22         print('Test4', self, a, ...)
23         end
24 })

  (2) 构造函数

构造函数对应的 method_name 是 ".ctor",和普通函数不一样的是,构造函数的热补丁并不是替换,而是执行原有逻辑后调用 lua。

1 -- 构造函数
2 ['.ctor'] = function(csobj)
3     return {evt = {}, start = 0}
4 end;

(3) 属性

每一个属性都对应一个get,set函数。

1 -- 属性AProp的赋值和取值
2 set_AProp = function(self, v)
3     print('set_AProp', v)
4     self.AProp = v
5 end;
6 get_AProp = function(self)
7     return self.AProp
8 end;

  (4) [] 操作符

赋值对应 set_Item,取值对应 set_Item。

1 -- []操作符,赋值和取值
2 get_Item = function(self, k)
3     print('get_Item', k)
4     return 1024
5 end;
6 set_Item = function(self, k, v)
7     print('set_Item', k, v)
8 end;

对于其他操作符,C#的操作符都有一套内部表示,比如+号的操作符函数名是op_Addition。

(5) 事件

+= 操作符是 add_...,-= 操作符是 remove_... ,函数第一个参数是自身,第二个参数是操作符右边的 delegate。

 1 -- 事件AEvent +=
 2 add_AEvent = function(self, cb)
 3     print('add_AEvent', cb)
 4     table.insert(self.evt, cb)
 5 end;
 6 -- 事件AEvent -=
 7 remove_AEvent = function(self, cb)
 8     print('remove_AEvent', cb)
 9     for i, v in ipairs(self.evt) do
10         if v == cb then
11             table.remove(self.evt, i)
12             break
13         end
14     end
15 end;

(6) 析构函数

函数名是 Finalize,传一个 self 参数。和普通函数不一样的是,析构函数的热补丁并不是替换,而是开头调用 lua 函数后继续原有逻辑。

1 -- 析构函数
2 Finalize = function(self)
3     print('Finalize', self)
4 end

(7) 泛化类型

每个泛化类型都是一个独立的类型,需要对实例化后的类型分别打补丁。

 1 xlua.hotfix(CS['GenericClass`1[System.Double]'], {
 2     ['.ctor'] = function(obj, a)
 3         print('GenericClass<double>', obj, a)
 4     end;
 5     Func1 = function(obj)
 6         print('GenericClass<double>.Func1', obj)
 7     end;
 8     Func2 = function(obj)
 9         print('GenericClass<double>.Func2', obj)
10         return 1314
11     end
12 })

(8) 子类调用父类

1 -- 子类调用父类的方法
2 xlua.hotfix(CS.BaseTest, 'Foo', function(self, p)
3         print('BaseTest', p)
4         base(self):Foo(p)
5     end)
6 xlua.hotfix(CS.BaseTest, 'ToString', function(self)
7         return '>>>' .. base(self):ToString()
8     end)
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
2年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这