【Unity知识点详解】Button点击事件拓展,单击、双击、长按实现
Button拓展
今天来聊一下关于Button的事件拓展,这里只是拿Button来举例,Unity中其他的UI组件如Toggle、Slider等都也适用。
我们知道在Button中我们可以通过onClick的方式来添加点击事件,但在游戏开发过程中我们往往对Button有着更多的功能需求,比如说双击、长按、按钮按下、按钮弹起等。这里举一个游戏中实际的例子,在游戏背包中的道具,单击道具时我们需要显示道具的tips框,双击时我们会去使用道具,长按时我们则可以拖动道具,当长按弹起时则道具回到原位或移动到新格子内。虽然这里的背包道具不是按钮,但在单个UI组件上集合了单击、双击、长按、按钮弹起等事件的响应。接下来将介绍如何拓展UI组件来实现这些功能。
首先我们来认识一下Selectable这个类,Selectable是所有交互组件的基类,Unity原生的Button组件就是继承了Selectable,我们要拓展Button功能也是对Selectable下的OnPointerDown、OnPointerUp等接口进行重写。
话不多说先上代码,代码有点长大家可以先跳过细节,后面慢慢讲解。
using System;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;public class ExButton : Button
{private enum EnumExButtonState{/// <summary>空</summary>None,/// <summary>鼠标按下</summary>PointerDown,/// <summary>鼠标按下</summary>PointerUp,/// <summary>单击</summary>Click,/// <summary>双击</summary>DoubleClick,/// <summary>长按开始</summary>PressBegin,/// <summary>长按</summary>Press,/// <summary>长按结束</summary>PressEnd,}/// <summary>按钮状态</summary>private EnumExButtonState mButtonState = EnumExButtonState.None;/// <summary>鼠标按下时间</summary>private float mPointerDownTime = 0.0f;[SerializeField]/// <summary>双击间隔时间</summary>private float mDoubleClickInterval = 0.2f;[SerializeField]/// <summary>长按开始时间</summary>private float mPressBeginTime = 0.3f;[SerializeField]/// <summary>长按间隔时间,0为每帧调用</summary>private float mPressIntervalTime = 0.2f;/// <summary>长按缓存时间</summary>private float mPressCacheTime = 0f;public Action OnClick { get; set; }public Action OnDoubleClick { get; set; }public Action OnPressBegin { get; set; }public Action OnPress { get; set; }public Action OnPressEnd { get; set; }public override void OnPointerDown(PointerEventData eventData){if (OnDoubleClick != null){if (mButtonState == EnumExButtonState.None){mButtonState = EnumExButtonState.PointerDown;mPointerDownTime = Time.time;}else if (mButtonState == EnumExButtonState.PointerUp){if (Time.time - mPointerDownTime < mDoubleClickInterval){mButtonState = EnumExButtonState.DoubleClick;return;}else{mButtonState = EnumExButtonState.PointerDown;mPointerDownTime = Time.time;}}}if (OnPressBegin != null || OnPress != null || OnPressEnd != null){if (mButtonState != EnumExButtonState.DoubleClick){mButtonState = EnumExButtonState.PointerDown;mPointerDownTime = Time.time;}}if (OnClick != null) { }}public override void OnPointerUp(PointerEventData eventData){if (OnDoubleClick != null){if (mButtonState == EnumExButtonState.DoubleClick)return;}if (OnPressBegin != null || OnPress != null || OnPressEnd != null){if (mButtonState == EnumExButtonState.Press){mButtonState = EnumExButtonState.PressEnd;return;}}if (OnClick != null){if (mButtonState == EnumExButtonState.PointerDown)mButtonState = EnumExButtonState.PointerUp;}}private void Update(){ProcessUpdate();ResponseButtonState();}private void ProcessUpdate(){if (OnDoubleClick != null) { }if (OnPressBegin != null || OnPress != null || OnPressEnd != null){if (mButtonState == EnumExButtonState.PointerDown){if (Time.time - mPointerDownTime > mPressBeginTime){mButtonState = EnumExButtonState.PressBegin;mPressCacheTime = 0f;return;}}}if (OnClick != null){if (mButtonState == EnumExButtonState.PointerUp){if (OnDoubleClick != null){if (Time.time - mPointerDownTime > mDoubleClickInterval)mButtonState = EnumExButtonState.Click;}else{mButtonState = EnumExButtonState.Click;}}}}private void ResponseButtonState(){switch (mButtonState){case EnumExButtonState.None:break;case EnumExButtonState.Click:OnClick?.Invoke();mButtonState = EnumExButtonState.None;break;case EnumExButtonState.DoubleClick:OnDoubleClick?.Invoke();mButtonState = EnumExButtonState.None;break;case EnumExButtonState.PressBegin:OnPressBegin?.Invoke();mButtonState = EnumExButtonState.Press;break;case EnumExButtonState.Press:{mPressCacheTime += Time.deltaTime;if (mPressCacheTime >= mPressIntervalTime){mPressCacheTime = mPressCacheTime - mPressIntervalTime;OnPress?.Invoke();}break;}case EnumExButtonState.PressEnd:OnPressEnd?.Invoke();mButtonState = EnumExButtonState.None;break;default:break;}}
}
ExButton组件功能的拓展需要继承自Button类,并且重写OnPointerDown、OnPointerUp方法(这里根据需求只重写了OnPointerDown、OnPointerUp方法,大家可以根据自己的需求重写Selectable下的方法)。根据需求我们要实现点击、双击、长按、长按开始、长按结束事件的回调,所以在代码中我们提供了OnClick、OnDoubleClick、OnPressBegin、OnPress、OnPressEnd回调方法。
public Action OnClick { get; set; }
public Action OnDoubleClick { get; set; }
public Action OnPressBegin { get; set; }
public Action OnPress { get; set; }
public Action OnPressEnd { get; set; }
逻辑采用了单状态机来实现,在OnPointerDown、OnPointerUp、Update方法中去改变成员mButtonState的状态,最终在ResponseButtonState方法中根据mButtonState的状态去进行事件的回调。
在逻辑中也进行了事件回调的优化处理,当OnClick、OnDoubleClick、OnPressBegin、OnPress、OnPressEnd所有回调都被注册时,会优先处理OnDoubleClick,其次是OnPressBegin、OnPress、OnPressEnd,最后才是OnClick。例如当OnDoubleClick未被注册时,则会跳过OnDoubleClick对应的逻辑判断,提前处理并响应其他回调事件。
base.OnPointerDown、base.OnPointerUp
public override void OnPointerDown(PointerEventData eventData)
{base.OnPointerDown(eventData);
}public override void OnPointerUp(PointerEventData eventData)
{base.OnPointerUp(eventData);
}
在重写OnPointerDown、OnPointerUp后如需保留原有功能,需要调用base.OnPointerDown、base.OnPointerUp方法。只有调用基类方法,按钮的按下颜色改变才会有效果,当然这些基类方法也可以在别处调用。
官方文档连接
Selectable文档连接:https://docs.unity3d.com/Packages/com.unity.ugui@2.0/manual/script-Selectable.html
Selectable类API文档连接:https://docs.unity3d.com/Packages/com.unity.ugui@2.0/api/UnityEngine.UI.Selectable.html?q=selectable
相关文章:
【Unity知识点详解】Button点击事件拓展,单击、双击、长按实现
Button拓展 今天来聊一下关于Button的事件拓展,这里只是拿Button来举例,Unity中其他的UI组件如Toggle、Slider等都也适用。 我们知道在Button中我们可以通过onClick的方式来添加点击事件,但在游戏开发过程中我们往往对Button有着更多的功能需…...
了解财富的本质才能知道自己几斤几两
生活在现代都市中,经历了经济的潮起潮落。在一望无际的楼宇下,是每天匆忙工作的一个个鲜活个体。有的在为了生存而工作,有的在享受着惬意的时光,有人行色匆匆,目光所及之处,尽是可遇不可求的机会。成为中产…...
机器学习模型—K最近邻(KNN)
机器学习模型—K最近邻(KNN) K最近邻 (KNN) 算法是一种用于解决分类和回归问题的监督机器学习方法。Evelyn Fix 和 Joseph Hodges 于 1951 年开发了该算法,随后 Thomas Cover 对其进行了扩展。本文探讨了 KNN 算法的基本原理、工作原理和实现。 虽然 k近邻算法 (KNN) 可以用…...
BUUCTF-----[CISCN 2019 初赛]Love Math
<?php error_reporting(0); //听说你很喜欢数学,不知道你是否爱它胜过爱flag if(!isset($_GET[c])){show_source(__FILE__); }else{//例子 c20-1$content $_GET[c];if (strlen($content) > 80) {die("太长了不会算");}$blacklist [ , \t, \r, \n…...
【前端】处理一次性十万条数据渲染方案(不考虑后端分页)
文章目录 一、定时渲染二、触底加载 一、定时渲染 思路:定时加载,分堆处理 1. 例如,前端请求到十五条数据以后,先不直接渲染,而是将这些数据分堆分批次渲染 2. 比如,一堆放10条数据,十万条数据…...
WPS 云文档保存在本地的地址如何从c盘更改为其他盘?
程序代码园发文地址:WPS 云文档保存在本地的地址如何从c盘更改为其他盘?-程序代码园小说,Java,HTML,Java小工具,程序代码园,http://www.byqws.com/ ,WPS 云文档保存在本地的地址如何从c盘更改为其他盘?http://www.byqws.com/blog/3146.html?…...
每日leetcode--接雨水
引言 接雨水问题是一个经典的算法问题,它要求我们计算给定一组不同高度的墙壁时,这些墙壁之间能够蓄积多少雨水。解决这个问题的方法有很多,其中一种常见的解法是通过辅助数组来记录每个位置的左右最大高度,并计算每个位置上方能…...
redis 性能优化一
目录 前言 尾延迟 前言 说到redis 性能优化,优化的目的是什么?提高响应,减少延迟。就要关注两点,一是尾延迟,二是Redis 的基线性能。只有指标,我们的优化,才有意义,才能做监控以及…...
柔性数组(变长数组)介绍
柔性数组简介 柔性数组,或称为可变长度数组,是一种在C语言结构体定义中使用的特殊数组,它允许结构体拥有一个可变大小的数组成员。柔性数组成员必须是结构体的最后一个成员,且它不占用结构体大小的计算,这使得可以动态…...
AMS、PMS和WMS学习链接
原文: Framework学习(三)之PMS、AMS、WMS_ams pms-CSDN博客 1:PackageMangerService(PMS)讲解博主 PMS系列我觉得csdn博主jeanboy讲的非常好,这里附上博主的博客链接jeanboy。这是一位资深级的博客专家。关于他PMS的讲…...
typedef 在枚举类型enum的使用方式
enum类型用途:为程序中的一组相关的常量取名字,以便以程序的可读性和维护性 方式一:typedef enum 变量名{E_XXXXX = 0,E_xxxx,E_XXXX_MAX }变量名_e; 方式二: typedef enum {E_xxxx = 0,E_xxxx,E_xxx_MAX}变量名_e;#include <stdio.h> typedef enum vii_vpp_mode {E_…...
DDD领域模型驱动
传统MVC架构 DDD架构: api层:api请求方式,透传【传递参数】,几个业务对应api 业务层:做编排,业务里要有哪些服务,执行顺序是什么,以及怎么做 领域层:负责领域内调用,然后领域怎么划分 Dao层:数据库操作【或者另外一个应用 数据源之类的】 遵守原则: ①允许跨层…...
基于pytest的证券清算系统功能测试工具开发
需求 1.造测试数据:根据测试需要,自动化构造各业务场景的中登清算数据与清算所需起来数据 2.测试清算系统功能: 自动化测试方案 工具设计 工具框架图 工具流程图 实现技术 python, pytest, allure, 多进程,mysql, 前端 效果 测…...
土地利用数据分类过程教学/土地利用分类/遥感解译/土地利用获取来源介绍/地理数据获取
本篇主要介绍如何对影像数据进行分类解译,及过程教学,示例数据下载链接:数据下载链接 一、背景介绍 土地是人类赖以生存与发展的重要资源和物质保障,在“人口-资源-环境-发展&#x…...
图【数据结构】
文章目录 图的基本概念邻接矩阵邻接表图的遍历BFSDFS 图的基本概念 图是由顶点集合及顶点间的关系组成的一种数据结构 顶点和边:图中结点称为顶点 权值:边附带的数据信息 路径 : 简单路径 和 回路: 子图:设图G {V, E}和图G1…...
整块代码自动生成、智能括号匹配……CodeGeeX编程提效,功能再升级!
CodeGeeX插件功能持续打磨,希望成为开发者更高效的智能编程工具,提高开发速度和代码质量。今天介绍VSCode中最新的v2.4.0版本插件新功能,让你在编写代码时更加得心应手。 一、新增block代码块生成的设置 CodeGeeX插件中,以往针对…...
java实现计算ROUGE-L指标(一)
ROUGE (Recall-Oriented Understudy for Gisting Evaluation) 是用于评估自动文摘或机器翻译的一种评估方法,其中的ROUGE-L指标是基于最长公共子序列(Longest Common Subsequence,LCS)来计算的 我们做AI问答系统,需要一…...
LLM之RAG实战(二十九)| 探索RAG PDF解析
对于RAG来说,从文档中提取信息是一种不可避免的场景,确保从源文件中提取出有效的内容对于提高最终输出的质量至关重要。 文件解析过程在RAG中的位置如图1所示: 在实际工作中,非结构化数据比结构化数据丰富得多。如果这些海量数据无…...
C while 和 do while 区别
while 和 do while 都是 C 语言中的循环语句,它们的主要区别在于循环体执行的顺序。 while 循环首先检查循环条件,只有当条件为真时才执行循环体。因此,如果条件一开始就为假,那么循环体将永远不会执行。而如果条件一直为真&…...
力扣每日一题 在受污染的二叉树中查找元素 哈希 DFS 二进制
Problem: 1261. 在受污染的二叉树中查找元素 思路 👨🏫 灵神题解 💖 二进制 时间复杂度:初始化为 O ( 1 ) O(1) O(1);find 为 O ( m i n ( h , l o g 2 t a r g e t ) O(min(h,log_2target) O(min(h,log2targ…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
Qt的学习(一)
1.什么是Qt Qt特指用来进行桌面应用开发(电脑上写的程序)涉及到的一套技术Qt无法开发网页前端,也不能开发移动应用。 客户端开发的重要任务:编写和用户交互的界面。一般来说和用户交互的界面,有两种典型风格&…...
41道Django高频题整理(附答案背诵版)
解释一下 Django 和 Tornado 的关系? Django和Tornado都是Python的web框架,但它们的设计哲学和应用场景有所不同。 Django是一个高级的Python Web框架,鼓励快速开发和干净、实用的设计。它遵循MVC设计,并强调代码复用。Django有…...
