Unity3D熱更新之LuaFramework篇[04]--自定義UI監聽方法 Unity3D熱更新之LuaFramework篇[02]--用Lua建立本身的面板

時隔一個多月我又回來啦!html

堅持真的是很難的一件事,其它事情稍忙,就很容易說服本身把寫博客的計劃給推遲了。git

好在終於克服了本身的惰性,今天又開始了。github

本篇繼續個人Luaframework學習之路。c#

 1、規範開發模式

此前的示例中,動態加載的panel都默認以GuiCamera爲父節點,且面板的大小設置得有些隨意,爲方便後續開發,現作一些調整和規範。框架

一、設定本項目的開發分辨率爲1334x750(Game視圖分辨率也設置爲這個大小);

二、調整相機,將原有的GuiCamera從Canvas下拖離出來(與Canvas並列),並作以下設置:

(1)Canvas的RenderMode爲Screen Space-Camera,並指定Render Camera爲GuiCamera;ide

(2)設定GUICamera的投射模式(Projection)爲正交(Orthogratphic);模塊化

(3)設置Size爲3.75(3.75 = 750 / 100 /2),這樣Canvas的Scale就會爲0.01;函數

(4)設置相機的Culling Mask爲Everything。post

設置效果見下圖:學習

三、調整動態加載panel的父節點爲Canvas。

找到Assets/LuaFramework/Scipts/Manager下的PanelManager.cs腳本,找到14行,將本行的

GameObject go = GameObject.FindWithTag("GuiCamera");

修改成:

GameObject go = GameObject.Find("Canvas");

以下圖,這樣動態加載的panel就會以Canvas爲父節點。

 

 2、新建一個登錄面板

爲了增長學習代入感,後續演示將會以登錄場景和大廳場景爲示例,文章展現的全部功能,都圍繞這兩個場景展開。此處先建立一個登錄面板。

一、建立一個登錄面板

建立一個登錄面板,結構層級以下所示,並作成預製體,添加打包。

  這一過程詳細作法請參見:Unity3D熱更新之LuaFramework篇[02]--用Lua建立本身的面板

二、建立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面板了。

 

 3、添加UI監聽

根據此前的經驗,添加監聽有兩個步驟:

一、在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;
end

二、在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監聽事件就是這麼簡單。

 4、後記

一、除了Toggle和InputField的事件外,其它的組件如Slider、Scroll Bar、Scroll View等,均可照此例添加。

二、上一步在LuaBehaviour中添加了兩個方法:AddToggle和AddInputFieldEndEditHandler,可是沒有實現相關的移除方法,須要本身完善。

三、考慮到功能單一原責,LuaBehaviour最好只包含Behaviour(腳本生命週期)相關的功能,而添加UI監聽的功能最好能抽離到一個單獨的類中實現,下一篇將會講這個。 

四、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---->>>");
end
LoginPanel.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);
end
LoginCtrl.lua
相關文章
相關標籤/搜索