Unity3D熱更新之LuaFramework篇[05]--Lua指令碼呼叫c#以及如何在Lua中使用Dotween

子非魚`發表於2019-06-15

 在上一篇文章 Unity3D熱更新之LuaFramework篇[04]--自定義UI監聽方法 中,我對LuaBehaviour指令碼進行了擴充套件,新增了兩個新的UI監聽方法,也提到最好能單寫一個指令碼處理此事。本篇文章就來繼續這個工作。

 

從Lua中呼叫C#程式碼

1、建立UI監聽指令碼

 開啟之前的工程,在Assets/LuaFrameworks/Scripts/Common下,建立一個UIEventEx.cs指令碼,將LuaBehaviour.cs中的AddButtonClick以及AddInputFieldEndEditHandler方法遷移過來,並擴充套件了一些其它方法,程式碼如下:

  1 using LuaInterface;
  2 using System.Collections;
  3 using System.Collections.Generic;
  4 using UnityEngine;
  5 using UnityEngine.EventSystems;
  6 using UnityEngine.UI;
  7 
  8 /// <summary>
  9 /// 自定義的新增UI監聽的方法,可以用lua中呼叫以做事件繫結
 10 /// </summary>
 11 public class UIEventEx {
 12     //新增監聽
 13     public static void AddButtonClick(GameObject go, LuaFunction luafunc)
 14     {
 15         if (go == null || luafunc == null)
 16             return;
 17 
 18         Button btn = go.GetComponent<Button>();
 19         if (btn == null)
 20             return;
 21 
 22         btn.onClick.AddListener
 23         (
 24             delegate ()
 25             {
 26                 luafunc.Call(go);
 27             }
 28         );
 29     }
 30 
 31     //新增監聽(外帶資料中轉功能)
 32     public static void AddButtonClick(GameObject go, LuaFunction luafunc, LuaTable luatable)
 33     {
 34         if (go == null || luafunc == null)
 35             return;
 36 
 37         Button btn = go.GetComponent<Button>();
 38         if (btn == null)
 39             return;
 40 
 41         btn.onClick.AddListener
 42         (
 43             delegate ()
 44             {
 45                 luafunc.Call(go, luatable);
 46             }
 47         );
 48     }
 49 
 50     /// <summary>
 51     /// 給Toggle元件新增監聽
 52     /// </summary>
 53     public static void AddToggle(GameObject go, LuaFunction luafunc, LuaTable luatable)
 54     {
 55         if (go == null || luafunc == null) return;
 56 
 57         Toggle toggle = go.GetComponent<Toggle>();
 58 
 59         if (toggle == null) return;
 60 
 61         go.GetComponent<Toggle>().onValueChanged.AddListener(
 62             delegate (bool select) {
 63                 luafunc.Call(luatable, select);
 64             }
 65         );
 66     }
 67 
 68 
 69     /// <summary>
 70     /// 給Toggle元件新增監聽
 71     /// </summary>
 72     public static void AddToggle(GameObject go, LuaFunction luafunc)
 73     {
 74         if (go == null || luafunc == null) return;
 75 
 76         Toggle toggle = go.GetComponent<Toggle>();
 77 
 78         if (toggle == null) return;
 79 
 80         go.GetComponent<Toggle>().onValueChanged.AddListener(
 81             delegate (bool select) {
 82                 luafunc.Call(select);
 83             }
 84         );
 85     }
 86 
 87     //給輸入元件(InputField)新增結束編輯(OnEndEdit)監聽
 88     public static void AddInputFieldEndEditHandler(GameObject go, LuaFunction luafunc)
 89     {
 90         if (go == null || luafunc == null) return;
 91 
 92         InputField input = go.GetComponent<InputField>();
 93 
 94         if (input == null)
 95         {
 96             Debug.LogError(go.name + "找不到InputField元件");
 97             return;
 98         }
 99 
100         go.GetComponent<InputField>().onEndEdit.AddListener(
101             delegate (string text) {
102                 luafunc.Call(text);
103             }
104         );
105     }
106 
107     /// <summary>
108     /// 新增對游標按下|抬起事件的支援
109     /// </summary>
110     /// <param name="go">目標物件</param>
111     /// <param name="luafunc">按下事件</param>
112     /// <param name="luafunc2">抬起事件</param>
113     public static void AddPointerDownUpSupport(GameObject go, LuaFunction luafunc, LuaFunction luafunc2)
114     {
115         if (go == null) return;
116 
117         EventsSupport es = go.AddComponent<EventsSupport>();
118 
119         es.InitDownUpHandler((PointerEventData pointerEventData) => {
120             if (luafunc != null)
121             {
122                 luafunc.Call(go, pointerEventData);
123             }
124 
125         }, (PointerEventData pointerEventData) => {
126             if (luafunc2 != null)
127             {
128                 luafunc2.Call(go, pointerEventData);
129             }
130         });
131     }
132 
133     /// <summary>
134     /// 給Slider元件新增onValueChanged事件
135     /// </summary>
136     /// <param name="go"></param>
137     /// <param name="luafunc"></param>
138     public static void AddSliderOnChangeEvent(GameObject go, LuaFunction luafunc)
139     {
140         if (go == null || luafunc == null) return;
141 
142         Slider component = go.GetComponent<Slider>();
143 
144         if (component == null)
145         {
146             Debug.LogError(go.name + "找不到Slider元件");
147             return;
148         }
149 
150         go.GetComponent<Slider>().onValueChanged.AddListener(
151             delegate (float val) {
152                 luafunc.Call(val);
153             }
154         );
155     }
156 
157     //清除監聽
158     public static void ClearButtonClick(GameObject go)
159     {
160         if (go == null)
161             return;
162 
163         Button btn = go.GetComponent<Button>();
164         if (btn == null)
165             return;
166 
167         btn.onClick.RemoveAllListeners();
168     }
169 
170 }
UIEventEx.cs

在Assets/LuaFrameworks/Scripts/Common下,建立一個EventsSupport.cs指令碼,該指令碼是一個實現了IPointerDownHandler, IPointerUpHandler等介面的類,用於在Lua中檢測滑鼠輸入(滑鼠點選,抬起、按下等功能),配合UIEventEx.cs中的AddPointerDownUpSupport方法使用。其程式碼如下:

 1 using System;
 2 using System.Collections;
 3 using System.Collections.Generic;
 4 using UnityEngine;
 5 using UnityEngine.EventSystems;
 6 
 7 /*  其它事件可根據需要在此類中實現
 8     IPointerEnterHandler - OnPointerEnter - Called when a pointer enters the object
 9     IPointerExitHandler - OnPointerExit - Called when a pointer exits the object
10     IPointerDownHandler - OnPointerDown - Called when a pointer is pressed on the object
11     IPointerUpHandler - OnPointerUp - Called when a pointer is released (called on the original the pressed object)
12     IPointerClickHandler - OnPointerClick - Called when a pointer is pressed and released on the same object
13     IInitializePotentialDragHandler - OnInitializePotentialDrag - Called when a drag target is found, can be used to initialise values
14     IBeginDragHandler - OnBeginDrag - Called on the drag object when dragging is about to begin
15     IDragHandler - OnDrag - Called on the drag object when a drag is happening
16     IEndDragHandler - OnEndDrag - Called on the drag object when a drag finishes
17     IDropHandler - OnDrop - Called on the object where a drag finishes
18     IScrollHandler - OnScroll - Called when a mouse wheel scrolls
19     IUpdateSelectedHandler - OnUpdateSelected - Called on the selected object each tick
20     ISelectHandler - OnSelect - Called when the object becomes the selected object
21     IDeselectHandler - OnDeselect - Called on the selected object becomes deselected
22     IMoveHandler - OnMove - Called when a move event occurs (left, right, up, down, ect)
23     ISubmitHandler - OnSubmit - Called when the submit button is pressed
24     ICancelHandler - OnCancel - Called when the cancel button is pressed
25  */
26 
27 /// <summary>
28 /// unity事件支援(本類用於實現Unity中的各種事件,借給Lua呼叫)
29 /// </summary>
30 public class EventsSupport : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
31 {
32     Action<PointerEventData> onPointerDownHandler = null;
33     Action<PointerEventData> onPointerUpHandler = null;
34 
35     public void InitDownUpHandler (Action<PointerEventData> downHandler, Action<PointerEventData> upHandler)
36     {
37         onPointerDownHandler = downHandler;
38         onPointerUpHandler = upHandler;
39     }
40 
41     public void OnPointerDown(PointerEventData pointerEventData)
42     {
43         //Output the name of the GameObject that is being clicked
44         //Debug.Log("[" + name + "] Game Object Click in Progress");
45         
46         if (onPointerDownHandler != null) {
47             onPointerDownHandler(pointerEventData);
48         }
49     }
50 
51     //Detect if clicks are no longer registering
52     public void OnPointerUp(PointerEventData pointerEventData)
53     {
54         //Debug.Log("[" + name + "] No longer being clicked");
55         if (onPointerUpHandler != null)
56         {
57             onPointerUpHandler(pointerEventData);
58         }
59     }
60 }
EventsSupport.cs

EventsSupport.cs指令碼需要掛在待檢測輸入的Game Object上。

 

2、使用指令碼

這裡還是以上一篇文章寫的登陸介面為例,之前我們是通過LuaBehaviour給Button、Toggle以及InputField新增的監聽函式,現在將相應的用法直接替換掉。

替換前:

替換後:

 

然後執行,看是否生效。

......

執行結果,報錯了,提示全域性變數 UIEventEx為nil(就是這個變數不存在的意思)

  看來這樣想當然的方法是行不通了,我們不能建立一個C#指令碼,然後在Lua中直接使用它。

 

3、C#類匯出

在上一步中,我們發現無法直接在Lua中使用建立的C#指令碼。通過查閱資料瞭解到,對自定義的c#類,如果想在Lua中使用的話,需要做一個匯出操作才行。

ToLua的官方git上也有相關的說明:

跟著說明操作:

1)找到Assets\LuaFramework\Editor\下的CustomSettings.cs指令碼; 

2)在CustomSettings的60 行左右照例新增一個匯出語句"_GT(typeof(UIEventEx)),";

 

3)點選Lua/Generate All選單,等到日誌列印 Generate LuaBinder over !字樣時,表明Generate操作已經完成了。

此時檢視Assets\LuaFramework\ToLua\Source\Generate,能找到一個叫UIEventExWrap的cs檔案,這個就是UIEventEx的匯出類。

 

4)重新執行Unity,已經不再報錯了,點選Button、Toggle、在InputField中輸入字元,功能都和之前使用LuaBehaviour時一致。

 

總結

如果想在Lua中使用自定義的c#類,需要4個步驟:

1)建立c#指令碼;

2)在CustomSetting.cs中新增匯出語句;

3)點選Lua/Generate All選單;

4)在Lua中以全域性變數的形式直接使用;

 

這裡涉及的轉化過程是這樣的:

1)UIEventEx指令碼通過Lua/Generate All選單生成UIEventWrap指令碼;

2)UIEventWrap指令碼經過ToLua的作用,最終成為Lua中的一個全域性變數UIEventEx

 

在之前的文章中我們曾直接使用Lua/Generate All選單而未做解釋,那麼現在你應該明白它的做用是什麼了。

至於ToLua怎麼把一個XxxWrap轉換為Lua中的全域性變數,就不是本文能講得清的了(你可以自己做弄清楚);

 

怎麼在Lua中使用Dotween

Dotween作為一款非常優秀的緩動動畫外掛,基本上快成為Unity的標配了。而如果想把所有的UI邏輯全部Lua化,那麼在Lua中使用Dotween就是必須的了。

根據前邊的經驗,Dotween相關的類對於ToLua來說,就是自定義類,想要在Lua中使用,就必須做匯出操作。

那麼就有以下步驟:

1)給專案匯入一個Dotween外掛;

2)匯出Dotween相關類;

第二步就是要整理Dotween相關的類,然後一個個寫匯出語句,這不是一個簡單的活兒。

不過不必擔心,ToLua已經幫我們做好了。

開啟Assets\LuaFramework\Editor\下的CustomSettings.cs指令碼,在70~100行左右,能看到Dotween相關類的匯出語句,不過由於未檢測到USING_DOTWEENING巨集定義的原因,這一段程式碼並未生效。

 3)使用巨集定義USING_DOTWEENING

 一個簡單的定義巨集的辦法是在指令碼頭部加入 #define USING_DOTWEENING語句,如下圖

另外一個辦法是在PlayerSettings的Scripting Define Symbols*下新增相應的巨集,如下圖: 

其中ASYNC_MOD是之前有的,兩個巨集之間用分號隔開,輸入USING_DOTWEENING 要回車一次,讓指令碼重新編譯。

這裡使用第一種辦法。

定義了巨集之後,Dotween相關類的匯出語句就生效了,然後要執行一次Lua/Generate All。

4)在Lua中作用Dotween

 以登陸介面的登陸按鈕為例,在LoginPanel.lua指令碼中新增如下的Dotween使用方法。 

然後執行,能看到動畫已經生效了(移動及迴圈都沒問題),不過最後的回撥沒執行。

看日誌有一個報錯,說的是TweenCallback未註冊。這個就是OnComplete回撥未執行的原因。

 TweenCallback是一個委託型別,根據此前了知識,委託型別也需要在CustomSetting中指定位置註冊。

開啟CustomSettings指令碼,在40~50行左右的位置,新增TweenCallback的匯出語句"_DT(typeof(DG.Tweening.TweenCallback)),",如下圖所示:

之後重新執行Lua/Generate All選單(如果有報錯,可先執行一次Clear再執行Generate All)。

現在將迴圈次數改為1,重新執行。

能看到動畫停止後,指定的日誌已經輸出。

在Lua中使用用Dotween,就是這樣一個步驟。

 

 有一點要注意的是,在Lua中的程式碼提示是很不健全的,特別是在呼叫用C#指令碼的時候。

這裡寫Dotween動畫的程式碼就是全靠經驗,如果不熟的話,也可以先用C#寫一遍,再搬到Lua中改造。

 

怎麼從C#中呼叫Lua指令碼

文章的前半部分介紹了Lua中呼叫c#的方法,那麼相應的如何從c#中呼叫Lua也有必要了解一下。

c#呼叫Lua是比較少的一個操作,基本上就在框架(LuaFramework)初始化的時候有用到。這裡不做詳細案例,只講一下了解方式。

方式1:

ToLua的Examples, 03_CallLuaFunction,這個指令碼詳細講述了c#呼叫Lua的過程。

方式2:

LuaFramework的LuaManager類,這個指令碼里有詳細的呼叫Main.Lua的過程。

 

 

後記 

一個疑問:

在寫Lua的Dotween程式碼的時候,使用DOLocalMove、SetLoops和OnComplete都是用冒號:的方式,實際上這三個都是static方法,這有違於上一篇文章中總結的靜態方法用點號,成員方法用冒號的規則。

暫不知道原因,如果你知道,還請留言指教。

 

相關文章