Unity Meta Quest 一体机开发(十三):【手势追踪】自定义交互事件 EventWrapper
文章目录
- 📕教程说明
- 📕交互事件概述
- 📕自定义交互逻辑
- ⭐方法一:Inspector 面板赋值
- ⭐方法二:纯代码处理
此教程相关的详细教案,文档,思维导图和工程文件会放入 Spatial XR 社区。这是一个高质量 XR 社区,博主目前在内担任 XR 开发的讲师。此外,该社区提供教程答疑、及时交流、进阶教程、外包、行业动态等服务。
社区链接:
Spatial XR 高级社区(知识星球)
Spatial XR 高级社区(爱发电)
📕教程说明
这篇教程将会介绍如何用 Meta XR SDK,自定义交互事件触发时执行的逻辑。最后会实现一个功能:用手指点击 UI 按钮后,在前方召唤一把剑。
环境配置可参考:https://blog.csdn.net/qq_46044366/article/details/133967343
配置一个基本的玩家物体可以参考前几期教程:https://blog.csdn.net/qq_46044366/article/details/134097455
系列教程专栏:https://blog.csdn.net/qq_46044366/category_12118293.html
配套的视频链接:
上半部分:https://www.bilibili.com/video/BV1rK4y1B7Zs
下半部分:https://www.bilibili.com/video/BV1vT4y1p7EK
电脑操作系统:Windows 11
使用的 VR 设备:Meta Quest 3(Quest 系列都适用)
使用的 Unity 版本:2021.3.5 LTS (这里推荐使用 2021 及以上的 LTS 版本)
Meta XR SDK 版本:v57
官方文档:https://developer.oculus.com/documentation/unity/unity-gs-overview/
Event Wrapper 介绍文档:
https://developer.oculus.com/documentation/unity/unity-isdk-event-wrappers/
最终效果:
📕交互事件概述
交互事件是 XR 开发中经常使用的一个东西。当一个交互动作发生的时候,可能会伴随着一些事情的发生。比如点击一个 “游戏开始” 的UI按钮,就会触发 “游戏开始” 事件,从而对游戏场景里的一些东西进行初始化。这个时候 “点击UI按钮” 就是一个交互事件,对游戏场景里的东西初始化就是这个事件触发后需要执行的事。在 Meta XR SDK 中,交互事件一般会在一个交互过程中的不同状态下触发,以点击 UI 按钮为例,这个动作属于 Poke 交互,我们知道交互的发生需要有 Interactor 和 Interactable 两个对象的参与,那么 Poke 交互对应的 Interactor 在手上,对应的 Interactable 在 UI 按钮物体上。对于 UI 按钮这个 Interactable 来说,当手指靠近按钮的时候,按钮自身会进入到 Hover 状态,这个时刻就可以当作一个交互事件发生了。然后当手指点击到按钮,将按钮按到底的时候,按钮自身会进入到 Select 状态,这边需要注意的是对于一个可以被推动的按钮来说,必须要推到底,也就是推到按钮 Surface 的位置才会进入到 Select 状态,当手指刚刚触碰到按钮表面的时候是不会进入到 Select 状态的。那么进入到 Select 状态这个瞬间也可以当作一个交互事件发生了。
其实前几期教程我们已经有接触到交互事件,我这里是在场景中添加了一个 Poke 教程里配置的 UI 按钮(教程链接:Unity Meta Quest 一体机开发(十二):【手势追踪】Poke 交互 - 用手指点击由 3D 物体制作的 UI 按钮)。
比如按钮身上的 Interactable Color Visual 脚本,这个脚本能够实现物体在进入不同状态的时候改变物体的颜色。那么这就和交互事件的概念比较像了,进入到不同的状态相当于不同交互事件的触发,改变颜色相当于事件触发后执行的事。Meta XR SDK 会自动检测相应的交互事件什么发生,但是这个脚本把交互事件触发后执行的逻辑写死了,它只能改变物体的颜色,无法处理其他的事。而接下来我会介绍如何去自定义交互事件触发时执行的逻辑。
📕自定义交互逻辑
Meta XR SDK 提供了一个 EventWrapper 的概念,叫做事件包装器(官方文档:https://developer.oculus.com/documentation/unity/unity-isdk-event-wrappers/)。它能够处理 Interaction SDK 中的一些交互事件,并且我们能够自定义交互事件发生时执行的逻辑。如下图所示,Meta 提供了不同类型的 EventWrapper:
我们这期教程主要关注可交互物体,也就是 Interactable 的交互事件,那么可以使用这两种 EventWrapper:InteractableUnityEventWrapper 和 PointableUnityEventWrapper。这两个脚本有一些相同的交互事件,也有一些不同的交互事件。不过最常用的是下图中标出的 4 个共同的交互事件:
Hover 是悬停的状态,Interactor 靠近 Interactable 的时候触发。当交互动作完成的时候,也就是 Interactor 选中 Interactable 时,由 Hover 状态转变为 Select 状态。
我们这期教程会介绍 InteractableUnityEventWrapper 脚本的用法,它用起来会稍微简单一点。
我们需要把 InteractableUnityEventWrapper 脚本添加到 UI 按钮物体上:
我们可以在脚本的 Inspector 面板上看到这个脚本提供的交互事件。
⭐方法一:Inspector 面板赋值
Event Wrapper 提供交互事件,并且检测交互事件什么时候触发。但是触发交互事件后需要执行什么事情需要我们自己处理。第一种处理方式是在 Event Wrapper 脚本的 Inspector 面板上直接进行赋值。我们会实现按下 UI 按钮后在前方生成一把剑的功能,判断什么时候按下 UI 按钮会由 Event Wrapper 来处理,我们这里把按下 UI 按钮视为 Select 交互事件触发,也就是将按钮按到底的时候,触发 Select 事件,然后执行生成剑的逻辑。那么我们需要自己写个脚本实现生成物体的功能,然后让 Event Wrapper 在判断到 Select 事件触发时去调用我们脚本中生成物体的方法。
脚本如下:
using Oculus.Interaction;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ItemSpawner : MonoBehaviour
{public GameObject spawnObj;public Transform spawnPoint;public void SpawnItem(){GameObject obj = GameObject.Instantiate(spawnObj);obj.transform.position = spawnPoint.position;}
}
spawnObj 是需要生成的物体,spawnPoint 是物体生成的位置,SpawnItem 方法负责将需要生成的物体生成在需要生成的位置上。
然后将这个脚本挂载到按钮物体身上,并且在面板上进行赋值:
我这边将一个剑的模型制作成了 Prefab,拖到了 spawnObj 变量上。然后在按钮物体上新建了一个子物体,作为生成剑的位置。
我们可以在场景中调整 SpawnPoint 子物体的位置。
然后点击 InteractableUnityEventWrapper 脚本上的 WhenSelect 事件的 “+”号,进行事件触发执行逻辑的添加:
当按钮被按到底的时候, WhenSelect 事件触发,然后调用 SpawnButton 物体上的 ItemSpawner 脚本中的 SpawnItem 方法,在设定的生成点生成剑的游戏物体。
⭐方法二:纯代码处理
我们修改 ItemSpawner 脚本:
using Oculus.Interaction;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ItemSpawner : MonoBehaviour
{public GameObject spawnObj;public Transform spawnPoint;private InteractableUnityEventWrapper eventWrapper;void Start(){eventWrapper = GetComponent<InteractableUnityEventWrapper>();eventWrapper.WhenSelect.AddListener(SpawnItem);}private void OnDestroy(){eventWrapper.WhenSelect.RemoveListener(SpawnItem);}public void SpawnItem(){GameObject obj = GameObject.Instantiate(spawnObj);obj.transform.position = spawnPoint.position;}
}
和方法一的区别是,我们在代码中获取了 InteractableUnityEventWrapper 脚本,然后用代码为 InteractableUnityEventWrapper 的 WhenSelect 事件添加了事件触发后需要执行的方法。
InteractableUnityEventWrapper 的 WhenSelect 事件的监听器(AddListener 方法)需要传入一个 UnityAction 类型的参数。UnityAction 是 Unity 中的一种委托类型,可以绑定无返回值,无参数的方法。我们写的 SpawnItem 方法正好符合条件,所以能直接传入 AddListener 方法中,相当于我们把 SpawnItem 方法的执行委托给了 Event Wrapper,当 Event Wrapper 检测到 WhenSelect 事件触发后,会执行我们委托的事情,也就是调用 SpawnItem 方法,在设定的生成点生成指定的物体。
另外需要注意的是,当我们调用了 AddListener 后,需要在合适的地方调用 RemoveListener 方法,将事件监听器移除掉。
如果我们选用纯代码的方法处理交互事件,就不用在 Unity 的 Inspector 面板中拖拽赋值了。然后 ItemSpawner 脚本需要和 InteractableUnityEventWrapper 脚本挂载到同一个游戏物体身上。
最终效果:
相关文章:

Unity Meta Quest 一体机开发(十三):【手势追踪】自定义交互事件 EventWrapper
文章目录 📕教程说明📕交互事件概述📕自定义交互逻辑⭐方法一:Inspector 面板赋值⭐方法二:纯代码处理 此教程相关的详细教案,文档,思维导图和工程文件会放入 Spatial XR 社区。这是一个高质量…...

13、Redis高频面试题
1、项目中为什么用Redis 我们项目中之所以选择Redis,主要是因为Redis有下面这些优点: 操作速度快:Redis的数据都保存在内存中,相比于其它硬盘类的存储,速度要快很多数据类型丰富:Redis支持 string&#x…...
Koa学习笔记
1、npm 初始化 npm init -y生成 package.json 文件,记录项目的依赖2、git 初始化 git init生成 .git 隐藏文件夹,.git 的本地仓库创建 .gitignore 文件,添加不提交文件的名称3、创建 ReadMe.md 文件 记录项目笔记4、搭建项目 安装 Koa 框架npm install koa5、编写最基本的…...

HiDataPlus 3.3.2-005 搭建(个人的一点心得体会 x86 平台)
HDP 集群搭建 前置安装 yum -y install createrepo yum install -y lrzsz yum install -y wget yum install -y vim修改当前集群机器的主机名 hostnamectl set-hostname XXX 这里的 XXX 就是要设置的当前机器的主机名称。主机名称是集群唯一的,一定不要重复&am…...

【PHP】PHP实现与硬件串口交互,接收硬件发送的实时数据
一、前言 目的:借助虚拟串口软件(VSPD)模拟硬件串口发送数据,使用PHP语言实现接收硬件发送的数据。 我这里的需求是连接天平,把天平的称量数据实时的传送到PHP使用。 使用工具:vspd串口调试工具 使用语…...

HNU-数据库系统-作业
数据库系统-作业 计科210X 甘晴void 202108010XXX 第一章作业 10.09 1.(名词解释)试述数据、数据库、数据库管理系统、数据库系统的概念。 数据,是描述事物的符号记录。 数据库(DB),是长期存储在计算机内、有组织、可共享的大量…...

Python基础知识:整理10 异常相关知识
1 异常的捕获 1.1 基础写法 """基本语法:try:可能发生错误的代码except:如果出现异常,将执行的代码""" try:fr open("D:/abc.txt", "r", encoding"utf-8") except:print("出现异常…...

golang并发安全-select
前面说了golang的channel, 今天我们看看golang select 是怎么实现的。 数据结构 type scase struct {c *hchan // chanelem unsafe.Pointer // 数据 } select 非默认的case 中都是处理channel 的 接受和发送,所有scase 结构体中c是用来存储…...

微软Visual Studio产品之Visual C++编程进阶——一维数组(画画版)
我是荔园微风,作为一名在IT界整整25年的老兵,看到不少初学者在学习编程语言的过程中如此的痛苦,我决定做点什么,我小时候喜欢看小人书(连环画),在那个没有电视、没有手机的年代,这是…...
Moonbeam生态项目分析 — — 下一代DeFi协议HydraDX
作者:David 概览 HydraDX是一个基于Substrate区块链框架构建的DeFi协议,旨在为波卡带来大量流动性。HydraDX的特色服务,HydraDX Omnipool,是一种创新的自动化做市商(AMM),通过将所有资产组合在…...
Spark九:Spark调优之Shuffle调优
Spark shuffle调优方法 map端和reduce端缓存大小设置,reduce端重试次数和等待时间间隔,以及bypass设置 学习资料:https://mp.weixin.qq.com/s/caCk3mM5iXy0FaXCLkDwYQ 一、map和reduce端缓冲区大小 1.1 map端 在Spark任务运行过程中&…...
linux c多线程优先级
在 Linux 系统中,可以使用 pthread_setschedparam 函数来设置线程的优先级。该函数需要传入一个指向 pthread_t 类型的线程 ID,以及一个指向 struct sched_param 类型的结构体对象。struct sched_param 结构体包含了线程的优先级信息。 下面是一个示例代…...
Redis在项目开发中的应用
Spring Boot集成Redis构建博客应用 在这个示例中,我们将展示如何使用Spring Boot和Redis构建一个简单的博客应用,包括文章发布、点赞和评论功能。 1. 添加依赖 首先,我们需要在pom.xml文件中添加Spring Boot和Redis的依赖项。 <!-- Sp…...

mapper向mapper.xml传参中文时的乱码问题
1.起因: 在idea中进行模糊查询传参时,发现在idea中查中文查不出记录,在navicate中可以查出来。 2.猜测: 1.idea中的编码问题导致的乱码。 2.idea和navicate的编码一致性导致的乱码。 3.mapper向mapper.xml传参后出现乱码。 3.解…...
基于Docker官方php:7.1.33-fpm镜像构建支持67个常见模组的php7.1.33镜像
实践说明:基于RHEL7(CentOS7.9)部署docker环境(23.0.1、24.0.2),所构建的php7.1.33镜像应用于RHEL7-9(如AlmaLinux9.1),但因为docker的特性,适用场景是不限于此的。 文档形成时期:2017-2023年 因系统或软件版本不同&am…...

Type-C PD充电器受电端sink诱骗取电汇总:小家电应用5V9V12V15V20V28V
小家电产品、美容产品、电动产品等升级采用Type-C接口,在Type-C接口上使用Type-C取电芯片,即可使用快速充电器的5V、9V、12V、15V、20V供电,无需再配充电器,各类品牌的充电器都可以支持。目前充电器常见的USB-PD功率为:…...

禁用code server docker容器中的工作区信任提示
VSCode 添加受限模式,主要是防止自动运行代码的,比如在vscode配置的task和launch参数是可以运行自定义代码的。如果用VScode打开未知的工程文件就有可能直接运行恶意代码。 但是当我们的实验基础模板文件可控的情况下,要想禁用code server do…...
JSON格式插件-VUE
JsonEditor 安装: npm i bin-code-editor -S引入: import Vue from vue; import CodeEditor from bin-code-editor; import bin-code-editor/lib/style/index.css; import App from ./App.vue; Vue.use(CodeEditor); new Vue({el: #app,render: h > …...

dubbo的springboot集成
1.什么是dubbo? Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用 Dubbo …...

【人工智能】智能电网:未来能源的革命
未来能源的革命 智能电网革命的意义在于将电力行业从传统的集中式发电和集中式输配电模式转变为智能化、分布式、互动式的能源网络。 现在我们从以下方面详细认真的了解一下智能电网: 智能变电站,智能配电网,智能电能表,智能交互…...

利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

WPF八大法则:告别模态窗口卡顿
⚙️ 核心问题:阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程,导致后续逻辑无法执行: var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题:…...

SQL注入篇-sqlmap的配置和使用
在之前的皮卡丘靶场第五期SQL注入的内容中我们谈到了sqlmap,但是由于很多朋友看不了解命令行格式,所以是纯手动获取数据库信息的 接下来我们就用sqlmap来进行皮卡丘靶场的sql注入学习,链接:https://wwhc.lanzoue.com/ifJY32ybh6vc…...