Babylon.js学习之路《七、用户交互:鼠标点击、拖拽与射线检测》
文章目录
- 1. 引言:用户交互的核心作用
- 1.1 材质与纹理的核心作用
- 2. 基础交互:鼠标与触摸事件
- 2.1 绑定鼠标点击事件
- 2.2 触摸事件适配
- 3. 射线检测(Ray Casting)
- 3.1 射线检测的原理
- 3.2 高级射线检测技巧
- 4. 拖拽物体的实现
- 4.1 拖拽基础:平面拖拽
- 4.2 3D 空间自由拖拽
- 4.3 拖拽限制(轴向锁定、范围限制)
- 5. 高级交互技巧
- 5.1 组合交互:拖拽 + 旋转/缩放
- 5.2 交互反馈设计
- 5.3 性能优化
- 6. 实战任务
- 任务 1:实现可拖拽的拼图游戏
- 任务 2:射线检测射击游戏
- 7. 常见问题与解决方案
- 7.1 射线检测不准确
- 7.2 拖拽时物体穿透地面
- 8. 总结与下一章预告
- 8.1 关键知识点回顾
- 8.2 下一章预告
1. 引言:用户交互的核心作用
- 上一章详解灯光与阴影,材质与纹理的相关知识。
- 这一章详细介绍一下Babylon中,用户交互:鼠标点击、拖拽与射线检测。
1.1 材质与纹理的核心作用
- 核心作用:
- 让用户与3D场景中的物体动态互动(如点击按钮、拖拽物体、触发事件)。
- 提升沉浸感:交互是游戏、数据可视化、虚拟展厅的核心功能。
- 案例对比:
- 无交互:静态场景,用户只能被动观察。
- 有交互:点击物体弹出信息、拖拽旋转模型、射线检测选中目标。
2. 基础交互:鼠标与触摸事件
2.1 绑定鼠标点击事件
-
Babylon.js 的
ActionManager
:
通过事件管理器简化交互逻辑,支持点击、悬停、拖拽等事件。 -
代码示例:点击球体变色
// 创建球 var sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 2, segments: 32}, scene); sphere.position = new BABYLON.Vector3(-1.5, 1, 0); // 创建PBR材质,金属度0,粗糙度0.7,反射颜色白色 var sphereMat = new BABYLON.PBRMaterial("sphereMat", scene) sphereMat.metallic = 0; sphereMat.roughness = 0.7; sphereMat.albedoColor = BABYLON.Color3.White(); sphere.material = sphereMat; // 创建点击事件 sphere.actionManager = new BABYLON.ActionManager(scene); // 绑定点击事件 sphere.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger, // 触发条件:点击() => {sphere.material.albedoColor = BABYLON.Color3.Random(); // 随机颜色}) );
2.2 触摸事件适配
-
移动端兼容性:
Babylon.js 自动处理触摸事件,无需额外代码。 -
示例:双指缩放与旋转:
camera.attachControl(canvas, true); // 启用触控支持camera.inputs.add(new BABYLON.FreeCameraTouchInput()); // 添加触控输入
3. 射线检测(Ray Casting)
3.1 射线检测的原理
-
核心机制:
从屏幕点击位置向3D场景发射一条射线,检测与物体的碰撞点。 -
代码实现:点击选中物体
// 定义场景点击事件 scene.onPointerDown = (evt, pickResult) => {// 判断是否点击到模型if (pickResult.hit) {const hitMesh = pickResult.pickedMesh;// 高亮选中物体hitMesh.material.emissiveColor = BABYLON.Color3.Yellow();} };
3.2 高级射线检测技巧
-
筛选检测目标:
仅检测特定类型的物体(如可交互的按钮)。const ray = new BABYLON.Ray(camera.position, scene.pointerX, scene.pointerY // 从屏幕坐标生成射线方向);const predicate = (mesh) => mesh.name.startsWith("interactive_"); // 仅检测名称前缀为 interactive_ 的物体const hit = scene.pickWithRay(ray, predicate);
-
长按检测与连续触发:
let isHolding = false;scene.onPointerObservable.add((pointerInfo) => {if (pointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {isHolding = true;// 开始长按检测const interval = setInterval(() => {if (!isHolding) clearInterval(interval);// 持续触发逻辑(如充能效果)}, 100);} else if (pointerInfo.type === BABYLON.PointerEventTypes.POINTERUP) {isHolding = false;}});
4. 拖拽物体的实现
4.1 拖拽基础:平面拖拽
-
步骤分解:
1. 按下时:检测是否选中物体,记录初始位置。
2. 移动时:根据鼠标位移更新物体位置。
3. 释放时:结束拖拽。 -
代码示例:
let draggedMesh = null; let startPosition = null;scene.onPointerDown = (evt, pickResult) => { if (pickResult.hit) {draggedMesh = pickResult.pickedMesh;startPosition = draggedMesh.position.clone(); } };scene.onPointerMove = () => { if (draggedMesh) {const ray = scene.createPickingRay(scene.pointerX, scene.pointerY);const hit = scene.pickWithRay(ray);if (hit.pickedPoint) {// 在平面上拖拽(Y轴固定)draggedMesh.position.x = hit.pickedPoint.x;draggedMesh.position.z = hit.pickedPoint.z;} } };scene.onPointerUp = () => { draggedMesh = null; };
4.2 3D 空间自由拖拽
- 基于射线与碰撞点:
scene.onPointerMove = () => {if (draggedMesh) {const ray = scene.createPickingRay(scene.pointerX, scene.pointerY);const hit = scene.pickWithRay(ray);if (hit.pickedPoint) {draggedMesh.position = hit.pickedPoint; // 直接移动到碰撞点}}};
4.3 拖拽限制(轴向锁定、范围限制)
- 限制拖拽方向:
// 只允许沿 X 轴拖拽draggedMesh.position.x = hit.pickedPoint.x;draggedMesh.position.y = startPosition.y; // 固定 Y 轴draggedMesh.position.z = startPosition.z; // 固定 Z 轴
- 限制拖拽范围:
draggedMesh.position.x = Math.min(10, Math.max(-10, hit.pickedPoint.x)); // X 轴范围 [-10, 10]
5. 高级交互技巧
5.1 组合交互:拖拽 + 旋转/缩放
- 拽时旋转物体:
let time = 0;scene.registerBeforeRender(() => {material.diffuseColor = new BABYLON.Color3(Math.sin(time) * 0.5 + 0.5, // R通道Math.cos(time) * 0.5 + 0.5, // G通道0.5 // B通道);time += 0.02;});
5.2 交互反馈设计
- 悬停高亮:
mesh.actionManager.registerAction(new BABYLON.SetValueAction(BABYLON.ActionManager.OnPointerOverTrigger,mesh.material,"emissiveColor",BABYLON.Color3.White() // 悬停时发光));mesh.actionManager.registerAction(new BABYLON.SetValueAction(BABYLON.ActionManager.OnPointerOutTrigger,mesh.material,"emissiveColor",BABYLON.Color3.Black() // 恢复原色));
5.3 性能优化
- 减少射线检测频率:
let lastCheck = 0;scene.onPointerMove = () => {if (Date.now() - lastCheck > 100) { // 每 100ms 检测一次const hit = scene.pick(scene.pointerX, scene.pointerY);lastCheck = Date.now();}};
- 使用 Octree 加速检测:
scene.createOrUpdateSelectionOctree(); // 创建空间索引const hit = scene.pickWithRay(ray, (mesh) => true, true); // 启用 Octree 优化
6. 实战任务
任务 1:实现可拖拽的拼图游戏
- 目标:拖拽碎片到正确位置后自动吸附。
- 代码要点:
if (distanceBetween(draggedMesh.position, targetPosition) < 0.5) {draggedMesh.position = targetPosition.clone(); // 自动吸附showSuccessEffect();}
任务 2:射线检测射击游戏
- 目标:点击屏幕发射子弹,击中目标后爆炸。
- 代码要点:
scene.onPointerDown = (evt, pickResult) => {if (pickResult.hit) {const explosion = BABYLON.MeshBuilder.CreateSphere("explosion", { diameter: 2 }, scene);explosion.position = pickResult.pickedPoint;explosion.material = new BABYLON.StandardMaterial("explodeMat", scene);explosion.material.emissiveColor = BABYLON.Color3.Red();setTimeout(() => explosion.dispose(), 500); // 0.5秒后消失}};
7. 常见问题与解决方案
7.1 射线检测不准确
- 原因:模型碰撞边界(Bounding Box)与视觉不匹配。
- 解决:
- 为复杂模型设置精确碰撞体
mesh.checkCollisions = true;mesh.ellipsoid = new BABYLON.Vector3(1, 2, 1); // 自定义碰撞范围
7.2 拖拽时物体穿透地面
- 解决
- 启用物理引擎并添加碰撞约束:
new BABYLON.PhysicsImpostor(mesh, BABYLON.PhysicsImpostor.BoxImpostor, {mass: 1,friction: 0.5}, scene);
8. 总结与下一章预告
8.1 关键知识点回顾
- 件绑定、射线检测、拖拽逻辑、交互反馈设计。
- 扩展方向:
- 手势识别:捏合缩放、滑动旋转。
- VR 交互:通过 WebXR 实现手柄射线交互。
- 多人协作:通过 WebSocket 同步交互状态。
8.2 下一章预告
- 《动画基础:关键帧动画与缓动效果》:创建简单动画,使用动画曲线(Easing Functions)优化效果。
相关文章:

Babylon.js学习之路《七、用户交互:鼠标点击、拖拽与射线检测》
文章目录 1. 引言:用户交互的核心作用1.1 材质与纹理的核心作用 2. 基础交互:鼠标与触摸事件2.1 绑定鼠标点击事件2.2 触摸事件适配 3. 射线检测(Ray Casting)3.1 射线检测的原理3.2 高级射线检测技巧 4. 拖拽物体的实现4.1 拖拽基…...

星际争霸小程序:用Java实现策略模式的星际大战
在游戏开发的世界里,策略模式是一种非常实用的设计模式,它允许我们在运行时动态地选择算法或行为。今天,我将带你走进一场星际争霸的奇幻之旅,用Java实现一个简单的星际争霸小程序,通过策略模式来模拟不同种族单位的战…...
请问交换机和路由器的区别?vlan 和 VPN 是什么?
交换机和路由器的区别 特性交换机(Switch)路由器(Router)工作层级数据链路层(L2,基于MAC地址)网络层(L3,基于IP地址)主要功能在局域网(LAN&#…...
BERT 作为Transformer的Encoder 为什么采用可学习的位置编码
摘要 BERT 在位置编码上与原始 Transformer 论文中的 sin/cos 公式不同,选择了可学习(learned)的位置嵌入方案。本文将从 Transformer 原始位置编码选项入手,分析 BERT 选择 learned positional embeddings 的四大核心原因&#x…...

Python数据可视化高级实战之一——绘制GE矩阵图
目录 一、课程概述 二、GE矩阵? 三、GE 矩阵图的适用范围 五、GE 矩阵的评估方法 (一)市场吸引力的评估要素 二、企业竞争实力的评估要素 三、评估方法与实践应用 1. 定量与定性结合法 2. 数据来源 六、GE矩阵的图形化实现 七、总结:GE 矩阵与 BCG 矩阵的对比分析 (一)GE…...

StreamSaver实现大文件下载解决方案
StreamSaver实现大文件下载解决方案 web端 安装 StreamSaver.js npm install streamsaver # 或 yarn add streamsaver在 Vue 组件中导入 import streamSaver from "streamsaver"; // 确保导入名称正确完整代码修正 <!--* projectName: * desc: * author: dua…...
【Vue 3全栈实战】从响应式原理到企业级架构设计
目录 🌟 前言🏗️ 技术背景与价值🩹 当前技术痛点🛠️ 解决方案概述👥 目标读者说明 🧠 一、技术原理剖析📊 核心概念图解💡 核心作用讲解🔧 关键技术模块说明⚖️ 技术选…...
Java线程池调优与实践经验
在Java面试中,线程池调优是一个常见且重要的考察点,尤其是当涉及Spring生态时,ThreadPoolTaskExecutor的使用经验通常会被深入追问。以下是针对该问题的结构化回答,结合原理、实践和调优经验: 1. 线程池调优的核心参数…...
【科研项目】大三保研人科研经历提升
大三保研人,五月科研项目经历提升 现在已经是五月下旬,各大高校的夏令营通知陆续发布,九月的预推免也近在眼前。我知道很多大三的同学正在焦虑——绩点已经定型,竞赛经历又不够丰富,简历上能写的东西太少,面…...
期刊采编系统安装升级错误
我们以ojs系统为例: PHP Fatal error: Uncaught Error: Call to a member function getId() on null in /esci/data/html/classes/install/Upgrade.inc.php:1019 Stacktrace: #0 /esci/data/html/lib/pkp/classes/install/Installer.inc.php(415): Upgrade->con…...

CSS【详解】弹性布局 flex
适用场景 一维(行或列)布局 基本概念 包裹所有被布局元素的父元素为容器 所有被布局的元素为项目 项目的排列方向(垂直/水平)为主轴 与主轴垂直的方向交交叉轴 容器上启用 flex 布局 将容器的 display 样式设置为 flex 或 i…...

自回归图像编辑 EditAR: Unified Conditional Generation with Autoregressive Models
Paperhttps://arxiv.org/pdf/2501.04699 Code (coming soon) 目录 方法 实验 EditAR是一个统一的自回归框架,用于各种条件图像生成任务——图像编辑、深度到图像、边缘到图像、分割到图像。 next-token预测的功效尚未被证明用于图像编辑。 EditAR主要构建在Ll…...

React Flow 中 Minimap 与 Controls 组件使用指南:交互式小地图与视口控制定制(含代码示例)
本文为《React Agent:从零开始构建 AI 智能体》专栏系列文章。 专栏地址:https://blog.csdn.net/suiyingy/category_12933485.html。项目地址:https://gitee.com/fgai/react-agent(含完整代码示例与实战源)。完整介绍…...
基于YOLOv8 的分类道路目标系统-PyTorch实现
本文源码: https://download.csdn.net/download/shangjg03/90873939 1. 引言 在智能交通和自动驾驶领域,道路目标分类是一项关键技术。通过对摄像头捕获的图像或视频中的目标进行分类识别,可以帮助车辆或系统理解周围环境,做出更安全的决策。本教程将介绍如何使用 PyTorch …...

STM32之串口通信WIFI上云
一、W模块的原理与应用 基本概念 如果打算让硬件设备可以通过云服务器进行通信(数据上报/指令下发),像主流的云服务器有阿里云、腾讯云、华为云,以及其他物联网云平台:巴法云.......,硬件设备需要通过TCP…...

PCB智能报价系统——————仙盟创梦IDE
软件署名 代码贡献: 紫金电子科技有限公司 文案正路:cybersnow 正文 对企业的竞争力有着深远影响。传统的 PCB 报价方式往往依赖人工核算,不仅耗时较长,还容易出现误差。随着科技的发展,PCB 自动报价系统应运而生&a…...
EXO分布式部署deepseek r1
EXO 是一个支持分布式 AI 计算的框架,可以用于在多个设备(包括 Mac Studio)上运行大语言模型(LLM)。以下是联调 Mac Studio 512GB 的步骤: 安装 EXO • 从 EXO GitHub 仓库 下载源码或使用 git clone 获取…...
每日算法 -【Swift 算法】寻找两个有序数组的中位数(O(log(m+n)))详细讲解版
🧠 用 Swift 寻找两个有序数组的中位数(O(log(mn)))详细讲解版 寻找两个有序数组的中位数,是 LeetCode 上非常经典的一道题,难度为 困难(Hard),但它的本质是一个 二分查找 的变形应…...
Linux问题排查-找到偷偷写文件的进程
在 Linux 系统中,若要通过已修改的文件找到修改该文件的进程 PID,可以结合以下方法分析,具体取决于文件是否仍被进程打开或已被删除但句柄仍存在: 一、文件仍被进程打开(未删除) 如果文件当前正在被某个进…...
SOPHGO算能科技BM1688内存使用与编解码开发指南
1. BM1688内存分配接口详解 1.1 设备内存分配接口区别 BM1688提供了三个主要的设备内存分配接口,它们的主要区别如下: // 基本设备内存分配接口 void* bm_malloc_device_byte(bm_handle_t handle, unsigned int size);// 指定heap区域的设备内存分配 void*</...
kotlin flow的两种SharingStarted策略的区别
一 两种 SharingStarted 策略的区别: SharingStarted.Eagerly: 立即开始收集上游流,即使没有下游订阅者持续保持活跃状态,直到 ViewModel 被清除优点:响应更快,数据始终保持最新缺点:消耗更多资源&#x…...

LeetCode-链表-合并两个有序链表
LeetCode-链表-合并两个有序链表 ✏️ 关于专栏:专栏用于记录 prepare for the coding test。 文章目录 LeetCode-链表-合并两个有序链表📝 合并两个有序链表🎯题目描述🔍 输入输出示例🧩题目提示🧪AC递归&…...

sqli-labs靶场29-31关(http参数污染)
目录 前言 less29(单引号http参数污染) less30(双引号http参数污染) less31(双引号括号http参数污染) 前言 在JSP中,使用request.getParameter("id")获取请求参数时,如果存在多个同名参数&a…...
独占内存访问指令LDXR/STXR
一、原子操作的介绍 在计算机领域里,如果要在多线程的情况下要保持数据的同步,需要引入称作Load-Link(LL)和Store-Conditional(SC)的操作,通常简称为LL/SC。 LL操作返回一个内存地址上当前存储…...

JVM 垃圾回收机制深度解析(含图解)
JVM 垃圾回收机制深度解析(含图解) 一、垃圾回收整体流程 垃圾回收图解 #mermaid-svg-KPtxlwWntQx8TOj3 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-KPtxlwWntQx8TOj3 .error-icon{fill…...

如何利用 Conda 安装 Pytorch 教程 ?
如何利用 Conda 安装 Pytorch 教程 ? 总共分为六步走: (1)第一步:验证conda 环境是否安装好? 1) conda -V2) conda --version(2)第二步:查看现有环境 conda env list…...
【ffmpeg】SPS与PPS的概念
PPS(Picture Parameter Set)详解 PPS(图像参数集)是H.264/H.265视频编码标准中的关键数据结构,与SPS(序列参数集)共同组成视频的解码配置信息,直接影响视频的正确解码和播放。以下是…...

uniapp vue 开发微信小程序 分包梳理经验总结
嗨,我是小路。今天主要和大家分享的主题是“uniapp vue 开发微信小程序 分包梳理经验总结”。 在使用 UniAppvue框架开发微信小程序时,当项目比较大的时候,经常需要分包加载。它有助于控制主包的大小,从而提升小程序的启…...

什么是VR展示?VR展示的用途
随着科技的迅猛发展,我们步入一个全新的数字时代。在这个时代,虚拟现实(VR)技术崭露头角,逐步改变我们对世界的认知。全景展示厅作为VR技术与传统展览艺术的完美结合,以独特的全景视角,引领我们…...

.NET外挂系列:4. harmony 中补丁参数的有趣玩法(上)
一:背景 1. 讲故事 前面几篇我们说完了 harmony 的几个注入点,这篇我们聚焦注入点可接收的几类参数的解读,非常有意思,在.NET高级调试 视角下也是非常重要的,到底是哪些参数,用一张表格整理如下ÿ…...