【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…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...
