19.UGUI进阶-UI事件监听接口
19.1 知识点
UGUI事件接口
UGUI事件接口用于解决控件所提供的常规事件监听列表无法满足的需求,例如长按、双击、拖拽等功能,以及让基础控件如Image、Text、RawImage响应玩家输入的情况。
而事件接口就是用来处理类似问题,让所有控件都能够添加更多的事件监听来处理对应的逻辑。
UGUI事件接口列表
接口名 - 接口函数名-解释说明
想要监听事件的话 就在脚本中继承对应接口 实现对应接口的方法 把脚本挂载到对应控件上 当对控件进行对应的操作时 会自动进行对应方法的逻辑
注意:接口要实现的方法中大多有一个PointerEventData对象作为参数。
UGUI常用事件接口
IPointerEnterHandler - OnPointerEnter
- 当鼠标指针进入对象时调用。
IPointerExitHandler - OnPointerExit
- 当鼠标指针退出对象时调用。
IPointerDownHandler - OnPointerDown
- 当在对象上按下鼠标指针时调用。
IPointerUpHandler - OnPointerUp
- 当松开鼠标指针时调用(在指针点击的对象上调用)。
IPointerClickHandler - OnPointerClick
- 当在同一对象上按下再松开鼠标指针时调用。
IBeginDragHandler - OnBeginDrag
- 当即将开始拖拽时在拖拽对象上调用。
IDragHandler - OnDrag
- 当发生拖拽时在拖拽对象上调用。
IEndDragHandler - OnEndDrag
- 当拖拽完成时在拖拽对象上调用。
UGUI不常用事件接口
IInitializePotentialDragHandler - OnInitializePotentialDrag
- 在找到拖动目标时调用,可用于初始化值。
IDropHandler - OnDrop
- 在拖动目标对象上调用。
IScrollHandler - OnScroll
- 当鼠标滚轮滚动时调用。
IUpdateSelectedHandler - OnUpdateSelected
- 每次勾选时在选定对象上调用。
ISelectHandler - OnSelect
- 当对象成为选定对象时调用。
IDeselectHandler - OnDeselect
- 取消选择选定对象时调用。
导航相关
IMoveHandler - OnMove
- 发生移动事件(上、下、左、右等)时调用。
ISubmitHandler - OnSubmit
- 按下 Submit 按钮时调用。
ICancelHandler - OnCancel
- 按下 Cancel 按钮时调用。
使用UGUI事件接口
继承MonoBehavior的脚本继承对应的事件接口,引用命名空间
public class MyEventHandler : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler, IDragHandler
实现接口中的内容
//定义一个公共方法OnDrag,实现IDragHandler接口,接受一个PointerEventData类型的参数eventData,表示拖动事件的数据
public void OnDrag(PointerEventData eventData)
{
print(eventData.delta); //打印eventData中的delta属性,表示拖动事件的位移量
}
//定义一个公共方法OnPointerDown,实现IPointerDownHandler接口,接受一个PointerEventData类型的参数eventData,表示按下事件的数据
public void OnPointerDown(PointerEventData eventData)
{
print("鼠标(触碰)按下"); //打印一条字符串,表示鼠标或触碰按下
print(eventData.pointerId); //打印eventData中的pointerId属性,表示按下事件的指针标识符,用于区分多点触控
print(eventData.position); //打印eventData中的position属性,表示按下事件的屏幕坐标
}
//定义一个公共方法OnPointerEnter,实现IPointerEnterHandler接口,接受一个PointerEventData类型的参数eventData,表示进入事件的数据
public void OnPointerEnter(PointerEventData eventData)
{
//鼠标进入 在移动设备上 是不存在 因为不存在 进入的概念
print("鼠标进入"); //打印一条字符串,表示鼠标进入对象
}
//定义一个公共方法OnPointerExit,实现IPointerExitHandler接口,接受一个PointerEventData类型的参数eventData,表示退出事件的数据
public void OnPointerExit(PointerEventData eventData)
{
//鼠标离开 在移动设备上 是不存在 因为不存在 进入的概念
print("鼠标离开"); //打印一条字符串,表示鼠标离开对象
}
//定义一个公共方法OnPointerUp,实现IPointerUpHandler接口,接受一个PointerEventData类型的参数eventData,表示抬起事件的数据
public void OnPointerUp(PointerEventData eventData)
{
print("鼠标(触碰)抬起"); //打印一条字符串,表示鼠标或触碰抬起
}
将该脚本挂载到想要监听自定义事件的UI控件上
PointerEventData指针目标数据类参数
PointerEventData 参数详解
PointerEventData 类是 Unity UGUI 事件系统中用于存储与用户输入设备(如鼠标、触摸屏等)交互相关信息的重要参数。它作为 UGUI 事件接口函数的通用数据载体,包含了丰富的指针交互状态属性。
父类 BaseEventData
PointerEventData 继承自 BaseEventData 类,后者提供了一个通用的事件数据结构基础。
关键属性
pointerId 按键标识
- 它代表了用户在操作过程中不同按键的唯一标识,例如鼠标左键、右键或中键。通过这个属性可以准确识别出触发事件的是哪一个按键。
位置相关属性
position 实时位置
- 描述当前指针在屏幕坐标系中的实时位置,当用户进行拖拽操作时,这个值会持续更新。
pressPosition 按下位置
- 记录了指针按下那一刻在屏幕上的初始位置。
Delta 位移变化量
- 表示从上一次事件到当前事件期间,指针在屏幕上的位移变化量。
点击相关属性
clickCount 连续点击次数
- 表示连续点击的次数,用于区分单击和快速连续点击(连击)的行为。
clickTime 最后一次点击发生的时间戳
- 记录了最后一次点击发生的时间戳,有助于根据点击间隔执行不同的响应逻辑。
相关摄像机信息
pressEventCamera 最后一次触发按下事件时所关联的摄像机
- 存储了最后一次触发
OnPointerPress
按下事件时所关联的摄像机实例。
enterEventCamera 最后一次触发进入事件时所关联的摄像机
- 记录了最后一次触发
OnPointerEnter
进入事件时所关联的摄像机实例。
总结
通过UGUI事件接口,可以监听自定义事件,实现一些特殊功能,如长按、双击、拖拽等。虽然可以满足需求,但管理不够方便,需要自己编写脚本并挂载到对应控件上。
19.2 知识点代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class Lesson19_UGUI进阶_UI事件监听接口 : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler, IDragHandler
{
#region 知识点三 使用UGUI事件接口
//定义一个公共方法OnDrag,实现IDragHandler接口,接受一个PointerEventData类型的参数eventData,表示拖动事件的数据
public void OnDrag(PointerEventData eventData)
{
print(eventData.delta); //打印eventData中的delta属性,表示拖动事件的位移量
}
//定义一个公共方法OnPointerDown,实现IPointerDownHandler接口,接受一个PointerEventData类型的参数eventData,表示按下事件的数据
public void OnPointerDown(PointerEventData eventData)
{
print("鼠标(触碰)按下"); //打印一条字符串,表示鼠标或触碰按下
print(eventData.pointerId); //打印eventData中的pointerId属性,表示按下事件的指针标识符,用于区分多点触控
print(eventData.position); //打印eventData中的position属性,表示按下事件的屏幕坐标
}
//定义一个公共方法OnPointerEnter,实现IPointerEnterHandler接口,接受一个PointerEventData类型的参数eventData,表示进入事件的数据
public void OnPointerEnter(PointerEventData eventData)
{
//鼠标进入 在移动设备上 是不存在 因为不存在 进入的概念
print("鼠标进入"); //打印一条字符串,表示鼠标进入对象
}
//定义一个公共方法OnPointerExit,实现IPointerExitHandler接口,接受一个PointerEventData类型的参数eventData,表示退出事件的数据
public void OnPointerExit(PointerEventData eventData)
{
//鼠标离开 在移动设备上 是不存在 因为不存在 进入的概念
print("鼠标离开"); //打印一条字符串,表示鼠标离开对象
}
//定义一个公共方法OnPointerUp,实现IPointerUpHandler接口,接受一个PointerEventData类型的参数eventData,表示抬起事件的数据
public void OnPointerUp(PointerEventData eventData)
{
print("鼠标(触碰)抬起"); //打印一条字符串,表示鼠标或触碰抬起
}
#endregion
void Start()
{
#region 知识点一 UGUI事件接口是用来解决什么问题的
//目前所有的控件都只提供了常用的事件监听列表
//如果想做一些类似长按,双击,拖拽等功能是无法制作的
//或者想让Image和Text,RawImage三大基础控件能够响应玩家输入也是无法制作的
//而事件接口就是用来处理类似问题
//让所有控件都能够添加更多的事件监听来处理对应的逻辑
#endregion
#region 知识点二 有哪些事件接口
//以下方式进行总结
//接口名 - 接口函数名 - 解释说明
//想要监听事件的话 就在脚本中继承对应接口 实现对应接口的方法 把脚本挂载到对应控件上 当对控件进行对应的操作时 会自动进行对应方法的逻辑
#region UGUI常用事件接口
//IPointerEnterHandler - OnPointerEnter - 当鼠标指针进入对象时调用
//IPointerExitHandler - OnPointerExit - 当鼠标指针退出对象时调用
//IPointerDownHandler - OnPointerDown - 当在对象上按下鼠标指针时调用
//IPointerUpHandler - OnPointerUp - 当松开鼠标指针时调用(在指针点击的对象上调用)
//IPointerClickHandler - OnPointerClick - 当在同一对象上按下再松开鼠标指针时调用
//IBeginDragHandler - OnBeginDrag - 当即将开始拖拽时在拖拽对象上调用
//IDragHandler - OnDrag - 当发生拖拽时在拖拽对象上调用
//IEndDragHandler - OnEndDrag - 当拖拽完成时在拖拽对象上调用
#endregion
#region UGUI不常用事件接口 了解即可
//IInitializePotentialDragHandler - OnInitializePotentialDrag - 在找到拖动目标时调用,可用于初始化值
//IDropHandler - OnDrop - 在拖动目标对象上调用
//IScrollHandler - OnScroll - 当鼠标滚轮滚动时调用
//IUpdateSelectedHandler - OnUpdateSelected - 每次勾选时在选定对象上调用
//ISelectHandler - OnSelect - 当对象成为选定对象时调用
//IDeselectHandler - OnDeselect - 取消选择选定对象时调用
//导航相关
//IMoveHandler - OnMove - 发生移动事件(上、下、左、右等)时调用
//ISubmitHandler - OnSubmit - 按下 Submit 按钮时调用
//ICancelHandler - OnCancel - 按下 Cancel 按钮时调用
#endregion
#endregion
#region 知识点三 使用UGUI事件接口
//1.继承MonoBehavior的脚本继承对应的事件接口,引用命名空间
//2.实现接口中的内容
//3.将该脚本挂载到想要监听自定义事件的UI控件上
#endregion
#region 知识点四 PointerEventData参数的关键内容
//PointerEventData参数的关键内容是UGUI事件接口的函数中的参数
//父类:BaseEventData
//pointerId: 鼠标左右中键点击鼠标的ID 通过它可以判断右键点击
//position:当前指针位置(屏幕坐标系)可能在拖拽状态
//pressPosition:按下的时候指针的位置 是按下一瞬间的
//delta:指针移动增量
//clickCount:连击次数
//clickTime:点击时间
//pressEventCamera:最后一个OnPointerPress按下事件关联的摄像机
//enterEvetnCamera:最后一个OnPointerEnter进入事件关联的摄像机
#endregion
#region 总结
//好处:
//需要监听自定义事件的控件挂载继承实现了接口的脚本就可以监听到一些特殊事件
//可以通过它实现一些长按,双击拖拽等功能
//坏处:
//不方便管理,需要自己写脚本继承接口挂载到对应控件上,比较麻烦,不够面向对象
#endregion
}
}
19.3 练习题
在上节课的练习题基础上,请用现在所学知识,制作一个这样的功能:长按一个UI按钮0.2s后开始蓄能,松开按钮后结束蓄能,蓄能满后HP+10,如果没有松开按钮,继续蓄能
在游戏面板中创建一个蓄能按钮,和两张图做成的蓄能条。调整变化图的锚点
创建LongPress 脚本,长按按钮脚本。继承按下和抬起接口。提供按钮抬起时触发的事件和按钮按下时触发的事件,在对应接口的实现方法响应事件。让外部去处理对应的逻辑。将脚本挂载到蓄能按钮上。
/// <summary>
/// 长按按钮脚本 提供两个事件给外部 让外部去处理对应的逻辑
/// </summary>
public class LongPress : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
public event UnityAction upEvent; // 按钮抬起时触发的事件
public event UnityAction downEvent; // 按钮按下时触发的事件
public void OnPointerDown(PointerEventData eventData)
{
downEvent?.Invoke(); // 触发按钮按下事件
}
public void OnPointerUp(PointerEventData eventData)
{
upEvent?.Invoke(); // 触发按钮抬起事件
}
}
在GamePanel脚本中定义长按按钮脚本变量、蓄力条背景变量,蓄力条变化变量,并在外面关联。定义按下按钮方法和抬起按钮方法,注册到长按按钮脚本中的对应事件汇总。一开始隐藏蓄能变化条。定义是否按下按钮变量,当前计时变量,进度条增长速度变量,当前血量变量。按下按钮方法汇总修改按下状态为true,计时变量清零,蓄能条清空重置进度条的长度。抬起按钮方法中修改按下状态为false, 隐藏蓄能条。在Update中判断是否按下按钮变量,如果按下了累加计时时间。计时事件大于0.2秒后显示蓄能条并根据增长速度和时间增加进度条长度。当蓄能条大于蓄能背景图最大长度800时,增加血量变量,打印输出。重置蓄能条。
public class GamePanel : MonoBehaviour
{
//关联长按功能按钮
public LongPress longPress; // 长按按钮
//进度条跟对象 用于控制显隐
public GameObject imgRoot; // 进度条的根对象
//进度条对象 用于控制进度
public RectTransform imgBk; // 进度条的背景图片
//是否按下
private bool isDown = false; // 是否按下按钮
//计时
private float nowTime = 0; // 当前计时
//增加速度
public float addSpeed = 10; // 进度条增长速度
//当前血量
private int hp = 10; // 当前血量
void Start()
{
longPress.downEvent += BtnDown; // 注册长按按钮按下事件的回调函数
longPress.upEvent += BtnUp; // 注册长按按钮抬起事件的回调函数
//一开始隐藏蓄能条
imgRoot.gameObject.SetActive(false); // 初始化时隐藏蓄能条
}
private void BtnDown()
{
isDown = true; // 按下状态为true
nowTime = 0; // 计时归零
//蓄能条清空
imgBk.sizeDelta = new Vector2(0, 40); // 重置进度条的长度
}
private void BtnUp()
{
isDown = false; // 按下状态为false
imgRoot.gameObject.SetActive(false); // 隐藏蓄能条
}
private void Update()
{
if (isDown)
{
//计时
nowTime += Time.deltaTime; // 累加计时时间
if (nowTime >= 0.2f)
{
imgRoot.gameObject.SetActive(true); // 显示蓄能条
//蓄能条增加
imgBk.sizeDelta += new Vector2(addSpeed * Time.deltaTime, 0); // 根据增长速度和时间增加进度条长度
if (imgBk.sizeDelta.x >= 800)
{
//HP增加 条清空
hp += 10; // 血量增加
print("当前hp" + hp); // 打印当前血量
imgBk.sizeDelta = new Vector2(0, 40); // 重置进度条长度
}
}
}
}
}
19.4 练习题代码
LongPress
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
/// <summary>
/// 长按按钮脚本 提供两个事件给外部 让外部去处理对应的逻辑
/// </summary>
public class LongPress : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
public event UnityAction upEvent; // 按钮抬起时触发的事件
public event UnityAction downEvent; // 按钮按下时触发的事件
public void OnPointerDown(PointerEventData eventData)
{
downEvent?.Invoke(); // 触发按钮按下事件
}
public void OnPointerUp(PointerEventData eventData)
{
upEvent?.Invoke(); // 触发按钮抬起事件
}
}
GamePanel
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GamePanel : MonoBehaviour
{
public Light light; // 灯光
public static GamePanel panel; // 游戏面板的静态实例
public Button btnAtk; // 攻击按钮
//音效开关
public Toggle togOn; // 开启音效的开关
public Toggle togOff; // 关闭音效的开关
public ToggleGroup tg; // 音效开关的ToggleGroup
//音效大小
public Slider sliderSound; // 音效大小的滑动条
public Text txtName; // 名字显示文本
public Button btnChangeName; // 改名按钮
public PlayerObject player; // 玩家对象
//背包按钮
public Button btnBag; // 背包按钮
//白天黑夜的切换
public Dropdown ddChange; // 白天黑夜切换下拉框
//关联长按功能按钮
public LongPress longPress; // 长按按钮
//进度条跟对象 用于控制显隐
public GameObject imgRoot; // 进度条的根对象
//进度条对象 用于控制进度
public RectTransform imgBk; // 进度条的背景图片
//是否按下
private bool isDown = false; // 是否按下按钮
//计时
private float nowTime = 0; // 当前计时
//增加速度
public float addSpeed = 10; // 进度条增长速度
//当前血量
private int hp = 10; // 当前血量
private void Awake()
{
panel = this; // 将当前实例指定为静态实例
}
void Start()
{
btnAtk.onClick.AddListener(() =>
{
//得到玩家对象 进行开火
player.Fire(); // 玩家进行开火操作
});
btnChangeName.onClick.AddListener(() => {
//显示改名面板
ChangeNamePanel.panel.gameObject.SetActive(true); // 显示改名面板
});
togOn.onValueChanged.AddListener(TogChangeValue); // 监听音效开启开关的值改变事件
togOff.onValueChanged.AddListener(TogChangeValue); // 监听音效关闭开关的值改变事件
//初始化 我们滑动条的值 通过数据初始化
sliderSound.value = MusicData.SoundValue; // 根据数据初始化音效大小滑动条的值
//监听滑动条改变的事件
sliderSound.onValueChanged.AddListener((v) =>
{
//处理音效的大小
MusicData.SoundValue = v; // 处理音效大小
});
btnBag.onClick.AddListener(() => {
//打开背包面板
BagPanel.panel.gameObject.SetActive(true); // 打开背包面板
});
ddChange.onValueChanged.AddListener((index) => {
switch (index)
{
case 0:
light.intensity = 1; // 切换到白天,设置灯光强度为1
break;
case 1:
light.intensity = 0.3f; // 切换到黑夜,设置灯光强度为0.3
break;
}
});
longPress.downEvent += BtnDown; // 注册长按按钮按下事件的回调函数
longPress.upEvent += BtnUp; // 注册长按按钮抬起事件的回调函数
//一开始隐藏蓄能条
imgRoot.gameObject.SetActive(false); // 初始化时隐藏蓄能条
}
private void BtnDown()
{
isDown = true; // 按下状态为true
nowTime = 0; // 计时归零
//蓄能条清空
imgBk.sizeDelta = new Vector2(0, 40); // 重置进度条的长度
}
private void BtnUp()
{
isDown = false; // 按下状态为false
imgRoot.gameObject.SetActive(false); // 隐藏蓄能条
}
private void Update()
{
if (isDown)
{
//计时
nowTime += Time.deltaTime; // 累加计时时间
if (nowTime >= 0.2f)
{
imgRoot.gameObject.SetActive(true); // 显示蓄能条
//蓄能条增加
imgBk.sizeDelta += new Vector2(addSpeed * Time.deltaTime, 0); // 根据增长速度和时间增加进度条长度
if (imgBk.sizeDelta.x >= 800)
{
//HP增加 条清空
hp += 10; // 血量增加
print("当前hp" + hp); // 打印当前血量
imgBk.sizeDelta = new Vector2(0, 40); // 重置进度条长度
}
}
}
}
private void TogChangeValue(bool v)
{
//得到当前激活的Toggle
foreach (Toggle item in tg.ActiveToggles())
{
if (item == togOn)
{
MusicData.SoundIsOpen = true; // 音效开启
}
else if (item == togOff)
{
MusicData.SoundIsOpen = false; // 音效关闭
}
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com