在上一篇文章 Unity3D熱更新之LuaFramework篇[04]--自定義UI監聽方法 中,我對LuaBehaviour指令碼進行了擴充套件,新增了兩個新的UI監聽方法,也提到最好能單寫一個指令碼處理此事。本篇文章就來繼續這個工作。
從Lua中呼叫C#程式碼
1、建立UI監聽指令碼
開啟之前的工程,在Assets/LuaFrameworks/Scripts/Common下,建立一個UIEventEx.cs指令碼,將LuaBehaviour.cs中的AddButtonClick以及AddInputFieldEndEditHandler方法遷移過來,並擴充套件了一些其它方法,程式碼如下:
UIEventEx.cs1 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 }在Assets/LuaFrameworks/Scripts/Common下,建立一個EventsSupport.cs指令碼,該指令碼是一個實現了IPointerDownHandler, IPointerUpHandler等介面的類,用於在Lua中檢測滑鼠輸入(滑鼠點選,抬起、按下等功能),配合UIEventEx.cs中的AddPointerDownUpSupport方法使用。其程式碼如下:
EventsSupport.cs1 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指令碼需要掛在待檢測輸入的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方法,這有違於上一篇文章中總結的靜態方法用點號,成員方法用冒號的規則。
暫不知道原因,如果你知道,還請留言指教。