
文章目录
- [1. 引言:用户交互的核心作用](#1. 引言:用户交互的核心作用)
-
- [1.1 材质与纹理的核心作用](#1.1 材质与纹理的核心作用)
- [2. 基础交互:鼠标与触摸事件](#2. 基础交互:鼠标与触摸事件)
-
- [2.1 绑定鼠标点击事件](#2.1 绑定鼠标点击事件)
- [2.2 触摸事件适配](#2.2 触摸事件适配)
- [3. 射线检测(Ray Casting)](#3. 射线检测(Ray Casting))
-
- [3.1 射线检测的原理](#3.1 射线检测的原理)
- [3.2 高级射线检测技巧](#3.2 高级射线检测技巧)
- [4. 拖拽物体的实现](#4. 拖拽物体的实现)
-
- [4.1 拖拽基础:平面拖拽](#4.1 拖拽基础:平面拖拽)
- [4.2 3D 空间自由拖拽](#4.2 3D 空间自由拖拽)
- [4.3 拖拽限制(轴向锁定、范围限制)](#4.3 拖拽限制(轴向锁定、范围限制))
- [5. 高级交互技巧](#5. 高级交互技巧)
-
- [5.1 组合交互:拖拽 + 旋转/缩放](#5.1 组合交互:拖拽 + 旋转/缩放)
- [5.2 交互反馈设计](#5.2 交互反馈设计)
- [5.3 性能优化](#5.3 性能优化)
- [6. 实战任务](#6. 实战任务)
-
- [任务 1:实现可拖拽的拼图游戏](#任务 1:实现可拖拽的拼图游戏)
- [任务 2:射线检测射击游戏](#任务 2:射线检测射击游戏)
- [7. 常见问题与解决方案](#7. 常见问题与解决方案)
-
- [7.1 射线检测不准确](#7.1 射线检测不准确)
- [7.2 拖拽时物体穿透地面](#7.2 拖拽时物体穿透地面)
- [8. 总结与下一章预告](#8. 总结与下一章预告)
-
- [8.1 关键知识点回顾](#8.1 关键知识点回顾)
- [8.2 下一章预告](#8.2 下一章预告)
1. 引言:用户交互的核心作用
- 上一章详解灯光与阴影,材质与纹理的相关知识。
- 这一章详细介绍一下Babylon中,用户交互:鼠标点击、拖拽与射线检测。
1.1 材质与纹理的核心作用
- 核心作用 :
- 让用户与3D场景中的物体动态互动(如点击按钮、拖拽物体、触发事件)。
- 提升沉浸感:交互是游戏、数据可视化、虚拟展厅的核心功能。
- 案例对比 :
- 无交互:静态场景,用户只能被动观察。
- 有交互:点击物体弹出信息、拖拽旋转模型、射线检测选中目标。
2. 基础交互:鼠标与触摸事件
2.1 绑定鼠标点击事件
-
Babylon.js 的
ActionManager
:通过事件管理器简化交互逻辑,支持点击、悬停、拖拽等事件。
-
代码示例:点击球体变色
javascript// 创建球 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 自动处理触摸事件,无需额外代码。
-
示例:双指缩放与旋转:
javascriptcamera.attachControl(canvas, true); // 启用触控支持 camera.inputs.add(new BABYLON.FreeCameraTouchInput()); // 添加触控输入
3. 射线检测(Ray Casting)
3.1 射线检测的原理
-
核心机制 :
从屏幕点击位置向3D场景发射一条射线,检测与物体的碰撞点。
-
代码实现:点击选中物体
javascript// 定义场景点击事件 scene.onPointerDown = (evt, pickResult) => { // 判断是否点击到模型 if (pickResult.hit) { const hitMesh = pickResult.pickedMesh; // 高亮选中物体 hitMesh.material.emissiveColor = BABYLON.Color3.Yellow(); } };
3.2 高级射线检测技巧
-
筛选检测目标 :
仅检测特定类型的物体(如可交互的按钮)。
javascriptconst ray = new BABYLON.Ray( camera.position, scene.pointerX, scene.pointerY // 从屏幕坐标生成射线方向 ); const predicate = (mesh) => mesh.name.startsWith("interactive_"); // 仅检测名称前缀为 interactive_ 的物体 const hit = scene.pickWithRay(ray, predicate);
-
长按检测与连续触发:
javascriptlet 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 拖拽基础:平面拖拽
-
步骤分解 :
-
按下时 :检测是否选中物体,记录初始位置。
-
移动时 :根据鼠标位移更新物体位置。
-
释放时:结束拖拽。
-
-
代码示例:
javascriptlet 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 空间自由拖拽
-
基于射线与碰撞点 :
javascriptscene.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 拖拽限制(轴向锁定、范围限制)
-
限制拖拽方向 :
javascript// 只允许沿 X 轴拖拽 draggedMesh.position.x = hit.pickedPoint.x; draggedMesh.position.y = startPosition.y; // 固定 Y 轴 draggedMesh.position.z = startPosition.z; // 固定 Z 轴
-
限制拖拽范围 :
javascriptdraggedMesh.position.x = Math.min(10, Math.max(-10, hit.pickedPoint.x)); // X 轴范围 [-10, 10]
5. 高级交互技巧
5.1 组合交互:拖拽 + 旋转/缩放
- 拽时旋转物体:
javascript
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 交互反馈设计
-
悬停高亮 :
javascriptmesh.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 性能优化
-
减少射线检测频率 :
javascriptlet lastCheck = 0; scene.onPointerMove = () => { if (Date.now() - lastCheck > 100) { // 每 100ms 检测一次 const hit = scene.pick(scene.pointerX, scene.pointerY); lastCheck = Date.now(); } };
-
使用 Octree 加速检测 :
javascriptscene.createOrUpdateSelectionOctree(); // 创建空间索引 const hit = scene.pickWithRay(ray, (mesh) => true, true); // 启用 Octree 优化
6. 实战任务
任务 1:实现可拖拽的拼图游戏
-
目标:拖拽碎片到正确位置后自动吸附。
-
代码要点 :
javascriptif (distanceBetween(draggedMesh.position, targetPosition) < 0.5) { draggedMesh.position = targetPosition.clone(); // 自动吸附 showSuccessEffect(); }
任务 2:射线检测射击游戏
-
目标:点击屏幕发射子弹,击中目标后爆炸。
-
代码要点 :
javascriptscene.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)与视觉不匹配。
-
解决 :
- 为复杂模型设置精确碰撞体
javascriptmesh.checkCollisions = true; mesh.ellipsoid = new BABYLON.Vector3(1, 2, 1); // 自定义碰撞范围
7.2 拖拽时物体穿透地面
-
解决
- 启用物理引擎并添加碰撞约束:
javascriptnew BABYLON.PhysicsImpostor(mesh, BABYLON.PhysicsImpostor.BoxImpostor, { mass: 1, friction: 0.5 }, scene);
8. 总结与下一章预告
8.1 关键知识点回顾
- 件绑定、射线检测、拖拽逻辑、交互反馈设计。
- 扩展方向:
- 手势识别:捏合缩放、滑动旋转。
- VR 交互:通过 WebXR 实现手柄射线交互。
- 多人协作:通过 WebSocket 同步交互状态。
8.2 下一章预告
- 《动画基础:关键帧动画与缓动效果》:创建简单动画,使用动画曲线(Easing Functions)优化效果。