文章目錄
- 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
:
通過事件管理器簡化交互邏輯,支持點擊、懸停、拖拽等事件。 -
代碼示例:點擊立方體變色
const box = BABYLON.MeshBuilder.CreateBox("box", {}, scene);box.actionManager = new BABYLON.ActionManager(scene);// 綁定點擊事件box.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger, // 觸發條件:點擊() => {box.material.diffuseColor = 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)優化效果。