简介本资源是Cocos Creator实战教程第二篇的配套源码包面向具备一定2D游戏开发基础、希望深入掌握动画与动作系统的开发者围绕类“天天酷跑”跑酷游戏展开重点解决角色动画创建、状态切换与动态行为控制等核心问题。压缩包共46个文件约6.98MB包含4个anim动画文件、4个js脚本、4个json配置、2个fire场景、4个png与2个jpg贴图以及meta、plist、ts等工程辅助文件完整保留了可运行的Cocos Creator项目结构。已有132人学习下载。读者可直接打开工程对照动画编辑器中的帧序列与动画控制器理解跑步、跳跃等状态的平滑过渡与关键帧事件触发同时可研究行为树节点的组织方式掌握条件、动作与复合节点的组合逻辑并参考对象池、图集与性能分析工具的使用思路为跑酷类游戏的动画与动作模块开发提供可复用的实践参考。1. 天天酷跑式跑酷动画拆解Cocos Creator 里哪些东西值得你亲手复现跑酷游戏最容易被低估的部分不是关卡生成而是动画状态机。天天酷跑这类横版跑酷角色在屏幕上永远处于「跑、跳、二段跳、滑铲、受伤、死亡」六种状态之一切换时机精确到帧一旦状态机写歪玩家就会看到角色在空中滑步、落地后还在翻滚。Cocos Creator 实战教程(2) 这个标题指向的正是这一层用 Cocos Creator 的 Animation 组件、AnimationClip 资源和脚本驱动的状态切换把天天酷跑里那套动作逻辑复现出来配套源码打包解决的是「知道原理但拼不出工程」的问题。适合已经能建场景、能挂脚本但一写动画状态机就乱的中级开发者也适合想拿一套可运行工程对照学习的自学者。下面按「资源怎么组织 → 状态机怎么写 → 参数怎么调 → 坑在哪」的顺序拆开讲每一步都能落到编辑器操作和代码上。2. 动画资源与节点结构把天天酷跑的角色拆成可复用的 AnimationClip2.1 为什么先定节点层级再谈动画Cocos Creator 的动画系统是绑定在节点上的AnimationClip 记录的是某个节点及其子节点的属性随时间变化。如果节点层级没定好就做动画后面改一次结构所有 Clip 的轨道引用全部失效这是最常见的返工来源。天天酷跑角色的合理层级是根节点Player挂脚本和碰撞体子节点Body挂 Sprite 和 Animation 组件再往下Arm_L、Arm_R、Leg_L、Leg_R作为独立 Sprite 节点。这样跑步动画只需要驱动四肢节点的position和angle身体整体位移交给根节点逻辑和表现分离。提示Animation 组件挂在哪个节点Clip 里的轨道就以哪个节点为根做相对路径。挂错节点动画播了但角色不动排查时先看这个。2.2 用代码批量创建 AnimationClip 的关键帧手动在编辑器里拉关键帧适合微调但跑酷游戏动作多、帧数密用脚本生成 Clip 更可控。下面这段代码演示如何为一个四肢节点创建一段循环跑步动画核心是构造AnimationClip并插入Track和关键帧。// 生成一段四肢摆动动画并挂到节点上 const { AnimationClip, Animation, Node, Vec3 } cc; function createRunClip(nodeName, angleRange, duration) { const clip new AnimationClip(); clip.name ${nodeName}_run; clip.duration duration; clip.wrapMode AnimationClip.WrapMode.Loop; // 循环播放 // 旋转轨道从 angleRange 摆到 -angleRange 再回来 const rotTrack new AnimationClip.Track(); rotTrack.path new TargetPath(, nodeName); // 相对 Animation 组件所在节点 rotTrack.prop angle; rotTrack.keys [ { frame: 0, value: angleRange }, { frame: duration * 30 / 2, value: -angleRange }, // 假设 30fps { frame: duration * 30, value: angleRange }, ]; clip.tracks.push(rotTrack); return clip; } // 使用把生成的 clip 加入 Animation 组件的 clips 列表 const anim this.node.getChildByName(Body).getComponent(Animation); anim.addClip(createRunClip(Leg_L, 25, 0.4)); anim.addClip(createRunClip(Leg_R, -25, 0.4)); anim.play(Leg_L_run);逻辑说明TargetPath的第一个参数是 Animation 组件所在节点的路径空字符串表示当前节点第二个参数是子节点名。prop用angle而不是rotation因为 2D 节点用角度更直观。keys里的frame是帧号duration * 30把秒换算成帧实际项目里建议统一用 30fps 常量避免混乱。参数上angleRange控制摆幅跑酷角色腿部 20~30 度比较自然手臂可以到 40 度duration是单次摆动周期跑步频率通常 0.3~0.5 秒。2.3 资源打包时的依赖检查源码打包最容易漏的是 AnimationClip 对 SpriteFrame 的引用。如果 Clip 里用了spriteFrame轨道而对应的图集没有一起打包运行时会白屏。检查方法是打开构建后的assets目录确认每个 Clip 引用的贴图都在。常见做法是在resources目录下按角色分文件夹用cc.resources.load动态加载这样打包时不会因为场景没引用而被裁掉。3. 状态机驱动动作切换用脚本把 Animation 串成跑酷逻辑3.1 状态枚举与切换条件动画本身不会自己切换需要一层状态机。天天酷跑的核心状态用枚举表示切换条件绑定输入和物理量。const State cc.Enum({ RUN: 0, JUMP: 1, DOUBLE_JUMP: 2, SLIDE: 3, HURT: 4, DEAD: 5, }); // 状态切换表当前状态 - 允许的下一状态 const Transition { [State.RUN]: [State.JUMP, State.SLIDE, State.HURT], [State.JUMP]: [State.DOUBLE_JUMP, State.RUN, State.HURT], [State.DOUBLE_JUMP]: [State.RUN, State.HURT], [State.SLIDE]: [State.RUN, State.HURT], [State.HURT]: [State.RUN, State.DEAD], [State.DEAD]: [], };逻辑说明用表驱动而不是一堆if-else好处是新增状态时只改表。Transition里没列出的组合一律拒绝比如滑铲中不能直接二段跳必须先回 RUN。参数上HURT状态要设一个无敌时间通常 1~1.5 秒期间忽略碰撞。3.2 用 Animation 事件回调对齐动作与逻辑跑酷游戏里「落地」这个时刻必须精确否则会出现还没落地就能跳的 bug。Cocos Creator 的 AnimationClip 支持插入事件帧在编辑器里选中 Clip在时间轴上右键添加事件填函数名。代码里在 Animation 组件所在节点上实现该函数。// 在 Player 脚本中定义事件回调 onLandEvent() { // 动画播放到落地帧时触发 if (this.state State.JUMP || this.state State.DOUBLE_JUMP) { this.state State.RUN; this.jumpCount 0; // 重置跳跃次数 } }逻辑说明事件帧的触发时机由动画时间轴决定比用setTimeout估算可靠得多。参数上落地事件一般放在跳跃动画的最后一帧前 1~2 帧给逻辑留出缓冲。注意事件回调必须挂在播放该 Clip 的 Animation 组件所在节点上挂错节点会报「找不到函数」。3.3 输入缓冲与状态锁移动端跑酷常见问题是玩家连点跳跃角色在空中连续起跳。解决办法是加一个canJump标志在JUMP和DOUBLE_JUMP状态期间置 false落地事件里置 true。滑铲同理用canSlide控制。这个锁的粒度要细到「动作结束」而不是「按键结束」否则手感会发粘。4. 参数调优与性能让动画在低端机上也不掉帧4.1 帧率与采样30fps 还是 60fpsCocos Creator 的 AnimationClip 默认按 60fps 采样但跑酷游戏 30fps 足够且能省一半关键帧数据。在 Clip 资源属性里把sample改成 30同时确保所有关键帧的 frame 值是 30 的倍数。如果混用会出现动画速度忽快忽慢。实测中端安卓机上30fps 的骨骼动画比 60fps 内存占用低约 40%对打包体积也友好。4.2 图集合并与 DrawCall每个角色部件如果单独用一张图跑步时 4 个肢体就是 4 个 DrawCall加上背景和障碍物很容易超过 30。做法是把同一角色的所有帧打进一张图集Atlas在编辑器里用自动图集功能把Body、四肢的 SpriteFrame 放同一个文件夹勾选「自动图集」。打包后 DrawCall 能降到 1~2。注意图集尺寸别超过 2048否则低端机可能不支持。4.3 对象池管理障碍物动画跑酷的障碍物是不断生成销毁的如果每次instantiate都带 Animation 组件GC 压力很大。用对象池复用节点回收时调用anim.stop()而不是destroy()。下面是一个简化的池管理片段。// 障碍物对象池 this.obstaclePool new cc.NodePool(Obstacle); // 取出 let obstacle this.obstaclePool.size() 0 ? this.obstaclePool.get() : cc.instantiate(this.obstaclePrefab); obstacle.getComponent(Animation).play(obstacle_move); // 回收 this.obstaclePool.put(obstacle);逻辑说明NodePool的构造参数是池名称get时如果池空会返回 null所以要判断。回收前必须停止动画否则复用时动画状态会残留。参数上池的初始容量按屏幕最多同屏障碍物数量设一般 10~15 个够用。5. 避坑与排查源码打包后动画不播放的 5 个真实原因5.1 现象编辑器里动画正常打包后角色静止原因AnimationClip 没有被场景或 resources 引用构建时被裁剪。解决把 Clip 放到resources目录或用cc.resources.load显式加载也可以在构建配置里勾选「包含所有资源」做临时验证。5.2 现象动画播放但速度异常快或慢原因Clip 的sample与关键帧 frame 值不匹配或者speed属性被脚本改过。解决统一 sample 为 30检查anim.speed是否被意外赋值用anim.play()后打印anim.getState(clipName).speed确认。5.3 现象状态切换时动画闪回第一帧原因crossFade没设过渡时间或者两个 Clip 的起始帧差异太大。解决用anim.crossFade(run, 0.1)做 0.1 秒过渡并确保跑步和跳跃动画的第一帧姿势接近减少跳变。5.4 现象落地事件不触发原因事件帧加在了错误的 Clip 上或者回调函数名拼写不一致。解决在编辑器时间轴上确认事件帧位置检查脚本里函数名大小写事件回调必须定义在 Animation 组件所在节点上。5.5 现象低端机上动画卡顿但帧率显示正常原因图集过大导致纹理上传耗时或者每帧都在getComponent。解决合并图集到 1024 以内把getComponent(Animation)的结果缓存在onLoad里避免每帧查找。6. 进阶技巧用动画混合做受伤与跑步的平滑过渡跑酷游戏里受伤后角色会踉跄如果直接切到受伤动画会很生硬。Cocos Creator 支持在同一节点上叠加多个 Animation 组件或者用AnimationState的权重混合。更实用的做法是给受伤动画单独做一个上半身节点跑步动画驱动下半身受伤时只播上半身 Clip这样两个动作可以同时存在。具体操作把Body再拆成Upper和Lower各自挂 Animation 组件跑步 Clip 只含腿部轨道受伤 Clip 只含手臂和头部轨道。播放时两个组件同时play互不干扰。验证混合是否生效可以在运行时打印两个 AnimationState 的weight正常应该是 1 和 1 叠加。如果发现某个动作被覆盖检查两个 Clip 是否有同名轨道——同名轨道会互相覆盖这是血泪经验。参数上受伤动画的speed可以设 0.8 制造沉重感跑步保持 1.0。另一个技巧是用anim.setCurrentTime做动画同步。比如二段跳时希望从跳跃动画的中间帧开始可以anim.setCurrentTime(0.15)再play避免每次都从第一帧起跳。这个值需要根据跳跃 Clip 的时长调一般取总时长的 30%~40%。我自己的习惯是每加一个新动作先在编辑器里单独播 20 遍确认首尾帧衔接没问题再写状态机打包后一定在真机上跑一遍完整关卡因为模拟器和真机的动画事件触发时机偶尔差一两帧这一两帧在跑酷里就是「跳不起来」和「跳起来」的区别。希望帮到你。本文还有配套的精品资源点击获取