Unity3D熱更新之LuaFramework篇[04]--自定義UI監聽方法
時隔一個多月我又回來啦!
堅持真的是很難的一件事,其它事情稍忙,就很容易說服自己把寫部落格的計劃給推遲了。
好在終於克服了自己的惰性,今天又開始了。
本篇繼續我的Luaframework學習之路。
一、規範開發模式
此前的示例中,動態載入的panel都預設以GuiCamera為父節點,且面板的大小設定得有些隨意,為方便後續開發,現做一些調整和規範。
1、設定本專案的開發解析度為1334x750(Game檢視解析度也設定為這個大小);
2、調整相機,將原有的GuiCamera從Canvas下拖離出來(與Canvas並列),並做如下設定:
(1)Canvas的RenderMode為Screen Space-Camera,並指定Render Camera為GuiCamera;
(2)設定GUICamera的投射模式(Projection)為正交(Orthogratphic);
(3)設定Size為3.75(3.75 = 750 / 100 /2),這樣Canvas的Scale就會為0.01;
(4)設定相機的Culling Mask為Everything。
設定效果見下圖:
3、調整動態載入panel的父節點為Canvas。
找到Assets/LuaFramework/Scipts/Manager下的PanelManager.cs指令碼,找到14行,將本行的
GameObject go = GameObject.FindWithTag("GuiCamera");
修改為:
GameObject go = GameObject.Find("Canvas");
如下圖,這樣動態載入的panel就會以Canvas為父節點。
二、新建一個登陸面板
為了增加學習代入感,後續演示將會以登陸場景和大廳場景為示例,文章展示的所有功能,都圍繞這兩個場景展開。此處先建立一個登陸面板。
1、建立一個登陸面板
建立一個登陸面板,結構層級如下所示,並做成預製體,新增打包。
這一過程詳細做法請參見:Unity3D熱更新之LuaFramework篇[02]--用Lua建立自己的面板
2、建立Login相關指令碼
建立LoginPanel相應的Lua指令碼並設定為首先載入。
在建立LoginCtrl.lua和LoginView.lua指令碼的時候,要注意在Controller和View下額外加一層目錄login,以做模組化管理。
然後在Require這兩個指令碼的時候,也要包含login目錄:
在CtrlManager.lua頭部引用時使用:require "Controller/Login/LoginCtrl";
在define.lua中定義PanelName時使用 "Login/LoginPanel"
PanelNames = { "PromptPanel", "MessagePanel", "FirstPanel", "Login/LoginPanel" }上述一切步驟完成後,運行遊戲就能直接加載出LoginPanel面板了。
三、新增UI監聽
根據此前的經驗,新增監聽有兩個步驟:
1、在LoginView中引用相關元件;
--初始化面板--
function LoginPanel.InitPanel()
--賬號輸入框
LoginPanel.accountInput = transform:FindChild("AccountInput").gameObject;
--密碼輸入框
LoginPanel.passwordInput = transform:FindChild("PwdInput").gameObject;
--登陸按鈕
LoginPanel.loginBtn = transform:FindChild("LoginButton").gameObject;
--記住密碼
LoginPanel.savePwdToggle = transform:FindChild("Toggle").gameObject;
end2、在LoginCtrl中新增事件處理函式;
登陸介面有三種需要互動的元素,一個是按鈕,一個是輸入框,一個是複選框(Toggle)。
按鈕(Button)事件的新增,我們之前有過介紹,是通過LuaBehaviour的AddClick方法實現的,如之前製作的FirstPanel面板的關閉按鈕,見下圖。
輸入框元件(InputField),如果只是需要獲取輸入值的話,不用新增監聽,到找元件並引用,取元件的text值就好了;如果需要在輸入結束時做一個操作(如判斷使用者名稱是否符合規則,註冊時會有此需求),則需要給輸入框新增相應監聽;
複選框元件(Toggle),這是一個時實互動元件,需要動態的取Toggle的值,因此需要新增監聽以判斷當前的選擇狀態。
既然按鈕可以通過LuaBehaviour指令碼新增監聽,那麼對於Toggle和InputField的監聽需是否也可以通過此指令碼實現呢?
LuaBehaviour.cs指令碼位於Assets\LuaFramework\Scripts\Common目錄下,開啟後能看到,此指令碼的包含有新增按鈕監聽的方法AddClick,本質是在傳遞過來的GameObject上查詢Button元件,並新增一個委託回撥。如下圖:
此指令碼中,還包含了對Lua指令碼的驅動方法(xxxPanel.lua指令碼中Awake,Start方法被呼叫,應該就是被LuaBehaviour呼叫的),以及RemoveClick、ClearClick方法。
但是,並沒有能給Toggle和InputField元件新增監聽的方法。
這個框架真的是有點簡單啊。
不過我們既然知道Button元件是怎麼實現監聽的,其它元件依照著新增一個就行了。
在LuaBehaviour指令碼中新增對Toggle的監聽方法,如下:
/// <summary> /// 給Toggle元件新增監聽 /// </summary> public void AddToggle(GameObject go, LuaFunction luafunc) { if (go == null || luafunc == null) return; buttons.Add(go.name, luafunc); go.GetComponent<Toggle>().onValueChanged.AddListener( delegate (bool select) { luafunc.Call(go, select); } ); }在LuaBehaviour給輸入元件(InputField)新增結束編輯(OnEndEdit)監聽,如下:
//給輸入元件(InputField)新增結束編輯(OnEndEdit)監聽 public static void AddInputFieldEndEditHandler(GameObject go, LuaFunction luafunc) { if (go == null || luafunc == null) return; InputField input = go.GetComponent<InputField>(); if (input == null) { Debug.LogError(go.name + "找不到InputField元件"); return; } go.GetComponent<InputField>().onEndEdit.AddListener( delegate (string text) { luafunc.Call(text); } ); }寫法還是很簡單的,本質就是呼叫C#中對相應元件的處理,封裝成方法,通過LuaBehaviour指令碼以使其能在Lua指令碼中被使用。
現在,在LoginCtrl.lua中給賬號輸入框新增一個編輯結束事件處理(實際並不需要,這裡只是做演示),給記住密碼的複選框新增一個狀態變化事件處理。
behaviour.AddInputFieldEndEditHandler(LoginPanel.accountInput, function (account)
log("賬號輸入結束,賬號" .. account);
end);
behaviour:AddToggle(LoginPanel.savePwdToggle, function (go, toggleVal)
log("記住密碼:" .. tostring(toggleVal));
end);然後執行,會報錯,提示 AddInputFieldEndEditHandler 方法不存在。
原因是剛剛改動的c#指令碼並未生效,這涉及到c#型別到Lua的對映問題,以後再細述。
目前的解決辦法是,點選“Lua/Generate All”選單。
等待Generate 過程結束後,再點選執行,一切正常。
測試InputField的編輯結束事件以及Toggle的狀態變化事件,達到預期效果,見下圖:
自定義新增UI監聽事件就是這麼簡單。
四、後記
1、除了Toggle和InputField的事件外,其它的元件如Slider、Scroll Bar、Scroll View等,都可照此例新增。
2、上一步在LuaBehaviour中添加了兩個方法:AddToggle和AddInputFieldEndEditHandler,但是沒有實現相關的移除方法,需要自己完善。
3、考慮到功能單一原責,LuaBehaviour最好只包含Behaviour(指令碼生命週期)相關的功能,而新增UI監聽的功能最好能抽離到一個單獨的類中實現,下一篇將會講這個。
4、lua中呼叫C#函式有點"."呼叫和冒號":"呼叫的區別,見上文LoginCtrl中AddToggle和AddInputFieldEndEditHandler的使用方法。如下圖:
為什麼會有不同的用法,是因為, 在LuaBehaviour中新增相應方法的時候做了區別(為了演示),AddToggle是成員方法,AddInputFieldEndEditHandler是靜態方法。
總結就是:成員方法呼叫用冒號,靜態方法呼叫用點號。
本文相關指令碼程式碼如下:
using UnityEngine; using LuaInterface; using System.Collections; using System.Collections.Generic; using System; using UnityEngine.UI; namespace LuaFramework { public class LuaBehaviour : View { private string data = null; private Dictionary<string, LuaFunction> buttons = new Dictionary<string, LuaFunction>(); protected void Awake() { Util.CallMethod(name, "Awake", gameObject); } protected void Start() { Util.CallMethod(name, "Start"); } protected void OnClick() { Util.CallMethod(name, "OnClick"); } protected void OnClickEvent(GameObject go) { Util.CallMethod(name, "OnClick", go); } /// <summary> /// 新增單擊事件 /// </summary> public void AddClick(GameObject go, LuaFunction luafunc) { if (go == null || luafunc == null) return; buttons.Add(go.name, luafunc); go.GetComponent<Button>().onClick.AddListener( delegate() { luafunc.Call(go); } ); } /// <summary> /// 給Toggle元件新增監聽 /// </summary> public void AddToggle(GameObject go, LuaFunction luafunc) { if (go == null || luafunc == null) return; buttons.Add(go.name, luafunc); go.GetComponent<Toggle>().onValueChanged.AddListener( delegate (bool select) { luafunc.Call(go, select); } ); } //給輸入元件(InputField)新增結束編輯(OnEndEdit)監聽 public static void AddInputFieldEndEditHandler(GameObject go, LuaFunction luafunc) { if (go == null || luafunc == null) return; InputField input = go.GetComponent<InputField>(); if (input == null) { Debug.LogError(go.name + "找不到InputField元件"); return; } go.GetComponent<InputField>().onEndEdit.AddListener( delegate (string text) { luafunc.Call(text); } ); } /// <summary> /// 刪除單擊事件 /// </summary> /// <param name="go"></param> public void RemoveClick(GameObject go) { if (go == null) return; LuaFunction luafunc = null; if (buttons.TryGetValue(go.name, out luafunc)) { luafunc.Dispose(); luafunc = null; buttons.Remove(go.name); } } /// <summary> /// 清除單擊事件 /// </summary> public void ClearClick() { foreach (var de in buttons) { if (de.Value != null) { de.Value.Dispose(); } } buttons.Clear(); } //----------------------------------------------------------------- protected void OnDestroy() { ClearClick(); #if ASYNC_MODE string abName = name.ToLower().Replace("panel", ""); ResManager.UnloadAssetBundle(abName + AppConst.ExtName); #endif Util.ClearMemory(); Debug.Log("~" + name + " was destroy!"); } } }LuaBehaviour.cs--- --- Generated by EmmyLua(https://github.com/EmmyLua) --- Created by Tan. --- DateTime: 2019/6/1 18:25 --- local transform; local gameObject; LoginPanel = {}; local this = LoginPanel; --啟動事件-- function LoginPanel.Awake(obj) gameObject = obj; transform = obj.transform; this.InitPanel(); logWarn("Awake lua--->>"..gameObject.name); end --初始化面板-- function LoginPanel.InitPanel() --賬號輸入框 LoginPanel.accountInput = transform:FindChild("AccountInput").gameObject; --密碼輸入框 LoginPanel.passwordInput = transform:FindChild("PwdInput").gameObject; --登陸按鈕 LoginPanel.loginBtn = transform:FindChild("LoginButton").gameObject; --記住密碼 LoginPanel.savePwdToggle = transform:FindChild("Toggle").gameObject; end --單擊事件-- function LoginPanel.OnDestroy() logWarn("OnDestroy---->>>"); endLoginPanel.lua--- --- Generated by EmmyLua(https://github.com/EmmyLua) --- Created by Tan. --- DateTime: 2019/6/1 18:25 --- LoginCtrl = {}; local this = LoginCtrl; local behaviour; local transform; local gameObject; --構建函式-- function LoginCtrl.New() logWarn("LoginCtrl.New--->>"); return this; end function LoginCtrl.Awake() logWarn("LoginCtrl.Awake--->>"); panelMgr:CreatePanel('Login', this.OnCreate); end --啟動事件-- function LoginCtrl.OnCreate(obj) gameObject = obj; transform = obj.transform; behaviour = gameObject:GetComponent('LuaBehaviour'); behaviour:AddClick(LoginPanel.loginBtn, function () log("你點選了登陸"); end); behaviour.AddInputFieldEndEditHandler(LoginPanel.accountInput, function (account) log("賬號輸入結束,賬號" .. account); end); behaviour:AddToggle(LoginPanel.savePwdToggle, function (go, toggleVal) log("記住密碼:" .. tostring(toggleVal)); end); end --單擊事件-- function LoginCtrl.OnClick(go) destroy(gameObject); end --關閉事件-- function LoginCtrl.Close() panelMgr:ClosePanel(CtrlNames.Login); endLoginCtrl.lua