首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >JSAR 鼠标交互接球游戏开发实践

JSAR 鼠标交互接球游戏开发实践

原创
作者头像
用户11861307
发布2025-10-16 02:57:36
发布2025-10-16 02:57:36
80
举报

项目概述

本项目是一个基于 JSAR(JavaScript Spatial Augmented Reality)框架和 Babylon.js 引擎开发的 3D 交互游戏,专为 Rokid AR 眼镜设计。游戏采用简洁的玩法:玩家通过手指移动(本地使用鼠标模拟)控制黄色球体,移动去接触随机生成的红色目标球,每次成功接触即可得分。

技术栈

- JSAR: Rokid AR 空间计算框架

- Babylon.js: 3D 渲染引擎

- TypeScript: 开发语言

- XSML: 空间场景标记语言

场景结构与核心设计

XSML 场景定义

项目使用 XSML 作为入口文件,定义了空间场景的基本结构:

<xsml version="1.0"> <head> <title>接球游戏</title> <script src="./lib/main.ts"></script> </head> <space> </space> </xsml>

XSML 提供了轻量级的场景声明方式,所有游戏逻辑都在 TypeScript 模块中实现。

核心代码深度解析

1. 场景初始化

游戏采用俯视视角,创建了一个 20x20 的网格地面作为游戏区域:

// 场景设置 scene.clearColor = new BABYLON.Color4(0.1, 0.1, 0.15, 1); // 光照 const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, 1, 0), scene); light.intensity = 1.2; // 创建地面与网格线 const ground = BABYLON.MeshBuilder.CreateGround('ground', { width: GRID_SIZE * CELL_SIZE, height: GRID_SIZE * CELL_SIZE }, scene);

网格线的绘制增强了空间感知,帮助玩家更好地判断位置:

for (let i = 0; i <= GRID_SIZE; i++) { const lineH = BABYLON.MeshBuilder.CreateBox('lineH' + i, { width: GRID_SIZE * CELL_SIZE, height: 0.05, depth: 0.05 }, scene); lineH.position = new BABYLON.Vector3(0, 0.01, i * CELL_SIZE - GRID_SIZE * CELL_SIZE / 2); // ... 垂直网格线类似 }

2. 玩家与目标对象

黄色玩家球带有发光效果和呼吸动画:

const player = BABYLON.MeshBuilder.CreateSphere('player', { diameter: CELL_SIZE * 0.8 }, scene); const playerMat = new BABYLON.StandardMaterial('playerMat', scene); playerMat.diffuseColor = new BABYLON.Color3(1, 1, 0.3); playerMat.emissiveColor = new BABYLON.Color3(0.5, 0.5, 0); // 自发光 player.material = playerMat; // 呼吸动画 let pulseTime = 0; scene.onBeforeRenderObservable.add(() => { pulseTime += 0.05; const scale = 1 + Math.sin(pulseTime) * 0.1; player.scaling = new BABYLON.Vector3(scale, scale, scale); });

红色目标球随机生成在网格上:

function createTarget() { if (target) { target.dispose(); target = null; } // 随机网格坐标 targetGridX = Math.floor(Math.random() * GRID_SIZE); targetGridY = Math.floor(Math.random() * GRID_SIZE); target = BABYLON.MeshBuilder.CreateSphere('target_' + Date.now(), { diameter: CELL_SIZE * 0.8 }, scene); const targetMat = new BABYLON.StandardMaterial('targetMat_' + Date.now(), scene); targetMat.diffuseColor = new BABYLON.Color3(1, 0.2, 0.2); targetMat.emissiveColor = new BABYLON.Color3(0.5, 0, 0); target.material = targetMat; // 坐标转换:网格 -> 世界坐标 const targetX = targetGridX * CELL_SIZE - GRID_SIZE * CELL_SIZE / 2 + CELL_SIZE / 2; const targetZ = targetGridY * CELL_SIZE - GRID_SIZE * CELL_SIZE / 2 + CELL_SIZE / 2; target.position = new BABYLON.Vector3(targetX, CELL_SIZE / 2, targetZ); }

3. JSAR 鼠标交互系统

这是项目的核心技术突破。JSAR 的交互系统与传统 Babylon.js 的 ActionManager 不同,需要使用专门的输入事件 API。

关键发现:必须先调用 `watchInputEvent()` 启用输入监听:

try { (spatialDocument as any).watchInputEvent(); console.log('[OK] watchInputEvent() 调用成功'); } catch (error) { console.log('[ERROR] watchInputEvent() 调用失败:', error); }

动态坐标映射算法

鼠标原始坐标需要映射到游戏网格,但不同设备的坐标范围不同。解决方案是动态追踪坐标范围:

let mouseEventCount = 0; let minMouseX = 999999, maxMouseX = 0; let minMouseY = 999999, maxMouseY = 0; spatialDocument.addEventListener('mouse', (event: any) => { if (!event.inputData) return; const mouseX = event.inputData.PositionX; const mouseY = event.inputData.PositionY; // 关键:过滤无效坐标 if (mouseX === 0 || mouseY === 0) { return; } // 记录坐标范围 minMouseX = Math.min(minMouseX, mouseX); maxMouseX = Math.max(maxMouseX, mouseX); minMouseY = Math.min(minMouseY, mouseY); maxMouseY = Math.max(maxMouseY, mouseY); const rangeX = maxMouseX - minMouseX; const rangeY = maxMouseY - minMouseY; // 只有足够的移动范围才更新位置 if (rangeX > 20 && rangeY > 20) { const normalizedX = (mouseX - minMouseX) / rangeX; const normalizedY = (mouseY - minMouseY) / rangeY; playerGridX = Math.floor(normalizedX * GRID_SIZE); playerGridY = Math.floor(normalizedY * GRID_SIZE); // 限制在网格内 playerGridX = Math.max(0, Math.min(GRID_SIZE - 1, playerGridX)); playerGridY = Math.max(0, Math.min(GRID_SIZE - 1, playerGridY)); updatePlayerPosition(); } });

设计亮点

1. 过滤无效坐标:(0,0) 坐标会破坏范围计算,必须过滤

2. 动态范围适配:不依赖固定坐标范围,自动适应不同设备

3. 最小阈值检测:rangeX/Y > 20 确保有足够的移动数据才开始映射

4. 碰撞检测与得分系统

采用网格坐标精确匹配的方式检测碰撞:

if (playerGridX === targetGridX && playerGridY === targetGridY) { score++; console.log('[SUCCESS] 得分成功!'); console.log('当前得分: ' + score); // 创建新目标 createTarget(); // 玩家闪烁反馈 const originalColor = playerMat.diffuseColor.clone(); playerMat.diffuseColor = new BABYLON.Color3(0, 1, 0); setTimeout(() => { playerMat.diffuseColor = originalColor; }, 200); }

游戏界面展示

俯视角度展示了完整的网格地面,黄色玩家球位于左上角,红色目标球位于中右区域,网格线清晰可见,提供了良好的空间定位参考。

透视角度展示了 3D 效果,可以看到地面网格的透视变化和球体的立体感,底部的 AR 控制图标清晰可见。

控制台输出显示了玩家从 [3,17] 移动到 [9,9] 的过程,最终成功与目标重合,得分为 1。

得分后,目标切换,黄色球和红色球的位置发生了变化,得分后目标球自动移动到了新的随机位置 [0,15]。

控制台日志详细记录了目标创建过程,包括网格坐标、世界坐标、Mesh 名称和场景中的对象总数,便于调试。

卡点回顾

卡点1:JSAR 输入事件不触发

问题:初期使用 Babylon.js 的 ActionManager,点击事件完全无响应。

解决

● 发现 JSAR 需要显式调用 watchInputEvent()

● 改用 spatialDocument.addEventListener('mouse', ...) 模式

卡点2:鼠标坐标映射不准确

问题

● 玩家位置始终为 [0,0]

● 坐标范围在不同设备上不一致

● 出现 (0,0) 无效坐标污染数据

解决方案

// 1. 过滤无效坐标 if (mouseX === 0 || mouseY === 0) return; // 2. 动态范围追踪 minMouseX = Math.min(minMouseX, mouseX); maxMouseX = Math.max(maxMouseX, mouseX); // 3. 归一化映射 const normalizedX = (mouseX - minMouseX) / rangeX; playerGridX = Math.floor(normalizedX * GRID_SIZE);

项目结构

jsar-rokid/ ├── main.xsml # XSML 场景入口 ├── lib/ │ └── main.ts # 游戏主逻辑(255 行) ├── package.json # 项目配置 ├── model/ │ └── welcome.glb # 3D 模型资源 └── icon.png # 应用图标

开发总结

本项目成功实现了 JSAR 框架下的鼠标交互机制,通过动态坐标映射算法解决了跨设备输入适配问题。简洁的游戏设计验证了 JSAR 在 AR 应用开发中的可行性,为后续更复杂的空间交互应用奠定了基础。

关键技术突破在于理解 JSAR 的输入事件模型,以及实现自适应的坐标归一化算法。这些经验对于开发 Rokid AR 应用具有重要的参考价值。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 项目概述
  • 技术栈
  • 场景结构与核心设计
    • XSML 场景定义
  • 核心代码深度解析
    • 1. 场景初始化
    • 2. 玩家与目标对象
    • 3. JSAR 鼠标交互系统
    • 4. 碰撞检测与得分系统
  • 游戏界面展示
  • 卡点回顾
    • 卡点1:JSAR 输入事件不触发
    • 卡点2:鼠标坐标映射不准确
  • 项目结构
  • 开发总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档