简介这是一份基于Cocos Creator引擎与TypeScript开发的抓娃娃小游戏完整项目源码面向游戏开发初学者及Cocos Creator进阶学习者旨在帮助读者掌握2D物理交互、动画控制、UI事件响应与跨平台打包等核心实践能力。资源包共992个文件涵盖225个PNG素材图、43个Prefab预制体含爪子、娃娃、底座等可复用组件、321个meta配置文件、289个JSON资源描述、49个anim动画剪辑如left_bg.anim、pao4.anim等以及6个TS主逻辑脚本和5个MP3音效整体7.72MB结构规范、模块清晰便于逐层理解与二次开发。已有1121人学习下载。读者可直接运行调试深入剖析爪子运动轨迹模拟、碰撞判定逻辑、概率化抓取机制及本地积分系统实现同时项目预留后端接口扩展点为后续接入排行榜或网络对战提供基础支撑。1. Cocos Creator 抓娃娃小示例一个被低估的物理交互练手项目新手用它搞懂刚体、关节、碰撞响应比写十个“Hello World”都管用你可能以为抓娃娃机只是游戏里一个带点趣味的UI动效——拉杆、摇臂、夹爪开合、毛绒玩具晃动、掉进出口……但真把它拆开看它是个微型物理系统铰链关节模拟机械臂旋转轴弹簧力控制夹爪闭合力度刚体质量与摩擦系数决定玩具是否滑脱碰撞检测精度影响“夹中却掉落”的玄学体验。这个用 Cocos Creator TypeScript 实现的小示例没用任何第三方插件全靠引擎内置的 Box2D 物理模块和节点层级动画驱动代码量不到 800 行却完整覆盖了从场景搭建、物理参数调优、输入事件绑定到状态机管理的闭环。它不是玩具是给刚学完 TS 基础、正卡在“知道 API 但不会组织逻辑”阶段的开发者的后悔药——我带过的 A 同学就是靠它三天内把物理组件文档翻烂、把RigidBody2D的linearDamping和angularDamping调出肌肉记忆后来独立接了个儿童教育类 App 的互动实验模块。如果你正在找一个不堆 UI、不绕弯子、能立刻跑起来并亲手拧动每个参数的 Cocos Creator 入门实战项目它就是那个“最小可行物理系统”。2. 场景结构与物理组件配置从空画布到可交互抓娃娃机的四层节点树抓娃娃机不是“画个机器放个玩具”就完事。Cocos Creator 里它的稳定性和交互感90% 取决于节点层级是否符合物理引擎的预期。这个示例采用四层嵌套结构每层承担明确职责避免常见“所有节点挂 RigidBody2D 导致抖动”或“碰撞体错位导致夹不住”的翻车。2.1 根节点MachineRoot静态刚体 碰撞体这是整个机器的锚点必须设为静态刚体Static否则整台机器会因夹爪动作反作用力而漂移// 在 MachineRoot 节点上添加 RigidBody2D 组件 // 属性面板设置 // - Type: Static // - Collision Filter: Category 0x0001, Mask 0x0002 // 仅与夹爪Category 0x0002碰撞 // - Body Type: Static提示Collision Filter的 Category/Mask 配置是关键。这里让机器本体只响应夹爪的碰撞忽略玩具、底座等其他物体大幅减少物理计算干扰。若漏设 Mask夹爪会同时撞到机器外壳和内部支撑杆产生不可预测的弹跳。2.2 机械臂节点ArmPivot刚体 铰链关节ArmPivot是旋转中枢需挂载RigidBody2DDynamic 类型和HingeJoint2D铰链关节。关节的anchor必须精确对齐到物理旋转中心点通常为节点局部坐标(0, 0)否则旋转时夹爪轨迹会偏移// ArmPivot 节点组件配置 // RigidBody2D: // - Type: Dynamic // - Linear Damping: 0.5 // 抑制水平漂移 // - Angular Damping: 1.2 // 让旋转快速停稳避免晃悠 // HingeJoint2D: // - Connected Body: null (连接到世界坐标系) // - Anchor: (0, 0) // 关键必须是节点自身原点 // - Lower Angle Limit: -45 // 限制下摆角度防止撞底座 // - Upper Angle Limit: 30 // 限制上抬角度防止打到顶盖参数说明Angular Damping设为 1.2 是血泪经验——低于 0.8 时机械臂抬起后会像钟摆一样来回晃 3~4 秒高于 1.5 则响应迟钝玩家拉杆后延迟感明显。这个值是在 0.5~2.0 区间实测 17 次后定的。2.3 夹爪节点ClawGroup刚体 多碰撞体组合夹爪不是单个刚体而是由ClawLeft、ClawRight、ClawBase三个子节点组成。ClawBase是主刚体DynamicClawLeft/Right通过DistanceJoint2D连接到它模拟弹簧驱动的开合// ClawBase 节点 // RigidBody2D: Type Dynamic, Mass 0.8 // Collider: BoxCollider2D, Size (0.3, 0.1), Offset (0, -0.05) // ClawLeft 节点子节点 // RigidBody2D: Type Dynamic, Mass 0.3 // DistanceJoint2D: // - Connected Body: ClawBase 的 RigidBody2D // - Anchor: (0, 0) // 左爪自身原点 // - Connected Anchor: (-0.15, 0) // 连接到基座左侧点 // - Max Distance: 0.12 // 闭合极限距离 // - Min Distance: 0.05 // 张开极限距离为什么不用动画控制开合动画只能改变视觉位置无法产生真实碰撞力。用DistanceJoint2D当夹爪闭合时若碰到玩具关节会自动计算反作用力推动玩具移动或弹起——这才是“夹中感”的物理基础。2.4 玩具节点ToyItem动态刚体 复杂碰撞体每个玩具都是独立刚体但碰撞体不能简单用 BoxCollider2D。示例中为毛绒熊用了PolygonCollider2D手动描点勾勒出熊的轮廓约 12 个顶点避免 Box 形状导致“夹住脚却滑脱身体”的黑匣子行为// ToyItem 节点 // RigidBody2D: // - Type: Dynamic // - Mass: 0.6 // 比夹爪轻确保夹爪能提起 // - Linear Damping: 0.3 // - Angular Damping: 0.8 // PolygonCollider2D: // - Points: [(-0.12, -0.15), (-0.08, -0.2), ..., (0.12, -0.15)] // 实际描点数据 // - Density: 0.5 // 影响惯性太大会让夹爪提不动注意Density不是越大越好。设为 1.0 时夹爪闭合后常出现“夹住但纹丝不动”因为玩具惯性过大0.5 是平衡点——夹住后能提起掉落时又有自然下坠感。3. TypeScript 核心逻辑状态机驱动的夹取流程与物理参数实时干预这个示例的 TS 代码不追求炫技而是用最直白的状态机State Machine把“拉杆→抬臂→下探→闭爪→提拉→松爪”六步拆解清楚。每一步都直接操作物理组件属性而非依赖动画时间轴——因为物理引擎的帧率与渲染帧率不完全同步硬绑动画时间必然翻车。3.1 状态定义与切换逻辑ClawController.ts中定义了 6 个状态用enum明确边界杜绝字符串魔法值enum ClawState { IDLE idle, // 待机夹爪悬停在顶部 RAISING raising, // 抬升机械臂上摆至最高点 LOWERING lowering, // 下探机械臂下摆至玩具上方 CLOSING closing, // 闭合夹爪收紧 LIFTING lifting, // 提拉夹住后上提 DROPPING dropping // 松开释放玩具成功/失败 }状态切换由update()中的switch控制关键不是“怎么切”而是“切的时候干啥”。例如从LOWERING切到CLOSING必须检查是否已触碰玩具// 在 update() 中 switch (this.currentState) { case ClawState.LOWERING: // 检查是否到达预设下探深度y 坐标阈值 if (this.armPivotNode.position.y this.lowerLimitY) { this.currentState ClawState.CLOSING; this.startClosing(); // 触发闭合逻辑 } break; }逻辑说明不依赖onCollisionEnter切换状态因为碰撞检测有延迟Box2D 默认 1/60s且多次碰撞可能触发多次。改用“位置阈值 主动检测”更可靠——lowerLimitY是预设的 y 坐标如 -120只要机械臂降到该位置就认为已到位立即闭合。这消除了“明明看到夹爪压到玩具了却不闭合”的挫败感。3.2 闭合力与夹持判定用applyForce模拟真实电机扭矩startClosing()方法不直接设关节距离而是对左右夹爪施加朝向中心的力让DistanceJoint2D自动收敛——这样能体现“夹紧过程中的阻力反馈”private startClosing() { const forceMagnitude 80; // 单位牛顿实测 60~100 最自然 const leftClaw this.clawLeftNode.getComponent(RigidBody2D); const rightClaw this.clawRightNode.getComponent(RigidBody2D); // 向中心施加力左爪受力向右x右爪受力向左-x leftClaw?.applyForce(new Vec2(forceMagnitude, 0), true); rightClaw?.applyForce(new Vec2(-forceMagnitude, 0), true); // 启动闭合计时器300ms 后判定是否夹住 this.scheduleOnce(() this.checkGrabSuccess(), 0.3); }参数说明forceMagnitude 80是核心参数。低于 50 时夹爪无力玩具易滑脱高于 120 时夹爪闭合过快像“啪”一声拍下去失去细腻感。300ms 是夹爪从张开到完全闭合的典型耗时足够 Box2D 完成多次迭代计算。3.3 夹持成功判定双重验证防误判checkGrabSuccess()不只看“是否发生碰撞”而是双保险private checkGrabSuccess() { const leftClaw this.clawLeftNode.getComponent(RigidBody2D); const rightClaw this.clawRightNode.getComponent(RigidBody2D); const toyRb this.currentToy?.getComponent(RigidBody2D); // 条件1左右夹爪与玩具均有碰撞onCollisionEnter 已记录 const isLeftColliding this.collisionRecords.has(${leftClaw.node.uuid}_${toyRb.node.uuid}); const isRightColliding this.collisionRecords.has(${rightClaw.node.uuid}_${toyRb.node.uuid}); // 条件2夹爪闭合距离 阈值物理关节实际距离 const joint this.clawLeftNode.getComponent(DistanceJoint2D); const currentDistance joint?.currentDistance || 0; const isClosedEnough currentDistance 0.08; // 8cm小于夹爪宽度一半 if (isLeftColliding isRightColliding isClosedEnough) { this.currentState ClawState.LIFTING; this.startLifting(); } else { this.currentState ClawState.DROPPING; this.dropToy(); // 未夹住松开 } }为什么需要双重验证单靠碰撞检测夹爪边缘擦过玩具表面即触发但未真正夹住单靠距离关节可能因初始误差显示“已闭合”但实际未接触玩具。两者同时满足才是可靠夹持。4. 避坑指南五个让新手调试到凌晨三点的真实问题与解法这个示例看似简单但我在某高校实验室带学生复现时90% 的人卡在以下五个坑里。每个坑都附带现象、根因和一招解决的命令/配置照着做就能省下至少 5 小时无效调试。4.1 现象机械臂旋转时剧烈抖动像得了帕金森原因ArmPivot节点的RigidBody2DType被误设为Kinematic且HingeJoint2D的Connected Body错误指向了MachineRoot刚体而非null。Kinematic 刚体不参与物理计算但关节强制约束其运动导致 Box2D 求解器崩溃式迭代。解决在编辑器中选中ArmPivot→RigidBody2D组件 → 将Type改为Dynamic再选HingeJoint2D→Connected Body清空设为null确保关节锚定世界坐标系。4.2 现象夹爪能闭合但永远夹不住玩具玩具像涂了油一样滑脱原因ToyItem的RigidBody2DMass过大1.0或Friction过小0.3。夹爪施加的力不足以克服玩具惯性且低摩擦导致接触面打滑。解决选中任意玩具节点 →RigidBody2D→Mass设为0.6BoxCollider2D或PolygonCollider2D→Friction设为0.5。实测Friction0.5时夹爪闭合后玩具有明显“被咬住”的滞涩感。4.3 现象点击拉杆没反应控制台无报错但ClawController的onStart()从未执行原因ClawController.ts脚本未正确挂载到MachineRoot节点或脚本executeInEditMode属性为true导致编辑器模式下运行但运行时失效。解决在层级管理器中右键MachineRoot→Add Component→Custom Script→ 选择ClawController然后在属性检查器中找到该脚本 → 取消勾选Execute In Edit Mode。4.4 现象夹住玩具后提拉时玩具和夹爪一起高速飞出屏幕像发射炮弹原因ClawLeft/Right的RigidBody2DMass与ToyItemMass比例失衡。若夹爪质量0.3远小于玩具0.6提拉时夹爪会被玩具反向拖拽Box2D 为维持约束强行赋予极大速度。解决统一质量比例。将ClawLeft/RightMass设为0.4ClawBase设为0.9ToyItem保持0.6。原则夹爪总质量 ≥ 玩具质量 × 1.2。4.5 现象多个玩具堆叠时夹爪只夹到最上面一个下面的玩具完全无反应原因所有玩具共用同一Collision FilterCategory如0x0002而ClawLeft/Right的Mask只设为0x0002导致 Box2D 默认只处理第一个碰撞体。解决为每个玩具分配唯一 Category。在ToyItem.ts的onLoad()中动态设置const collider this.getComponent(Collider2D); collider?.setCategory(0x0002 | (this.toyIndex 8)); // 用索引高位区分同时ClawLeft/Right的Mask改为0xFFFF全通配确保检测所有玩具。5. 物理参数调优技巧用三组对照实验快速定位手感瓶颈参数调优不是蒙眼乱试。我总结了一套“三组对照实验法”每次只动一个变量用固定操作如“标准下探→闭合→提拉”对比结果30 分钟内就能锁定问题根源。以下是我在某跨平台系统项目中验证过的三组黄金组合。5.1 实验一分离“夹紧力”与“夹紧速度”的感知新手常混淆applyForce力和DistanceJoint2D的Max/Min Distance距离。其实前者决定“能不能夹住”后者决定“夹多紧”。用以下三组测试测试组applyForceMin Distance结果现象手感诊断A 组600.04夹爪闭合慢玩具易滑脱力不足需增大applyForceB 组1000.04夹爪“啪”一声闭合玩具弹跳力过大需降低applyForceC 组800.08夹爪闭合后仍有缝隙玩具晃动Min Distance过大需减小操作建议先固定applyForce80只调Min Distance从 0.10 递减到 0.04找到玩具刚好不晃动的临界值再以此值为基准微调applyForce±10优化闭合节奏。5.2 实验二验证“提拉稳定性”的质量配比提拉失败往往源于质量比失衡。准备三个质量不同的玩具Mass0.4,0.6,0.8用同一夹爪ClawBase Mass0.9测试玩具质量提拉成功率失败表现优化方向0.4100%—夹爪过强可降ClawBase Mass至 0.70.695%偶尔滑脱当前配比最优无需调整0.840%玩具下坠夹爪被拖拽必须提升ClawBase Mass至 1.1血泪经验ClawBase Mass应 ≥ToyItem Mass× 1.3。0.6×1.30.78所以ClawBase Mass0.9是安全冗余值。低于此值提拉失败率陡增。5.3 实验三摩擦与密度的协同效应Friction摩擦和Density密度共同决定玩具“被夹住后的行为”。用同一玩具Mass0.6测试不同组合FrictionDensity行为表现原因分析0.30.3夹住后玩具软塌塌下垂密度小→刚性弱摩擦再大也撑不起形0.50.5夹住后挺立提拉平稳黄金组合摩擦提供咬合力密度保证结构刚性0.70.5夹住后玩具僵硬提拉时抖动摩擦过大→接触面锁死Box2D 求解器震荡技巧先固定Density0.5调Friction找“不滑脱又不僵硬”的区间0.4~0.6再微调Density±0.1优化提拉顺滑度。记住Friction影响“横向滑动”Density影响“纵向形变”。从那以后我每次调物理参数都强制走一遍这三组对照实验——哪怕赶工期也宁可花 20 分钟跑三遍不赌“这次应该没问题”。因为 Box2D 的非线性特性一个参数的微小变动可能在特定角度下引发连锁翻车。希望帮到你。本文还有配套的精品资源点击获取