zoukankan      html  css  js  c++  java
  • Unity XLua 官方案例学习

    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

      

      

      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

       

      

      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)
  • 相关阅读:
    C++ 类
    百练3383:Cell Phone Network
    百练4092:牛仔裤
    百练4091:最近餐馆
    百练4090:超级备忘录
    百练2868:牛顿迭代
    百练2812:恼人的青蛙
    百练2805:正方形
    bzoj1856 [Scoi2010]字符串
    bzoj2096 [Poi2010]Pilots
  • 原文地址:https://www.cnblogs.com/coderJiebao/p/unity3d23.html
Copyright © 2011-2022 走看看