时隔一个多月我又回来啦!

坚持真的是很难的一件事,其它事情稍忙,就很容易说服自己把写博客的计划给推迟了。

好在终于克服了自己的惰性,今天又开始了。

本篇继续我的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脚本位于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---->>>");
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

Unity3D热更新之LuaFramework篇[04]--自定义UI监听方法的更多相关文章

  1. Unity3D热更新之LuaFramework篇[10]--总结篇

    背景 19年年初的时候,进到一家新单位,公司正准备将现有的游戏做成支持热更的版本.于是寻找热更方案的任务就落在了我头上. 经过搜索了解,能做Unity热更的方案是有好几种,但是要么不够成熟,要么不支持 ...

  2. Unity3D热更新之LuaFramework篇[05]--Lua脚本调用c#以及如何在Lua中使用Dotween

    在上一篇文章 Unity3D热更新之LuaFramework篇[04]--自定义UI监听方法 中,我对LuaBehaviour脚本进行了扩展,添加了两个新的UI监听方法,也提到最好能单写一个脚本处理此 ...

  3. Unity3D热更新之LuaFramework篇[03]--prefab加载和Button事件

    在上一篇文章 Unity3D热更新之LuaFramework篇[02]--用Lua创建自己的面板 中,我介绍了LuaFramework加载面板的方法,但这个方法并不适用于其它Prefab资源,在这套框 ...

  4. Unity3D热更新之LuaFramework篇[02]--用Lua创建自己的面板

    在上篇文章 Unity3D热更新之LuaFramework篇[01]--从零开始 中,我们了解了怎么获得一个可用的LuaFramework框架. 本篇将我会先介绍一下如何配置Lua开发环境,然后分析在 ...

  5. Unity3D热更新之LuaFramework篇[07]--怎么让unity对象绑定Lua脚本

    前言 在上一篇文章 Unity3D热更新之LuaFramework篇[06]--Lua中是怎么实现脚本生命周期的 中,我分析了由LuaBehaviour来实现lua脚本生命周期的方法. 但在实际使用中 ...

  6. Unity3D热更新之LuaFramework篇[09]--资源热更新与代码热更新的具体实现

    前言 在上一篇文章 Unity3D热更新之LuaFramework篇[08]--热更新原理及热更服务器搭建 中,我介绍了热更新的基本原理,并且着手搭建一台服务器. 本篇就做一个实战练习,真正的来实现热 ...

  7. Unity3D热更新之LuaFramework篇[08]--热更新原理及热更服务器搭建

    前言 前面铺垫了这么久,终于要开始写热更新了. Unity游戏热更新包含两个方面,一个是资源的更新,一个是脚本的更新. 资源更新是Unity本来就支持的,在各大平台也都能用.而脚本的热更新在iOS平台 ...

  8. Unity3D热更新之LuaFramework篇[01]--从零开始

    前言 因工作关系,需要对手头的项目进行热更新支持.了解后发现,Lua的几个变种:XLua.ToLua(原uLua)和Slua都可以做Unity热更,而ToLua更是提供了一个简易的热更框架--LuaF ...

  9. Unity3D热更新之LuaFramework篇[06]--Lua中是怎么实现脚本生命周期的

    前言 用c#开发的时候,新建的脚本都默认继承自Monobehaviour, 因此脚本才有了自己的生命周期函数,如Awake,Start, Update, OnDestroy等. 在相应的方法中实现游戏 ...

随机推荐

  1. python_函数作用域

    py文件:全局作用域 函数:局部作用域 一个函数是一个作用域 def func(): x = 9 print(x) func() print(x) 作用域中查找数据规则:优先在自己的作用域找数据,自己 ...

  2. Linux命令之tree(目录树结构)

    tree命令 官方下载地址:http://mama.indstate.edu/users/ice/tree/,右键复制如下图地址: 下载: 执行wget  http://mama.indstate.e ...

  3. 前端知识体系:JavaScript基础-原型和原型链-理解原型设计模式以及 JavaScript中的原型规则

    理解原型设计模式以及 JavaScript中的原型规则(原文地址) 1.原型对象:我们创建的每一个函数(JavaScript中函数也是一个对象)都有一个原型属性 prototype,原型属性实质上是一 ...

  4. trigger(type,[data]) 在每一个匹配的元素上触发某类事件。

    trigger(type,[data]) 概述 在每一个匹配的元素上触发某类事件.大理石平台价格表 这个函数也会导致浏览器同名的默认行为的执行.比如,如果用trigger()触发一个'submit', ...

  5. 如何复制word的图文到ueditor中自动上传?

    官网地址http://ueditor.baidu.com Git 地址 https://github.com/fex-team/ueditor 参考博客地址 http://blog.ncmem.com ...

  6. BZOJ 4017 小 Q 的无敌异或 ( 树状数组、区间异或和、区间异或和之和、按位计贡献思想 )

    题目链接 题意 : 中文题 分析 : 首先引入两篇写的很好的题解 题解一.题解二 听说这种和异或相关区间求和的问题都尽量按位考虑 首先第一问.按二进制位计贡献的话.那么对于第 k 位而言 其贡献 = ...

  7. Windows操作系统中的I/O(读/写 输入/输出)

    导言 写一个Windows平台下的应用程序大多时候都是离不开读写文件,网络通信的. 比如一个服务应用程序来说,它可能从网络适配器接受用户的请求,对请求进行处理计算,最终将用户端所需的数据返回,中间可能 ...

  8. 单链表之Java实现

    初次接触java,用java也写了一个链表.代码如下: import java.io.*; class Node{ public int data; //数据域 public Node next; / ...

  9. jQuery的$符号

    jQuery使用$的原因是: 书写简洁, 相对于其他字符与众不同, 容易被记住. JQuery占用了两个变量: $ 和 jQuery. 当在代码中打印 $ 和 jQuery时: <script ...

  10. BZOJ 2402 陶陶的难题II (树链剖分、线段树、凸包、分数规划)

    毒瘤,毒瘤,毒瘤-- \(30000\)这个数据范围,看上去就是要搞事的啊... 题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=2402 ...