深入理解JavaScript系列():揭秘命名函数表达式
诽闹级瓤项目背景最近在研究 Rokid AR 眼镜的开发想做点有意思的东西。看了一圈案例发现大家都在做一些比较正经的应用——导航、信息展示之类的。我就想能不能整点不一样的游戏而且是用语音控制的游戏。这篇文章记录了我从零开始用 JSAR 框架开发一款太空射击小游戏的全过程。代码不复杂效果还挺酷的。image技术选型框架: JSAR (Rokid 官方的空间应用运行时)3D 引擎: Babylon.js目标设备: Rokid AR 智能眼镜开发环境: VSCode JSAR 扩展游戏设计在写代码之前先把游戏玩法想清楚元素设计玩家角色三角形飞船可左右移动敌人随机生成的陨石从上往下掉战斗方式发射能量弹击毁陨石得分规则每击毁一个陨石10分失败条件被陨石撞击3次游戏结束难度曲线分数越高陨石速度越快控制方式设计成双模式键盘模式电脑上调试用方向键移动空格射击语音模式Rokid 眼镜实际使用说左、右、发射核心技术实现1. 3D 场景搭建1.1 相机视角采用 45 度俯视角让玩家能同时看到自己的飞船和前方飞来的陨石const camera new BABYLON.ArcRotateCamera(camera,0,Math.PI / 4, // 45度俯视角80, // 距离场景中心80单位new BABYLON.Vector3(0, 0, 0),scene);1.2 网格地板为了增强 3D 空间感添加了蓝色网格地板// 用线条绘制网格const gridSize 150;const gridDivisions 15;const lineColor new BABYLON.Color3(0, 0.5, 1);// 横向线for (let i 0; i gridDivisions; i) {const z (i / gridDivisions) * 200;const points [new BABYLON.Vector3(-gridSize / 2, -20, z - 50),new BABYLON.Vector3(gridSize / 2, -20, z - 50)];const line BABYLON.MeshBuilder.CreateLines(gridLineH i, { points }, scene);line.color lineColor;line.alpha 0.5;}// 纵向线类似代码网格的透视效果让玩家能清晰感知深度和速度。image1.3 星空背景用点云系统创建 300 颗随机分布的星星const starCount 300;const stars new BABYLON.PointsCloudSystem(stars, 3, scene);stars.addPoints(starCount, (particle, i) {particle.position new BABYLON.Vector3(Math.random() * 200 - 100,Math.random() * 200 - 100,Math.random() * 100 30 // Z轴分布增强深度);particle.color new BABYLON.Color4(1, 1, 1, Math.random() * 0.8 0.2);});stars.buildMeshAsync();image2. 飞船系统2.1 飞船模型飞船由三角形机身 机翼 引擎光效组成。为了在 AR 眼镜中看得清楚所有尺寸都放大了 5 倍// 三角形机身const ship BABYLON.MeshBuilder.CreateCylinder(player, {height: 10, // 放大5倍后的尺寸diameterTop: 0,diameterBottom: 7.5,tessellation: 3 // 三条边形成三角形}, scene);ship.rotation.x Math.PI / 2; // 旋转90度让尖端朝上ship.position.y -15;// 添加机翼增强3D效果const wingLeft BABYLON.MeshBuilder.CreateBox(wingLeft, {width: 3,height: 0.5,depth: 4}, scene);wingLeft.position new BABYLON.Vector3(-4, -2, 0);wingLeft.parent ship;// 引擎光效const engineGlow BABYLON.MeshBuilder.CreateSphere(engineGlow, {diameter: 2.5}, scene);engineGlow.position new BABYLON.Vector3(0, -6, 0);engineGlow.parent ship;const glowMaterial new BABYLON.StandardMaterial(glowMat, scene);glowMaterial.emissiveColor new BABYLON.Color3(1, 0.5, 0); // 橙色发光engineGlow.material glowMaterial;2.2 流畅移动控制使用按键状态追踪实现流畅的连续移动this.keys {};window.addEventListener(keydown, (event) {this.keys[event.key] true;if (event.key || event.key Enter) {this.shoot();}});window.addEventListener(keyup, (event) {this.keys[event.key] false;});// 在渲染循环中处理移动scene.registerBeforeRender(() {if (this.keys[ArrowLeft] || this.keys[a] || this.keys[A]) {this.movePlayer(-1);}if (this.keys[ArrowRight] || this.keys[d] || this.keys[D]) {this.movePlayer(1);}});这样按住方向键就能持续移动没有系统按键延迟的顿挫感。image3. 武器系统3.1 子弹设计子弹采用圆柱体造型带有尾迹效果shoot() {const bullet BABYLON.MeshBuilder.CreateCylinder(bullet, {height: 3,diameter: 1}, this.scene);bullet.position this.player.position.clone();bullet.position.y 5;bullet.rotation.x Math.PI / 2;// 黄色发光材质const material new BABYLON.StandardMaterial(bulletMat, this.scene);material.emissiveColor new BABYLON.Color3(1, 1, 0);bullet.material material;// 添加尾迹const trail BABYLON.MeshBuilder.CreateCylinder(trail, {height: 2,diameter: 0.5}, this.scene);trail.position new BABYLON.Vector3(0, -2.5, 0);trail.parent bullet;const trailMat new BABYLON.StandardMaterial(trailMat, this.scene);trailMat.emissiveColor new BABYLON.Color3(1, 0.8, 0);trailMat.alpha 0.6;trail.material trailMat;this.bullets.push(bullet);}image3.2 子弹更新逻辑scene.registerBeforeRender(() {for (let i this.bullets.length - 1; i 0; i--) {this.bullets[i].position.y 8; // 速度也放大了// 飞出屏幕后销毁if (this.bullets[i].position.y 50) {this.bullets[i].dispose();this.bullets.splice(i, 1);}}});4. 敌人系统4.1 陨石生成陨石使用 Babylon.js 的多面体有 3 种不同形状spawnAsteroid() {const asteroid BABYLON.MeshBuilder.CreatePolyhedron(asteroid, {type: Math.floor(Math.random() * 3), // 0-2随机形状size: Math.random() * 4 3 // 放大5倍}, this.scene);asteroid.position new BABYLON.Vector3(Math.random() * 80 - 40, // X: -40到40随机位置50, // Y: 从顶部出现Math.random() * 10 - 5 // Z: -5到5增加深度变化);// 岩石材质const material new BABYLON.StandardMaterial(asteroidMat, this.scene);material.diffuseColor new BABYLON.Color3(0.6, 0.4, 0.3);material.emissiveColor new BABYLON.Color3(0.2, 0.1, 0.05);asteroid.material material;// 随机旋转速度asteroid.rotationSpeed new BABYLON.Vector3(Math.random() * 0.05,Math.random() * 0.05,Math.random() * 0.05);this.asteroids.push(asteroid);}image4.2 定时生成startAsteroidSpawner() {this.asteroidSpawnTimer setInterval(() {this.spawnAsteroid();}, 2000); // 每2秒生成一个}5. 碰撞检测与爆炸效果5.1 碰撞检测使用 Babylon.js 内置的 intersectsMesh 方法for (let i this.asteroids.length - 1; i 0; i--) {const asteroid this.asteroids[i];// 检测子弹碰撞let hit false;for (let j this.bullets.length - 1; j 0; j--) {const bullet this.bullets[j];if (asteroid.intersectsMesh(bullet, false)) {this.createExplosion(asteroid.position);asteroid.dispose();bullet.dispose();this.asteroids.splice(i, 1);this.bullets.splice(j, 1);this.score 10;this.updateUI();hit true;break;}}// 检测飞船碰撞if (!hit asteroid.intersectsMesh(this.player, false)) {this.createExplosion(asteroid.position);asteroid.dispose();this.asteroids.splice(i, 1);this.lives--;this.updateUI();if (this.lives 0) {this.endGame();}}}5.2 粒子爆炸效果createExplosion(position) {const particleSystem new BABYLON.ParticleSystem(explosion, 100, this.scene);particleSystem.particleTexture new BABYLON.Texture(, this.scene);particleSystem.emitter position;particleSystem.minSize 0.5;particleSystem.maxSize 1.5;particleSystem.minLifeTime 0.3;particleSystem.maxLifeTime 0.6;particleSystem.emitRate 200;particleSystem.createSphereEmitter(1);particleSystem.color1 new BABYLON.Color4(1, 0.5, 0, 1);particleSystem.color2 new BABYLON.Color4(1, 0.8, 0, 1);particleSystem.colorDead new BABYLON.Color4(0.5, 0.5, 0.5, 0);particleSystem.minEmitPower 1;particleSystem.maxEmitPower 3;particleSystem.start();setTimeout(() {particleSystem.stop();setTimeout(() particleSystem.dispose(), 1000);}, 200);}6. UI 系统6.1 得分和生命值显示UI 使用 DynamicTexture 在 3D 平面上绘制文字createUI() {// 得分显示this.scoreText BABYLON.MeshBuilder.CreatePlane(scoreText, {width: 60,height: 12}, this.scene);this.scoreText.position new BABYLON.Vector3(-50, 40, 0);const scoreTexture new BABYLON.DynamicTexture(scoreTexture,{ width: 1024, height: 256 }, this.scene, true);const scoreMaterial new BABYLON.StandardMaterial(scoreMat, this.scene);scoreMaterial.diffuseTexture scoreTexture;scoreMaterial.emissiveColor new BABYLON.Color3(1, 1, 1);this.scoreText.material scoreMaterial;this.scoreText.renderingGroupId 1; // 确保UI在最上层this.scoreTexture scoreTexture;// 生命值显示类似代码}updateUI() {// 更新得分const ctx this.scoreTexture.getContext();ctx.clearRect(0, 0, 1024, 256);ctx.fillStyle #FFD700;ctx.font bold 120px Arial;ctx.textAlign left;ctx.fillText(得分: this.score, 50, 160);this.scoreTexture.update();// 更新生命值类似代码}canvas 尺寸是 1024x256而 plane 是 60x12这样宽高比一致文字不会变形。image6.2 游戏结束界面endGame() {this.gameOver true;clearInterval(this.asteroidSpawnTimer);const gameOverText BABYLON.MeshBuilder.CreatePlane(gameOverText, {width: 120,height: 40}, this.scene);gameOverText.position new BABYLON.Vector3(0, 0, 0);const texture new BABYLON.DynamicTexture(gameOverTexture, {width: 1024,height: 512}, this.scene, true);const ctx texture.getContext();ctx.fillStyle rgba(0, 0, 0, 0.9);ctx.fillRect(0, 0, 1024, 512);ctx.fillStyle #FF3333;ctx.font bold 100px Arial;ctx.textAlign center;ctx.fillText(游戏结束, 512, 150);ctx.fillStyle #FFD700;ctx.font bold 80px Arial;ctx.fillText(最终得分: this.score, 512, 280);ctx.fillStyle #FFFFFF;ctx.font 50px Arial;ctx.fillText(按 R 重新开始, 512, 400);texture.update();const material new BABYLON.StandardMaterial(gameOverMat, this.scene);material.diffuseTexture texture;material.emissiveColor new BABYLON.Color3(0.8, 0.8, 0.8);gameOverText.material material;gameOverText.renderingGroupId 2; // 最上层}7. 语音控制7.1 Rokid 语音 APIJSAR 提供了 rokid.voice API 用于语音识别setupVoiceControl() {if (typeof rokid ! undefined rokid.voice) {rokid.voice.on(command, (command) {switch(command) {case 左:this.movePlayer(-1);break;case 右:this.movePlayer(1);break;case 发射:case 开火:this.shoot();break;}});}}7.2 键盘模拟语音调试用在电脑上调试时用 J/K/L 键模拟语音命令window.addEventListener(keydown, (event) {// 语音模拟if (event.key j || event.key J) {console.log(?? 语音命令: 左);this.movePlayer(-1);} else if (event.key l || event.key L) {console.log(?? 语音命令: 右);this.movePlayer(1);} else if (event.key k || event.key K) {console.log(?? 语音命令: 发射);this.shoot();}});image8. 游戏难度递增根据得分提升游戏难度// 在碰撞检测中击中陨石后this.score 10;this.updateUI();// 难度递增if (this.score % 50 0 this.difficulty 2) {this.difficulty 0.1;console.log(难度提升至: this.difficulty.toFixed(1));}// 在陨石更新时应用难度asteroid.position.y - GAME_CONFIG.asteroidSpeed * this.difficulty;每得 50 分陨石速度提升 10%最高提升到 2 倍速。性能优化对象管理游戏中会频繁创建和销毁子弹、陨石必须及时清理// 子弹飞出屏幕if (bullet.position.y 50) {bullet.dispose(); // 销毁3D对象this.bullets.splice(i, 1); // 从数组删除}// 陨石飞出屏幕if (asteroid.position.y -40) {asteroid.dispose();this.asteroids.splice(i, 1);}渲染分组使用 renderingGroupId 控制渲染顺序确保 UI 始终在最上层this.scoreText.renderingGroupId 1;this.livesText.renderingGroupId 1;gameOverText.renderingGroupId 2;控制说明键盘模式电脑调试按键功能← / A向左移动→ / D向右移动空格 / 回车发射R重新开始语音模拟键盘按键模拟语音J左L右K发射语音控制Rokid 眼镜直接说左 - 飞船左移右 - 飞船右移发射 / 开火 - 射击开发心得踩过的坑忘记 dispose 对象 → 内存泄漏游戏越来越卡碰撞检测死循环 → 删除元素时索引出错要倒序遍历粒子系统没清理 → 爆炸效果叠加导致卡顿相机角度调试 → 最初是正面视角太平面改成 45 度俯视后立体感大增物体尺寸太小 → 第一版看不清细节放大 5-10 倍后体验好很多经验总结3D 游戏要注重空间感相机角度、参考物网格、Z 轴变化尺寸和速度要成比例调整放大物体后速度也要相应提升按键状态追踪比 keydown 事件更流畅没有系统延迟UI 宽高比canvas 和 plane 的宽高比要一致避免文字变形及时清理对象dispose 数组 splice 两步都要做多用 console.log 调试特别是碰撞检测部分

相关新闻

ESP32P4使用RMII驱动CH182D

ESP32P4使用RMII驱动CH182D

我们使用的是sergeykharenko/ch182 • v0.0.1 • ESP Component Registry 这个组件库,首先感谢作者的开源提供该组件库,感谢乐鑫及沁恒官方提供的技术支持。 下面是原理图部分, 该芯片数据手册:CH182D -PDF数据手册-参考资料-立创…

2026/7/3 8:49:45 阅读更多 →
Linux服务器环境配置与Python开发环境搭建完全指南

Linux服务器环境配置与Python开发环境搭建完全指南

本文基于CentOS 7/8和Ubuntu 22.04 LTS系统,涵盖系统管理、环境配置、Python安装、深度学习框架部署等实战内容。 一、系统基础信息查看 1.1 查看操作系统版本 在开始任何配置之前,首先需要确认操作系统类型和版本: # CentOS/RHEL 系统 ca…

2026/7/7 5:05:10 阅读更多 →
T-SQL查询进阶--理解SQL Server中索引的概念,原理以及其他

T-SQL查询进阶--理解SQL Server中索引的概念,原理以及其他

两拇炒诙一般而言,代码可分为同步与异步两类。两者同样需要等待操作完成:同步会阻塞当前线程,直至操作结束后再继续执行后续逻辑;异步则不阻塞当前线程,而是在发起操作时预先注册完成后的处理逻辑,待操作完…

2026/7/5 23:00:06 阅读更多 →

最新新闻

Playwright与MCP协议结合:用自然语言驱动浏览器自动化测试

Playwright与MCP协议结合:用自然语言驱动浏览器自动化测试

1. 项目概述:当Playwright遇上MCP,测试自动化迎来“对话式”革命 如果你是一名前端开发者、测试工程师,或者任何需要和浏览器打交道的技术人,最近一定被两个词刷屏了: Playwright 和 MCP 。前者是微软开源的现代浏…

2026/7/7 20:25:09 阅读更多 →
Switch游戏机畅享B站:wiliwili第三方客户端终极使用指南

Switch游戏机畅享B站:wiliwili第三方客户端终极使用指南

Switch游戏机畅享B站:wiliwili第三方客户端终极使用指南 【免费下载链接】wiliwili 第三方B站客户端,目前可以运行在PC全平台、PSVita、PS4 、Xbox 和 Nintendo Switch上 项目地址: https://gitcode.com/GitHub_Trending/wi/wiliwili 你是否曾经想…

2026/7/7 20:23:08 阅读更多 →
从真实案例剖析SQL注入与XSS攻击:防御实战与靶场演练

从真实案例剖析SQL注入与XSS攻击:防御实战与靶场演练

1. 项目概述:从真实案例看Web安全的两大“顽疾”干了这么多年安全开发和渗透测试,我见过太多因为SQL注入和XSS(跨站脚本攻击)导致的数据泄露、网站被黑甚至公司倒闭的案例。很多开发者,尤其是刚入行的朋友,…

2026/7/7 20:21:08 阅读更多 →
构建高效自动化渗透测试体系:从架构设计到工程实践

构建高效自动化渗透测试体系:从架构设计到工程实践

1. 项目概述与核心理念在安全行业摸爬滚打八年,尤其是在一线攻防对抗和内部安全建设里泡了这么久,我越来越深刻地感受到一个趋势:单靠“人肉”堆砌的渗透测试,无论是效率、覆盖面还是持续性,都已经跟不上现代企业资产膨…

2026/7/7 20:19:07 阅读更多 →
如何快速掌握Java反编译神器:Luyten完整操作指南

如何快速掌握Java反编译神器:Luyten完整操作指南

如何快速掌握Java反编译神器:Luyten完整操作指南 【免费下载链接】Luyten An Open Source Java Decompiler Gui for Procyon 项目地址: https://gitcode.com/gh_mirrors/lu/Luyten 你是否曾经面对编译后的Java字节码文件感到无从下手?想要理解第三…

2026/7/7 20:19:07 阅读更多 →
XSS攻防实战:从靶场到IDE辅助的漏洞分析与防御

XSS攻防实战:从靶场到IDE辅助的漏洞分析与防御

1. 项目概述:从靶场到实战,一次完整的XSS攻防思维重塑最近在整理自己的Web安全学习笔记,翻到了几年前啃下来的xss-lab靶场记录。这个由PHP编写、包含20个经典关卡的XSS练习平台,可以说是无数安全从业者的“启蒙老师”。有意思的是…

2026/7/7 20:17:04 阅读更多 →

日新闻

鸿蒙新特性:图片画廊与轮播导航——构建沉浸式图片浏览体验

鸿蒙新特性:图片画廊与轮播导航——构建沉浸式图片浏览体验

图片浏览是移动应用中最高频的场景之一。从社交应用的照片流到电商平台的商品图集,从旅游应用的景点相册到摄影作品展示——用户对图片浏览的体验要求不断提高:流畅的切换动画、直观的缩略图导航、便捷的收藏操作、自动播放模式。HarmonyOS NEXT ArkUI 虽…

2026/7/7 0:05:16 阅读更多 →
24V DC-DC降压芯片PW2312B/PW2815,SOT23-6到SOP8-EP方案对比

24V DC-DC降压芯片PW2312B/PW2815,SOT23-6到SOP8-EP方案对比

24V稳压芯片完整选型指南 PW8600 PW75XX PW2815 PW2312B LDODC/DC全方案 一、24V稳压方案概述 24V直流电源在工业自动化、门禁系统、电梯控制、汽车电子、LED驱动、监控设备等场景中应用极广,是最常见的中压直流母线电压。要将24V母线稳定降压至下游MCU、传感器…

2026/7/7 0:05:16 阅读更多 →
RAG+知识图谱混合检索与Graph RAG核心对比

RAG+知识图谱混合检索与Graph RAG核心对比

做企业RAG落地的团队,往往容易卡在一容易踩坑的选型难题: 当需求单纯靠向量RAG搞不定、单纯靠知识图谱也搞不定,必须同时依赖「文本语义理解 实体关系推理」时,到底是做「向量图谱混合检索」就够了,还是必须上「Grap…

2026/7/7 0:07:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/7 14:24:45 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/7 12:34:47 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/7 15:59:06 阅读更多 →

月新闻