mcpField
T
threejs-animation

threejs-animation

github.com/akillness开发5356 次安装今天更新
代码仓库

关于 threejs-animation

面向 Three.js 开发者的 3D 动画实现技能,涵盖导入动画、骨架和程序化动作。重点是按 delta 时间更新、妥善切换动作,并随模型生命周期清理状态。

主要功能

  1. 01使用 AnimationMixer 管理动画片段
  2. 02混合并切换动画动作
  3. 03驱动骨骼与形态目标
  4. 04按 delta 时间实现帧率无关的动作
  5. 05清理混合器状态和监听器

适用场景

  1. 01播放、暂停、定位或循环导入的动画
  2. 02修复依赖帧率的动画问题
  3. 03平滑混合动作,避免画面跳变
  4. 04模型移除后清除残留动画状态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/akillness/jeo-skills --skill threejs-animation

安装后在 Claude Code 中输入/threejs-animation确认已加载。

附带文件SKILL.toonevals/evals.json

详细介绍

Three.js 动画

使用此技能实现动画本身:片段选择、混合器所有权、混合、骨架/变形驱动,以及时间步长的正确性。使用 threejs-loaders 加载模型;如果主要问题是游戏状态或战斗设计,而非动画 API,请使用 web-game-development。

适用场景

  • 通过 AnimationMixer 播放、暂停、跳转、循环或混合 AnimationClip 数据
  • 将 GLTF 片段附加到已加载的场景,并随模型生命周期清理混合器
  • 为骨骼、变形目标影响值或轻量级程序化运动添加动画
  • 修复动画无法播放、过渡时跳变、播放速度随帧率变化或发生泄漏的问题

说明

步骤 1:确定动画的所有者和时钟
  1. 检查模型的 gltf.animations、根对象和当前渲染循环的所有者。
  2. 每个带动画的根对象创建一个混合器,并在每帧推进一次,使用经过的增量时间,而非固定的每帧时间量。
  3. 按语义名称保留动作引用;不要盲目按索引访问导出的片段数组,因为资源版本更新可能会改变片段顺序。
const mixer = new THREE.AnimationMixer(gltf.scene);
const clips = new Map(gltf.animations.map((clip) => [clip.name, clip]));
const idle = mixer.clipAction(clips.get("Idle"));
idle.reset().fadeIn(0.2).play();

const clock = new THREE.Clock();
function update() {
  mixer.update(clock.getDelta());
  renderer.render(scene, camera);
  requestAnimationFrame(update);
}
update();
步骤 2:平滑过渡动作,避免视觉不连续

明确设置循环和钳制行为;播放新动作前重置该动作;只有片段共用兼容的姿势/骨架时,才使用交叉淡化。

function transition(from, to, duration = 0.2) {
  to.reset().setEffectiveWeight(1).setEffectiveTimeScale(1).play();
  from.crossFadeTo(to, duration, false);
}

对于单次播放的动作,监听混合器的 finished 事件,并切换回已知的待机动作。功能拆除时移除监听器。

步骤 3:选择最精简的动画机制
需求默认机制
已制作的变换/属性关键帧AnimationClip + 关键帧轨道
导入的角色动画AnimationMixer + clipAction
逐帧待机动作或 UI 响应基于增量时间的程序化更新
面部表情或网格变形morphTargetInfluences
运行时骨骼调整混合器更新后的骨骼变换
分层叠加姿势仅在验证参考姿势后使用叠加片段

在 mixer.update(delta) 之后应用程序化骨骼偏移,以免已制作的片段覆盖这些偏移。将开销较大的 IK、物理或重定向处理限制在经过测量的预算内。

步骤 4:随功能一同释放动画状态

带动画的根对象被永久移除时,调用 mixer.stopAllAction() 和 mixer.uncacheRoot(root)。如果根对象由其他场景或实例共享,则不要取消其缓存。

步骤 5:验证动画行为
  • 在低帧率和高帧率下测试;动画时长和移动距离必须保持一致。
  • 测试过渡中断、单次播放完成和快速切换动作。
  • 确认命名片段存在,并在资源导出内容发生变化时报告有用的错误。
  • 反复挂载/卸载场景来验证清理效果,确认监听器或保留的混合器动作不会增加。

示例

为变形目标添加动画
const face = gltf.scene.getObjectByName("Face");
face.morphTargetInfluences[0] = 0.75;

确认网格包含变形目标;如果导出内容包含多个表情,请使用资源中的命名索引映射,而非魔法数字。

驱动简单的程序化上下浮动
const elapsed = clock.getElapsedTime();
mesh.position.y = baseY + Math.sin(elapsed * 2) * 0.1;

单独存储 baseY。累加位置变化会造成漂移,也会使其难以与已制作的动画混合。

最佳实践

  1. 每帧使用以秒为单位的 delta,且恰好更新混合器一次。
  2. 已制作的动作优先使用导出的片段;程序化偏移应为叠加式且局部化。
  3. 在导出时为片段命名,并在加载时验证名称。
  4. 克隆骨架或混合器前,确认骨架和材质的共享所有权。
  5. 在目标硬件上分析蒙皮网格和变形目标的性能,再增加其数量。

参考资料

评论

支持 Markdown