18.EventListener和EventTrigger事件相关

18.NGUI进阶-EventListener和EventTrigger特殊事件监听相关


18.1 知识点

控件自带事件的局限性

  • 目前复合控件只提供了一些常用的事件监听方式,比如:
    • Button —— 点击 uIButton.onClick.Add(new EventDelegate(ClickDoSomthing));
    • Toggle —— 值变化 uIToggle.onChange.Add(new EventDelegate(Change));
  • 如果想要制作按下、抬起、长按等功能,利用现有的知识是无法完成的。

NGUI事件响应函数

  • NGUI事件响应函数是用于添加了碰撞器的对象挂载脚本,在脚本中写入响应函数和逻辑。即写好逻辑事件响应函数脚本挂载到添加了碰撞器的控件。
  • NGUI提供了一些利用反射调用的函数。原理是NGUI会在EventSystem不停地检测玩家输入,当触发到某种输入时,会去触发对象身上的各个脚本找是否有对应的函数,有的话执行对应事件。

常用NGUI事件响应函数有

  • 经过 OnHover(bool isOver)
  • 按下 OnPress(bool pressed)
  • 点击 OnClick()
  • 双击 OnDoubleClick()
  • 拖曳开始 OnDragStart()
  • 拖曳中 OnDrag(Vector2 delta)
  • 拖曳结束 OnDragEnd()
  • 拖曳经过某对象 OnDragOver(GameObject go)
  • 拖曳离开某对象 OnDragOut(GameObject go)
  • 等等等等

NGUI事件响应函数代码

void OnPress(bool pressed)
{
    if (pressed)
    {
        print("按下");
    }
    else
    {
        print("抬起");
    }
}

void OnHover(bool isOver)
{
    if (isOver)
    {
        print("鼠标经过");
    }
    else
    {
        print("鼠标离开");
    }
}

void OnClick()
{
    print("点击相关");
}

void OnDoubleClick()
{
    print("双击相关");
}

void OnDragStart()
{
    print("开始拖曳");
}

void OnDrag(Vector2 delta)
{
    print("拖曳中" + delta);
}

void OnDragEnd()
{
    print("拖曳结束");
}

void OnDragOver(GameObject obj)
{
    // 这个函数传进来的是拖的对象是谁
    print("拖曳经过" + obj.name);
}

void OnDragOut(GameObject obj)
{
    // 这个函数传进来的是拖的对象是谁
    print("拖曳离开" + obj.name);
}

更加方便事件监听的UIEventListener和UIEventTrigger

  • 如果使用上述的NGUI事件响应函数,那么Panel下的所有子控件都要写一个脚本挂上去,很不方便、不面向对象。
  • NGUI提供了更加方便的UIEventListener和UIEventTrigger。
  • UIEventListener和UIEventTrigger帮助我们封装了所有特殊响应函数,可以通过它们进行事件监听管理添加。

UIEventListener 用代码给对象添加事件监听

UIEventListener uIEventListener = UIEventListener.Get(UISpriteA.gameObject);
uIEventListener.onPress += (obj, isPress) => {
    print(obj.name + "被按下或者抬起了" + isPress);
};

uIEventListener.onDragStart += BeginDrag;
private void BeginDrag(GameObject obj)
{
    print(obj.name + "开始拖曳");
}

UIEventTrigger 在Inspector面板给UISpriteB添加UIEventTrigger脚本 添加按下事件监听

UIEventListener和UIEventTrigger区别

  • Listener更适合代码添加监听,Trigger适合拖曳对象添加监听。
  • Listener传入的参数更具体,Trigger则不会传入参数,我们需要在函数中去判断处理逻辑。

18.2 知识点代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson18_NGUI进阶_EventListener和EventTrigger特殊事件监听相关 : MonoBehaviour
{
    public UISprite UISpriteA;
    public UISprite UISpriteB;
    
    void Start()
    {
        #region 知识点一 控件自带事件的局限性
        //目前复合控件只提供了一些常用的事件监听方式
        //比如
        //Button —— 点击 uIButton.onClick.Add(new EventDelegate(ClickDoSomthing));
        //Toggle —— 值变化  uIToggle.onChange.Add(new EventDelegate(Change));
        //等等
        //如果想要制作 按下 抬起 长按等功能 利用现在的知识是无法完成的
        #endregion

        #region 知识点二 NGUI事件响应函数

        //NGUI事件响应函数的是用于添加了碰撞器的对象 挂载脚本 脚本中写入响应函数和逻辑
        //NGUI提供了一些利用反射调用的函数
        //原理是NGUI会在EventSystem不停的检测玩家输入
        //当触发到某种输入时 回去触发对象身上的各个脚本找是否有对应的函数
        //有的话执行对应事件

        //常用NGUI事件响应函数有
        //经过 OnHover(bool isOver)
        //按下 OnPress(bool pressed)
        //点击 OnClick()
        //双击 OnDoubleClick()
        //拖曳开始 OnDragStart()
        //拖曳中  OnDrag(Vector2 delta)
        //拖曳结束 OnDragEnd()
        //拖曳经过某对象 OnDragOver(GameObject go)
        //拖曳离开某对象 OnDragOut(GameObject go)
        //等等等等

        #endregion

        #region 知识点三 更加方便事件监听的UIEventListener和UIEventTrigger

        //如果用上面的NGUI事件响应函数 那么Panel下的所有子空间都要写一个脚本挂上去 很不方便 不面向对象
        //NGUI提供了更加方便的UIEventListener和UIEventTrigger

        //UIEventListener和UIEventTrigger帮助我们封装了所有 特殊响应函数
        //可以通过它们进行事件监听管理添加

        //UIEventListener 用代码给对象添加事件监听
        //调用UIEventListener的Get方法  获得想要添加监听的游戏对象的UIEventListener脚本 假如没有就会加一个UIEventListener脚本 有就直接拿
        UIEventListener uIEventListener = UIEventListener.Get(UISpriteA.gameObject);
        //UIEventListener这个类里定义了很多委托和委托变量 和NGUI事件响应函数名字大多一样 可以给对应委托添加监听
        //鼠标按下或抬起添加监听
        uIEventListener.onPress += (obj, isPress) => {
            print(obj.name + "被按下或者抬起了" + isPress);
        };
        //开始拖曳时添加监听
        uIEventListener.onDragStart += BeginDrag;


        //UIEventTrigger 在Inspector面板添加UIEventTrigger脚本给对象添加事件监听


        //UIEventListener和UIEventTrigger区别
        //1.Listener更适合 代码添加监听 Trigger适合拖曳对象添加监听
        //2.Listener传入的参数 更具体  Trigger就不会传入参数 我们需要在函数中去判断处理逻辑
        #endregion
    }

    #region 知识点三 更加方便事件监听的UIEventListener和UIEventTrigger

    private void BeginDrag(GameObject obj)
    {
        print(obj.name + "开始拖曳");
    }

    public void Press()
    {

    }

    #endregion

    #region 知识点二 NGUI事件响应函数

    void OnPress(bool pressed)
    {
        if (pressed)
        {
            print("按下");
        }
        else
        {
            print("抬起");
        }
    }

    void OnHover(bool isOver)
    {
        if (isOver)
        {
            print("鼠标经过");
        }
        else
        {
            print("鼠标离开");
        }
    }

    void OnClick()
    {
        print("点击相关");
    }

    void OnDoubleClick()
    {
        print("双击相关");
    }

    void OnDragStart()
    {
        print("开始拖曳");
    }

    void OnDrag(Vector2 delta)
    {
        print("拖曳中" + delta);
    }

    void OnDragEnd()
    {
        print("拖曳结束");
    }

    void OnDragOver(GameObject obj)
    {
        //这个函数传进来的是拖的对象是谁
        print("拖曳经过" + obj.name);
    }

    void OnDragOut(GameObject obj)
    {
        //这个函数传进来的是拖的对象是谁
        print("拖曳离开" + obj.name);
    }

    #endregion
}

18.3 练习题

在Anchor锚点的练习题基础上,请用现在所学知识,制作一个这样的功能,制作一个NGUI摇杆可以控制场景上的坦克移动

在GamePanel下创建一大一小两个Sprite作为摇杆背景和摇杆,小的摇杆是大摇杆背景的子物体并且层级更高。给小的摇杆添加碰撞器

在GamePanel脚本中创建一个Sprite对象并关联小的摇杆Sprite,在拖拽摇杆时添加移动摇杆和玩家移动的监听,在停止拖拽摇杆是恢复摇杆位置并让玩家停止移动

#region Lesson18_NGUI进阶_EventListener和EventTrigger特殊事件监听相关练习题
public UISprite controller;
#endregion

void Start()
{
    #region Lesson18_NGUI进阶_EventListener和EventTrigger特殊事件监听相关练习题
    
    UIEventListener listener = UIEventListener.Get(controller.gameObject);
    // 通过给控制图标 GameObject 对象添加的 UIEventListener 组件来监听鼠标/手指的拖曳操作
    
    listener.onDrag = (obj, vector) => {
    
        // 根据拖曳偏移量实现控制图标跟随鼠标移动 给本地坐标加位置 本地坐标默认(0,0)
        controller.transform.localPosition += new Vector3(vector.x, vector.y, 0);
    
        // 判断是否超出了可拖曳区域,并进行限制,使其不至于越过设定的最大半径 magnitude是向量求模
        if (controller.transform.localPosition.magnitude > 130)
            //大于最大拖拽区域就把他变成130 normalized是该方向的单位向量
            controller.transform.localPosition = controller.transform.localPosition.normalized * 130;
    
        // 根据摇杆控制图标位置计算出摇杆操纵的移动方向(以世界坐标系下的 x-z 平面为基础)
        Vector2 dir = new Vector2(controller.transform.localPosition.x,
                                  controller.transform.localPosition.y).normalized;
    
        // 更改玩家对象当前的朝向和位移信息
        player.Move(dir);
    };
    
    listener.onDragEnd = (obj) =>
    {
        // 拖放结束后,重置摇杆图标回到原点位置
        controller.transform.localPosition = Vector3.zero;
    
        // 结束时让玩家停止移动
        player.StopMove();
    };
    
    #endregion
}

理清摇杆偏移的方向向量和坦克要移动的方向向量的映射逻辑

在TankObj脚本中,添加移动相关的变量。在Update判断要移动时,设置坦克边旋转边向前走。

// 定义一个TankObj类,继承自MonoBehaviour
public class TankObj : MonoBehaviour
{
    // 移动速度
    public float moveSpeed = 10;
    // 旋转速度
    public float roundSpeed = 20;
    // 移动标识
    private bool isMoving = false;
    // 要移动的方向向量
    private Vector3 moveDir;
    
    // 更新函数,在每一帧执行
    private void Update()
    {
        // 判断坦克是否处于移动状态来进行相应的逻辑处理
        if (isMoving)
        {
            // 坦克沿着前方方向移动
            this.transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
            // 旋转坦克使其朝向移动方向 用Lerp插值计算Quaternion之间的插值转换,第三个参数表示在0到1之间进行插值,越接近1就越靠近目标值
            this.transform.rotation = Quaternion.Lerp(this.transform.rotation, Quaternion.LookRotation(moveDir), roundSpeed * Time.deltaTime);
        }
    }
    
    // 传入移动方向
    public void Move(Vector3 dir)
    {
        // 设置实际要转的角度 摇杆的x轴对应坦克的x轴 摇杆的y轴对应坦克的z轴
        moveDir.x = dir.x;
        moveDir.z = dir.y;
        isMoving = true; // 设置为移动状态
    }
    
    // 停止移动
    public void StopMove()
    {
        isMoving = false;
    }
}

18.4 练习题代码

GamePanel

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GamePanel : MonoBehaviour
{
    private static GamePanel instance;

    public static GamePanel Instance => instance;


    public UIButton btn;

    public TankObj player;

    public UIToggle togSound;

    public UILabel labName;

    public UIButton btnChangeName;

    #region Lesson13_NGUI基础_组合控件_PopupList下拉列表练习题
    public UIPopupList list;
    public Light lightObj;
    #endregion

    #region Lesson14_NGUI基础_组合控件_Slider滑动条练习题
    public UISlider sliderSound;
    #endregion

    #region Lesson15_NGUI基础_组合控件_ScrollBar滚动条和ProgressBar进度条练习题
    public UIProgressBar progressBar;
    #endregion

    #region Lesson18_NGUI进阶_EventListener和EventTrigger特殊事件监听相关练习题
    public UISprite controller;
    #endregion

    private void Awake()
    {
        instance = this;
    }

    void Start()
    {
        btn.onClick.Add(new EventDelegate(() =>
        {
            player.Fire();
        }));

        togSound.onChange.Add(new EventDelegate(() =>
        {
            MusicData.isOpenSound = togSound.value;
        }));

        btnChangeName.onClick.Add(new EventDelegate(() =>
        {
            ChangeNamePanel.Instance.gameObject.SetActive(true);
        }));

        #region Lesson13_NGUI基础_组合控件_PopupList下拉列表练习题
        list.onChange.Add(new EventDelegate(() =>
        {
            switch (list.value)
            {
                case "白天":
                    lightObj.intensity = 1;
                    break;
                case "黑夜":
                    lightObj.intensity = 0.2f;
                    break;
            }
        }));
        #endregion

        #region Lesson14_NGUI基础_组合控件_Slider滑动条练习题
        sliderSound.onChange.Add(new EventDelegate(() =>
        {
            MusicData.soundValue = sliderSound.value;
        }));
        #endregion

        #region Lesson15_NGUI基础_组合控件_ScrollBar滚动条和ProgressBar进度条练习题
        HideHpPro();
        #endregion

        #region Lesson16_NGUI基础_组合控件_ScrollView滚动视图练习题
        btnBag.onClick.Add(new EventDelegate(() =>
        {
            BagPanel.Instance.gameObject.SetActive(true);
        }));
        #endregion

        #region Lesson18_NGUI进阶_EventListener和EventTrigger特殊事件监听相关练习题

        UIEventListener listener = UIEventListener.Get(controller.gameObject);

        listener.onDrag = (obj, vector) =>
        {

            controller.transform.localPosition += new Vector3(vector.x, vector.y, 0);

            if (controller.transform.localPosition.magnitude > 130)
                controller.transform.localPosition = controller.transform.localPosition.normalized * 130;

            Vector2 dir = new Vector2(controller.transform.localPosition.x,
                                      controller.transform.localPosition.y).normalized;

            player.Move(dir);
        };

        listener.onDragEnd = (obj) =>
        {
            controller.transform.localPosition = Vector3.zero;

            player.StopMove();
        };

        #endregion
    }

    /// <summary>
    /// 显示蓄能条
    /// </summary>
    public void ShowHpPro()
    {
        progressBar.gameObject.SetActive(true);
    }

    /// <summary>
    /// 隐藏蓄能条
    /// </summary>
    public void HideHpPro()
    {
        progressBar.gameObject.SetActive(false);
    }

    /// <summary>
    /// 更新蓄能条
    /// </summary>
    /// <param name="nowValue"></param>
    /// <param name="maxValue"></param>
    public void UpdatePro(float nowValue, float maxValue)
    {
        progressBar.value = nowValue / maxValue;
    }
}

TankObj

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TankObj : MonoBehaviour
{
    public float moveSpeed = 10;

    public float roundSpeed = 20;

    public Transform shootPos;

    private AudioClip clip;

    private float nowTime;

    public int nowHp = 100;

    private bool isMoving = false;

    private Vector3 moveDir;

    public void Fire()
    {
        AudioSource source = this.gameObject.AddComponent<AudioSource>();

        if (clip == null)
            clip = Resources.Load<AudioClip>("Sound/CannonShoot");
        source.clip = clip;

        source.volume = MusicData.soundValue;
        source.mute = !MusicData.isOpenSound;
        source.Play();

        Destroy(source, 2);

        Instantiate(Resources.Load<GameObject>("Obj/Bullet"), shootPos.position, shootPos.rotation);
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            GamePanel.Instance.ShowHpPro();
            nowTime = 0;
            GamePanel.Instance.UpdatePro(nowTime, 5);
        }
        else if (Input.GetMouseButtonUp(0))
        {
            GamePanel.Instance.HideHpPro();
        }
        else if (Input.GetMouseButton(0))
        {
            nowTime += Time.deltaTime;
            GamePanel.Instance.UpdatePro(nowTime, 5);

            if (nowTime >= 5)
            {
                nowTime = 0;
                nowHp += 5;
                print("当前血量" + nowHp);
            }
        }

        if (isMoving)
        {
            this.transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
            this.transform.rotation = Quaternion.Lerp(this.transform.rotation, Quaternion.LookRotation(moveDir), roundSpeed * Time.deltaTime);
        }
    }

    public void Move(Vector3 dir)
    {
        moveDir.x = dir.x;
        moveDir.z = dir.y;
        isMoving = true;
    }

    public void StopMove()
    {
        isMoving = false;
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏