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

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

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

本篇继续我的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. ISCSI共享存储

    ISCSI网络磁盘    默认端口:3260 服务端: 一. 二.安装软件:targetcli 用命令targetcli进行配置------------------------进入iscsi磁盘配置模 ...

  2. 简单理解yii事件

    https://blog.csdn.net/qq43599939/article/details/80363827  通过观察者来理解yii事件 测试 class TestController ext ...

  3. 解决Spring AOP Controller 不生效

    在spring-mvc.xml文件中,进行以下配置,就可以实现在Controller中, 方法一:最简单的,在spring-mvc.xml配置文件中,添加以下语句 spring-mvc.xml < ...

  4. 解决蓝牙鼠标在 Ubuntu 中单位时间内断开的问题

    1 查询你的鼠标的蓝牙地址 1.1 如:E1:DE:02:05:5E:F5 2 将查询到的设备地址写入配置文件 /etc/bluetooth/main.conf # Use vendor id sou ...

  5. php+文件分块上传

    PHP用超级全局变量数组$_FILES来记录文件上传相关信息的. 1.file_uploads=on/off 是否允许通过http方式上传文件 2.max_execution_time=30 允许脚本 ...

  6. BZOJ 4152: [AMPPZ2014]The Captain Dijkstra+贪心

    Code: #include <queue> #include <cstdio> #include <cstring> #include <algorithm ...

  7. POJ - 3376 Finding Palindromes manacher+字典树

    题意 给n个字符串,两两拼接,问拼接后的\(n\times n\)个字符串中有多少个回文串. 分析 将所有正串插入字典树中,马拉车跑出所有串哪些前缀和后缀为回文串,记录位置,用反串去字典树中查询,两字 ...

  8. dup2函数

    将当前系统中的进程信息打印到文件中 命令行:ps aux > out 将ps得到的信息重定向到out文件中 使用dup2文件在程序中完成. int dup2(int oldfd,int newf ...

  9. 线程的interrupt()

    官网解释 If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) method ...

  10. Linux 网络通信命令之 netstat

    定义 Linux netstat命令用于显示网络状态. 利用netstat指令可让你得知整个Linux系统的网络情况. 语法 netstat [-acCeFghilMnNoprstuvVwx][-A& ...