zoukankan      html  css  js  c++  java
  • Unity3D热更新之LuaFramework篇[05]--Lua脚本调用c#以及如何在Lua中使用Dotween

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

    从Lua中调用C#代码

    1、创建UI监听脚本

     打开之前的工程,在Assets/LuaFrameworks/Scripts/Common下,创建一个UIEventEx.cs脚本,将LuaBehaviour.cs中的AddButtonClick以及AddInputFieldEndEditHandler方法迁移过来,并扩展了一些其它方法,代码如下:

      1 using LuaInterface;
      2 using System.Collections;
      3 using System.Collections.Generic;
      4 using UnityEngine;
      5 using UnityEngine.EventSystems;
      6 using UnityEngine.UI;
      7 
      8 /// <summary>
      9 /// 自定义的添加UI监听的方法,可以用lua中调用以做事件绑定
     10 /// </summary>
     11 public class UIEventEx {
     12     //添加监听
     13     public static void AddButtonClick(GameObject go, LuaFunction luafunc)
     14     {
     15         if (go == null || luafunc == null)
     16             return;
     17 
     18         Button btn = go.GetComponent<Button>();
     19         if (btn == null)
     20             return;
     21 
     22         btn.onClick.AddListener
     23         (
     24             delegate ()
     25             {
     26                 luafunc.Call(go);
     27             }
     28         );
     29     }
     30 
     31     //添加监听(外带数据中转功能)
     32     public static void AddButtonClick(GameObject go, LuaFunction luafunc, LuaTable luatable)
     33     {
     34         if (go == null || luafunc == null)
     35             return;
     36 
     37         Button btn = go.GetComponent<Button>();
     38         if (btn == null)
     39             return;
     40 
     41         btn.onClick.AddListener
     42         (
     43             delegate ()
     44             {
     45                 luafunc.Call(go, luatable);
     46             }
     47         );
     48     }
     49 
     50     /// <summary>
     51     /// 给Toggle组件添加监听
     52     /// </summary>
     53     public static void AddToggle(GameObject go, LuaFunction luafunc, LuaTable luatable)
     54     {
     55         if (go == null || luafunc == null) return;
     56 
     57         Toggle toggle = go.GetComponent<Toggle>();
     58 
     59         if (toggle == null) return;
     60 
     61         go.GetComponent<Toggle>().onValueChanged.AddListener(
     62             delegate (bool select) {
     63                 luafunc.Call(luatable, select);
     64             }
     65         );
     66     }
     67 
     68 
     69     /// <summary>
     70     /// 给Toggle组件添加监听
     71     /// </summary>
     72     public static void AddToggle(GameObject go, LuaFunction luafunc)
     73     {
     74         if (go == null || luafunc == null) return;
     75 
     76         Toggle toggle = go.GetComponent<Toggle>();
     77 
     78         if (toggle == null) return;
     79 
     80         go.GetComponent<Toggle>().onValueChanged.AddListener(
     81             delegate (bool select) {
     82                 luafunc.Call(select);
     83             }
     84         );
     85     }
     86 
     87     //给输入组件(InputField)添加结束编辑(OnEndEdit)监听
     88     public static void AddInputFieldEndEditHandler(GameObject go, LuaFunction luafunc)
     89     {
     90         if (go == null || luafunc == null) return;
     91 
     92         InputField input = go.GetComponent<InputField>();
     93 
     94         if (input == null)
     95         {
     96             Debug.LogError(go.name + "找不到InputField组件");
     97             return;
     98         }
     99 
    100         go.GetComponent<InputField>().onEndEdit.AddListener(
    101             delegate (string text) {
    102                 luafunc.Call(text);
    103             }
    104         );
    105     }
    106 
    107     /// <summary>
    108     /// 添加对光标按下|抬起事件的支持
    109     /// </summary>
    110     /// <param name="go">目标对象</param>
    111     /// <param name="luafunc">按下事件</param>
    112     /// <param name="luafunc2">抬起事件</param>
    113     public static void AddPointerDownUpSupport(GameObject go, LuaFunction luafunc, LuaFunction luafunc2)
    114     {
    115         if (go == null) return;
    116 
    117         EventsSupport es = go.AddComponent<EventsSupport>();
    118 
    119         es.InitDownUpHandler((PointerEventData pointerEventData) => {
    120             if (luafunc != null)
    121             {
    122                 luafunc.Call(go, pointerEventData);
    123             }
    124 
    125         }, (PointerEventData pointerEventData) => {
    126             if (luafunc2 != null)
    127             {
    128                 luafunc2.Call(go, pointerEventData);
    129             }
    130         });
    131     }
    132 
    133     /// <summary>
    134     /// 给Slider组件添加onValueChanged事件
    135     /// </summary>
    136     /// <param name="go"></param>
    137     /// <param name="luafunc"></param>
    138     public static void AddSliderOnChangeEvent(GameObject go, LuaFunction luafunc)
    139     {
    140         if (go == null || luafunc == null) return;
    141 
    142         Slider component = go.GetComponent<Slider>();
    143 
    144         if (component == null)
    145         {
    146             Debug.LogError(go.name + "找不到Slider组件");
    147             return;
    148         }
    149 
    150         go.GetComponent<Slider>().onValueChanged.AddListener(
    151             delegate (float val) {
    152                 luafunc.Call(val);
    153             }
    154         );
    155     }
    156 
    157     //清除监听
    158     public static void ClearButtonClick(GameObject go)
    159     {
    160         if (go == null)
    161             return;
    162 
    163         Button btn = go.GetComponent<Button>();
    164         if (btn == null)
    165             return;
    166 
    167         btn.onClick.RemoveAllListeners();
    168     }
    169 
    170 }
    UIEventEx.cs

    在Assets/LuaFrameworks/Scripts/Common下,创建一个EventsSupport.cs脚本,该脚本是一个实现了IPointerDownHandler, IPointerUpHandler等接口的类,用于在Lua中检测鼠标输入(鼠标点击,抬起、按下等功能),配合UIEventEx.cs中的AddPointerDownUpSupport方法使用。其代码如下:

     1 using System;
     2 using System.Collections;
     3 using System.Collections.Generic;
     4 using UnityEngine;
     5 using UnityEngine.EventSystems;
     6 
     7 /*  其它事件可根据需要在此类中实现
     8     IPointerEnterHandler - OnPointerEnter - Called when a pointer enters the object
     9     IPointerExitHandler - OnPointerExit - Called when a pointer exits the object
    10     IPointerDownHandler - OnPointerDown - Called when a pointer is pressed on the object
    11     IPointerUpHandler - OnPointerUp - Called when a pointer is released (called on the original the pressed object)
    12     IPointerClickHandler - OnPointerClick - Called when a pointer is pressed and released on the same object
    13     IInitializePotentialDragHandler - OnInitializePotentialDrag - Called when a drag target is found, can be used to initialise values
    14     IBeginDragHandler - OnBeginDrag - Called on the drag object when dragging is about to begin
    15     IDragHandler - OnDrag - Called on the drag object when a drag is happening
    16     IEndDragHandler - OnEndDrag - Called on the drag object when a drag finishes
    17     IDropHandler - OnDrop - Called on the object where a drag finishes
    18     IScrollHandler - OnScroll - Called when a mouse wheel scrolls
    19     IUpdateSelectedHandler - OnUpdateSelected - Called on the selected object each tick
    20     ISelectHandler - OnSelect - Called when the object becomes the selected object
    21     IDeselectHandler - OnDeselect - Called on the selected object becomes deselected
    22     IMoveHandler - OnMove - Called when a move event occurs (left, right, up, down, ect)
    23     ISubmitHandler - OnSubmit - Called when the submit button is pressed
    24     ICancelHandler - OnCancel - Called when the cancel button is pressed
    25  */
    26 
    27 /// <summary>
    28 /// unity事件支持(本类用于实现Unity中的各种事件,借给Lua调用)
    29 /// </summary>
    30 public class EventsSupport : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
    31 {
    32     Action<PointerEventData> onPointerDownHandler = null;
    33     Action<PointerEventData> onPointerUpHandler = null;
    34 
    35     public void InitDownUpHandler (Action<PointerEventData> downHandler, Action<PointerEventData> upHandler)
    36     {
    37         onPointerDownHandler = downHandler;
    38         onPointerUpHandler = upHandler;
    39     }
    40 
    41     public void OnPointerDown(PointerEventData pointerEventData)
    42     {
    43         //Output the name of the GameObject that is being clicked
    44         //Debug.Log("[" + name + "] Game Object Click in Progress");
    45         
    46         if (onPointerDownHandler != null) {
    47             onPointerDownHandler(pointerEventData);
    48         }
    49     }
    50 
    51     //Detect if clicks are no longer registering
    52     public void OnPointerUp(PointerEventData pointerEventData)
    53     {
    54         //Debug.Log("[" + name + "] No longer being clicked");
    55         if (onPointerUpHandler != null)
    56         {
    57             onPointerUpHandler(pointerEventData);
    58         }
    59     }
    60 }
    EventsSupport.cs

    EventsSupport.cs脚本需要挂在待检测输入的Game Object上。

    2、使用脚本

    这里还是以上一篇文章写的登陆界面为例,之前我们是通过LuaBehaviour给Button、Toggle以及InputField添加的监听函数,现在将相应的用法直接替换掉。

    替换前:

    替换后:

    然后运行,看是否生效。

    ......

    运行结果,报错了,提示全局变量 UIEventEx为nil(就是这个变量不存在的意思)

      看来这样想当然的方法是行不通了,我们不能创建一个C#脚本,然后在Lua中直接使用它。

    3、C#类导出

    在上一步中,我们发现无法直接在Lua中使用创建的C#脚本。通过查阅资料了解到,对自定义的c#类,如果想在Lua中使用的话,需要做一个导出操作才行。

    ToLua的官方git上也有相关的说明:

    跟着说明操作:

    1)找到AssetsLuaFrameworkEditor下的CustomSettings.cs脚本; 

    2)在CustomSettings的60 行左右照例添加一个导出语句"_GT(typeof(UIEventEx)),";

     

    3)点击Lua/Generate All菜单,等到日志打印 Generate LuaBinder over !字样时,表明Generate操作已经完成了。

    此时查看AssetsLuaFrameworkToLuaSourceGenerate,能找到一个叫UIEventExWrap的cs文件,这个就是UIEventEx的导出类。

    4)重新运行Unity,已经不再报错了,点击Button、Toggle、在InputField中输入字符,功能都和之前使用LuaBehaviour时一致。

    总结

    如果想在Lua中使用自定义的c#类,需要4个步骤:

    1)创建c#脚本;

    2)在CustomSetting.cs中添加导出语句;

    3)点击Lua/Generate All菜单;

    4)在Lua中以全局变量的形式直接使用;

    这里涉及的转化过程是这样的:

    1)UIEventEx脚本通过Lua/Generate All菜单生成UIEventWrap脚本;

    2)UIEventWrap脚本经过ToLua的作用,最终成为Lua中的一个全局变量UIEventEx

    在之前的文章中我们曾直接使用Lua/Generate All菜单而未做解释,那么现在你应该明白它的做用是什么了。

    至于ToLua怎么把一个XxxWrap转换为Lua中的全局变量,就不是本文能讲得清的了(你可以自己做弄清楚);

    怎么在Lua中使用Dotween

    Dotween作为一款非常优秀的缓动动画插件,基本上快成为Unity的标配了。而如果想把所有的UI逻辑全部Lua化,那么在Lua中使用Dotween就是必须的了。

    根据前边的经验,Dotween相关的类对于ToLua来说,就是自定义类,想要在Lua中使用,就必须做导出操作。

    那么就有以下步骤:

    1)给项目导入一个Dotween插件;

    2)导出Dotween相关类;

    第二步就是要整理Dotween相关的类,然后一个个写导出语句,这不是一个简单的活儿。

    不过不必担心,ToLua已经帮我们做好了。

    打开AssetsLuaFrameworkEditor下的CustomSettings.cs脚本,在70~100行左右,能看到Dotween相关类的导出语句,不过由于未检测到USING_DOTWEENING宏定义的原因,这一段代码并未生效。

     3)使用宏定义USING_DOTWEENING

     一个简单的定义宏的办法是在脚本头部加入 #define USING_DOTWEENING语句,如下图

    另外一个办法是在PlayerSettings的Scripting Define Symbols*下添加相应的宏,如下图: 

    其中ASYNC_MOD是之前有的,两个宏之间用分号隔开,输入USING_DOTWEENING 要回车一次,让脚本重新编译。

    这里使用第一种办法。

    定义了宏之后,Dotween相关类的导出语句就生效了,然后要执行一次Lua/Generate All。

    4)在Lua中作用Dotween

     以登陆界面的登陆按钮为例,在LoginPanel.lua脚本中添加如下的Dotween使用方法。 

    然后运行,能看到动画已经生效了(移动及循环都没问题),不过最后的回调没执行。

    看日志有一个报错,说的是TweenCallback未注册。这个就是OnComplete回调未执行的原因。

     TweenCallback是一个委托类型,根据此前了知识,委托类型也需要在CustomSetting中指定位置注册。

    打开CustomSettings脚本,在40~50行左右的位置,添加TweenCallback的导出语句"_DT(typeof(DG.Tweening.TweenCallback)),",如下图所示:

    之后重新执行Lua/Generate All菜单(如果有报错,可先执行一次Clear再执行Generate All)。

    现在将循环次数改为1,重新运行。

    能看到动画停止后,指定的日志已经输出。

    在Lua中使用用Dotween,就是这样一个步骤。

     有一点要注意的是,在Lua中的代码提示是很不健全的,特别是在调用用C#脚本的时候。

    这里写Dotween动画的代码就是全靠经验,如果不熟的话,也可以先用C#写一遍,再搬到Lua中改造。

    怎么从C#中调用Lua脚本

    文章的前半部分介绍了Lua中调用c#的方法,那么相应的如何从c#中调用Lua也有必要了解一下。

    c#调用Lua是比较少的一个操作,基本上就在框架(LuaFramework)初始化的时候有用到。这里不做详细案例,只讲一下了解方式。

    方式1:

    ToLua的Examples, 03_CallLuaFunction,这个脚本详细讲述了c#调用Lua的过程。

    方式2:

    LuaFramework的LuaManager类,这个脚本里有详细的调用Main.Lua的过程。

    后记 

    一个疑问:

    在写Lua的Dotween代码的时候,使用DOLocalMove、SetLoops和OnComplete都是用冒号:的方式,实际上这三个都是static方法,这有违于上一篇文章中总结的静态方法用点号,成员方法用冒号的规则。

    暂不知道原因,如果你知道,还请留言指教。

    ------------------------------------

    疑问已解决,答案在1楼,感谢 @ 马三小伙儿 大佬的解答

  • 相关阅读:
    用友跨账套查询
    用友单据导入
    Delphi Math单元函数
    9.golang 字符串操作
    7.golang 结构体和指针
    6.golang 数组,切片,映射
    5.golang 控制流程 if, else if ,switch ,defer使用
    golang 指针类型* ,以及 &取变量内存地址
    phpjwt 第三方库生成token验证,也可以做公钥,私钥的验证方法。
    golang采坑一 expected ';', found 'import'
  • 原文地址:https://www.cnblogs.com/imteach/p/11026054.html
Copyright © 2011-2022 走看看