zoukankan      html  css  js  c++  java
  • 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;
    end

    2、在LoginCtrl中添加事件处理函数;

    登陆界面有三种需要交互的元素,一个是按钮,一个是输入框,一个是复选框(Toggle)。

    按钮(Button)事件的添加,我们之前有过介绍,是通过LuaBehaviour的AddClick方法实现的,如之前制作的FirstPanel面板的关闭按钮,见下图。

    输入框组件(InputField),如果只是需要获取输入值的话,不用添加监听,到找组件并引用,取组件的text值就好了;如果需要在输入结束时做一个操作(如判断用户名是否符合规则,注册时会有此需求),则需要给输入框添加相应监听;

    复选框组件(Toggle),这是一个时实交互组件,需要动态的取Toggle的值,因此需要添加监听以判断当前的选择状态。

    既然按钮可以通过LuaBehaviour脚本添加监听,那么对于Toggle和InputField的监听需是否也可以通过此脚本实现呢?

    LuaBehaviour.cs脚本位于AssetsLuaFrameworkScriptsCommon目录下,打开后能看到,此脚本的包含有添加按钮监听的方法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---->>>");
    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
  • 相关阅读:
    Censored! POJ
    POJ
    HDU
    ionic中的生命周期函数
    ionic项目相关的操作命令
    数组的join()函数操作
    pop()实现逐个删除数组最后一位并输出
    CSS制作彩虹效果
    ionic2 页面加载时图片添加的问题
    升级ionic版本后,创建新项目报Error Initializing app错误解决
  • 原文地址:https://www.cnblogs.com/imteach/p/10960188.html
Copyright © 2011-2022 走看看