拓冰建站拓冰建站
首页 / 资讯中心 / 正文

AI辅助重构:用React+Three.js+Rapier重写3D娃娃机项目

1. 项目缘起一个被遗忘的“半成品”与AI的邂逅五年前我沉迷于用代码创造一些“无用但有趣”的东西。当时Three.js正火物理引擎也让人着迷于是心血来潮想做一个在线的、可交互的娃娃机。想法很美好用React搭界面Three.js渲染3D场景再找个物理引擎模拟抓取的真实感。我吭哧吭哧搞了几天实现了基础的场景搭建、模型加载和一个极其简陋的抓取动画。然后……就没有然后了。项目文件夹静静地躺在硬盘角落成了典型的“从入门到放弃”的标本。直到最近AI编程工具的风潮让我重新审视这些陈年旧码。看着那个粗糙的、爪子穿模、逻辑混乱的娃娃机项目一个念头冒了出来如果让现在的AI来“重写”它会怎么样不是让它从零生成而是基于我五年前的思路和残破的代码用今天更成熟的技术栈和AI的理解能力重新构建一个完整、可玩、代码优雅的项目。这不仅仅是一次代码翻新更像是一场跨越时间的对话看看五年间我的技术认知与AI的代码能力能碰撞出什么火花。于是我花了半天时间以“AI辅助重写”为核心将这个老项目彻底复活。2. 技术栈选型与AI工具的角色定位五年前我的技术选择带着明显的探索和拼凑痕迹。今天重做首先要明确一个清晰、现代且高效的技术栈。AI在这里不仅是写代码的“手”更是提供选型建议、规避历史陷阱的“顾问”。2.1 前端框架坚守React但拥抱全新开发范式项目主体是浏览器内的3D交互应用React依然是构建复杂UI状态的首选其组件化模型与3D场景中的对象管理有天然的契合度。但五年前我用的是React Class Component和一堆散乱的setState状态流像一团乱麻。AI的辅助建议与实施我向AI工具如Cursor或Claude Code描述了项目需求一个需要精细控制3D物体状态、抓取流程、游戏分数和UI联动的应用。AI几乎一致推荐使用React Hooks TypeScript的组合。Hooks尤其是useState,useEffect,useRef,useReducer能让状态和副作用逻辑更清晰、更内聚。例如娃娃机的抓取臂状态、计时器、奖品池管理都可以用自定义Hook封装与3D渲染逻辑解耦。TypeScript的引入是本次重写的关键提升。我为整个项目定义了清晰的接口InterfaceIClawMachineState、IPrizeItem、IPhysicsWorld等。AI在根据我的自然语言描述生成代码片段时TypeScript能立刻提供类型约束和智能提示极大减少了因类型错误导致的运行时Bug。当我说“创建一个控制抓钩上下移动的函数”时AI生成的代码已经包含了参数类型speed: number和返回类型Promisevoid使得代码意图一目了然。2.2 3D渲染核心Three.js的版本升级与最佳实践五年前我用的是Three.js r89很多API如今已变。AI帮我快速梳理了版本差异并直接采用了最新的稳定版本如r162。重写不是简单替换版本号而是在AI的帮助下应用当前社区公认的最佳实践。场景图Scene Graph管理过去我把所有模型机器、爪子、奖品都直接add到scene里管理混乱。AI建议并帮我重构为更清晰的层级结构// 使用Group进行逻辑分组 const machineGroup new THREE.Group(); const clawGroup new THREE.Group(); const prizesGroup new THREE.Group(); machineGroup.add(cabinetModel, glassModel); clawGroup.add(armModel, clawModel); prizesGroup.add(prize1, prize2, prize3); scene.add(machineGroup, clawGroup, prizesGroup);这样移动爪子只需操作clawGroup旋转整个机器只需操作machineGroup逻辑清晰便于AI理解和后续维护。资源管理与加载当年用THREE.TextureLoader和THREE.GLTFLoader都是直接内联没有加载状态和错误处理。AI帮我引入了LoadingManager和异步加载模式并生成了加载进度条UI的配套代码提升了用户体验。性能优化AI提示我对于静态的娃娃机柜体模型应使用THREE.StaticCopy属性或在加载时合并几何体以减少draw call。对于大量重复的奖品模型建议使用实例化网格THREE.InstancedMesh这在五年前我完全没概念。2.3 物理引擎从“纸片”动画到真实模拟旧项目最大的败笔是物理。抓取动画是用Tween.js做的简单线性运动爪子与奖品是“检测矩形框重叠”这种2D游戏逻辑毫无真实感。AI的决策支持过程我向AI提出了需求“需要为3D娃娃机添加真实的抓取物理包括碰撞检测、抓取判定、奖品被抓起后的摆动。” AI没有直接推荐某个引擎而是列出了几个选项并分析了在Web环境下的优劣Cannon.js纯JavaScript轻量API直接但功能相对基础社区活跃度一般。Ammo.jsBullet物理引擎的Emscripten移植功能强大工业级但包体积大初始化慢。Rapier.rs编译为WebAssembly新兴引擎性能极好API现代但中文资料相对少。考虑到本项目并非需要极度复杂的物理特性如流体、软体而是注重实时交互性能和开发体验AI最终建议我尝试Rapier。它WASM的特性保证了性能且其TypeScript类型定义完善与我的技术栈契合。AI不仅给出了建议还生成了初始化的样板代码import * as RAPIER from dimforge/rapier3d; // 初始化物理世界 const world new RAPIER.World({ x: 0.0, y: -9.81, z: 0.0 }); // 创建地面和碰撞体 const groundColliderDesc RAPIER.ColliderDesc.cuboid(10, 0.1, 10); world.createCollider(groundColliderDesc); // 在Three.js对象和Rapier刚体之间建立关联 const rigidBodyDesc RAPIER.RigidBodyDesc.dynamic().setTranslation(x, y, z); const rigidBody world.createRigidBody(rigidBodyDesc); const colliderDesc RAPIER.ColliderDesc.ball(radius); world.createCollider(colliderDesc, rigidBody); // 在渲染循环中更新物理世界 function animate() { world.step(); // 同步Three.js对象位置到物理刚体位置 const position rigidBody.translation(); threeJsObj.position.set(position.x, position.y, position.z); requestAnimationFrame(animate); }这段由AI生成的代码清晰地勾勒出了整合物理引擎的核心链路我只需在此基础上将我的3D模型与对应的刚体、碰撞体绑定即可。2.4 AI编程工具Cursor作为核心“协作者”整个重写过程我主要使用Cursor作为AI编程助手。它的优势在于深度集成在IDE中能直接分析整个项目上下文包括我五年前的烂代码提供基于上下文的建议。我的工作流是需求拆解与描述我用自然语言在Cursor里描述一个功能点比如“我想让爪子下落时如果碰到奖品就停止并闭合然后上升”。代码生成与审查Cursor会生成一段包含物理检测、状态变更的TypeScript函数。我并非直接采纳而是仔细阅读理解其逻辑例如它使用了Rapier的world.intersectionsWith进行碰撞检测。迭代与调试如果生成的代码运行不如预期比如抓取判定太灵敏我会继续对话“抓取判定需要更精确只有当爪子的两个钳口都接触到奖品时才触发抓取。” Cursor会修改代码可能引入更详细的射线检测或形状查询。代码重构对于AI生成的一些冗长函数我会命令它“将这个函数重构将物理检测和状态更新逻辑分离。” 它便能给出更模块化的代码。AI在此过程中扮演的不是替代者而是一个知识渊博、不知疲倦的结对编程伙伴。它快速将我的意图转化为语法正确的代码草案而我负责把握整体架构、业务逻辑的正确性和代码最终质量。3. 核心功能模块的重构与AI实现有了清晰的技术栈和工具重写工作就可以系统性地展开。我将娃娃机拆解为几个核心模块逐一用AI辅助重构。3.1 游戏状态管理从混乱到清晰旧项目状态散落在各个角落难以维护。我决定使用useReducerHook来集中管理游戏状态。我向AI描述了状态机游戏状态IDLE待机 - COIN_INSERTED投币 - CLAW_MOVING移动爪子 - CLAW_DESCENDING下爪 - CLAW_GRASPING抓取中 - CLAW_ASCENDING上升 - RESULT判定结果 - IDLEAI为我生成了状态和动作Action的类型定义以及reducer函数的基本骨架。我在此基础上填充了每个状态转换的具体逻辑。例如当派发‘GRASP_COMPLETE’动作时reducer会计算是否抓取成功并更新奖品列表和玩家分数。这种集中式状态管理让UI如按钮禁用状态、提示文字和3D场景动画的同步变得异常简单。3.2 抓取物理与交互真实感的核心这是本次重写的重中之重也是AI辅助价值最高的部分。物理实体绑定首先需要为Three.js场景中的每个可交互物体爪子、奖品创建对应的Rapier刚体RigidBody和碰撞体Collider。我让AI编写了一个工具函数createPhysicsEntity它接收一个Three.js的Mesh对象和物理参数质量、形状、类型返回一个包含rigidBody和collider的对象并自动将两者的位置同步。这避免了手动维护两套坐标的麻烦。抓取逻辑实现下爪过程不再是简单的动画。AI帮我实现的逻辑是爪子一个Dynamic刚体在CLAW_DESCENDING状态下被施加一个向下的力rigidBody.applyForce。在每一帧物理更新后检测爪子碰撞体clawCollider与所有奖品碰撞体的交集world.contactPair(clawCollider, prizeCollider)。如果检测到接触且接触点数量或深度满足“抓取”阈值则切换状态到CLAW_GRASPING。此时AI生成的代码会创建一个物理约束将奖品刚体与爪子刚体通过一个FixedImpulseJoint或BallJoint连接起来。这就模拟了爪子“抓住”奖品的物理效果奖品会随着爪子上升而自然摆动而不是僵直地粘在一起。上升到顶后解除物理约束并根据奖品最终位置是否落入出货口判定游戏结果。这个过程中AI快速帮我处理了复杂的Rapier API调用并解释了不同关节Joint类型的区别让我能做出合适的选择。用户输入与控制我保留了旧项目的“方向键移动爪子”设计。AI将键盘事件监听与物理引擎结合生成的控制代码不再是直接设置position.x而是对爪子的刚体施加一个冲量applyImpulse或设置速度setLinvel使得移动带有惯性手感更真实。3.3 3D模型与动画优化五年前的模型是网上随便找的.obj文件材质粗糙。这次我使用AI图像生成工具如Midjourney或Stable Diffusion的辅助设计功能重新生成了更卡通、更精致的娃娃机和奖品概念图然后借助Blender快速建模导出为.glb格式。在加载环节我遇到了一个经典问题“glb模型为什么到three.js里打开全是黑的”旧项目也遇到过当时没解决。这次我直接问AI。AI给出了系统性的排查清单检查光源场景中是否添加了AmbientLight环境光和DirectionalLight平行光没有光源模型就是黑的。检查相机位置相机是否在模型内部或被模型遮挡使用camera.lookAt()指向场景中心。检查模型材质某些.glb模型的材质可能需要THREE.sRGBEncoding或特定的渲染器配置。AI建议我尝试在加载后遍历模型材质并设置material.needsUpdate true。检查模型缩放模型可能过大或过小超出视锥体。添加一个BoxHelper来可视化模型边界。通过AI的指引我迅速定位到问题是光源不足和材质编码问题并得以解决。AI还建议我为抓取臂的张开/闭合添加骨骼动画THREE.AnimationMixer让动作更流畅并生成了混合动画剪辑的基础代码。3.4 UI与游戏逻辑集成使用ReactUI层变得非常简单。我创建了GameControlPanel组件显示状态、币数、分数以及控制按钮。状态通过Context或Props从主reducer传递下来。AI帮我快速搭建了这些UI组件并确保了样式与3D画风的统一。对于“开始游戏”、“移动”等按钮事件它们只是派发对应的Action如‘MOVE_LEFT’由reducer和物理系统去响应实现了完美的关注点分离。4. 开发、构建与部署的现代化改造五年前我用的是create-react-app的早期版本构建配置一无所知。现在我用ViteTypeScript作为构建工具链速度极快。Vite配置AI帮我初始化了项目并解决了TypeScript装饰器解析的问题。当我在tsconfig.json中遇到“选项‘baseUrl’已弃用”的警告时AI解释了这是TypeScript 7.0的变更并给出了迁移到‘rootDir’和路径映射paths的正确配置方法。调试与性能分析AI建议我使用Three.js的调试库drei和物理引擎的调试渲染器将碰撞体以线框形式可视化这对于调试抓取判定至关重要。在性能方面AI提示我使用stats.js监控帧率并注意在组件卸载时正确销毁Three.js的渲染器、场景和物理世界防止内存泄漏。部署项目是纯前端的部署极其简单。AI建议我可以使用Vercel、Netlify或GitHub Pages。我选择了Vercel只需关联Git仓库它就能自动识别Vite项目并进行部署。AI还生成了必要的vercel.json配置虽然本项目基本不需要并提醒我注意构建输出目录是dist。5. 半日重构经验、教训与AI的边界从打开旧项目到新版本上线总共耗时约半天。这个过程中AI的贡献是颠覆性的但也暴露出其局限性。AI带来的效率提升是惊人的知识检索与代码生成我不再需要反复搜索“Three.js如何创建阴影”、“Rapier怎么创建碰撞传感器”用自然语言描述AI就能给出可直接使用或稍作修改的代码块。代码重构与规范化AI能迅速将过程式代码重构为声明式、模块化的函数组件和自定义Hook并强制加入TypeScript类型代码质量远超我五年前的水平。问题排查与调试遇到“模型全黑”、“物理对象穿透”等问题时向AI描述现象它能给出结构化的排查思路节省了大量盲目试错的时间。但AI并非万能我的核心作用无可替代架构设计与决策AI不会主动说“我们应该用useReducer来管理状态”。它需要我提出清晰、正确的指令。整体应用架构、模块划分、数据流设计仍然依赖我的经验和判断。业务逻辑理解娃娃机抓取的“玄学”手感如爪子的抓力、奖品的滑动需要微调参数。AI可以帮我写参数调节的代码框架但“怎样算好玩”这个感觉需要我亲自测试和调整。AI无法理解“趣味性”这种主观体验。错误处理与边界情况AI生成的代码往往是“理想路径”。当网络加载失败、物理引擎初始化出错、用户快速连续点击时需要我手动添加健壮的错误处理和状态保护。AI可能会遗漏这些边缘情况。代码审查与优化对AI生成的代码我必须逐行审查。有时它会使用已弃用的API或写出性能不佳的写法如在渲染循环中创建新对象。我需要识别并纠正这些。一个具体的踩坑案例在实现爪子抓取奖品时AI最初生成的代码是在检测到碰撞的瞬间就将奖品刚体的类型从Dynamic改为KinematicPositionBased并直接设置其位置跟随爪子。这导致了诡异的“抽搐”现象。我意识到这是物理模拟和直接设置位置的冲突。经过几轮与AI的对话调试我们最终采用了前面提到的物理关节Joint方案这才是符合物理规律的实现。这个过程让我深刻体会到AI提供的是“素材”和“可能性”而工程师的價值在于“选择”和“判断”。这次“AI重写”实验结果远超预期。一个死气沉沉的老项目在半天内脱胎换骨拥有了现代化的代码结构、真实的物理交互和不错的视觉效果。它证明了在当前阶段AI编程工具已经成为前端开发者尤其是从事3D、游戏等复杂交互领域开发的强大“倍增器”。它极大地压缩了“查找资料-理解API-编写样板代码”的循环让开发者能更专注于核心创意、架构设计和用户体验打磨。然而它没有改变编程的本质——解决问题的逻辑思维。AI更像是一本超级智能的、能对话的API文档和代码片段生成器。驾驭它需要你清楚地知道自己要去哪里以及如何下达正确的指令。对于新手它可能让你更快地做出东西但也可能让你在不理解原理的情况下堆砌代码对于有经验的开发者它则能解放生产力让你从繁琐的重复劳动中解脱出来去挑战更有价值的问题。我的这个“新”娃娃机就是这种新工作流下一个有趣的产物。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门