Skip to content

238. 综合实战:开放世界(三十九)

Published:

上节实现了对话:

2026-03-08 22.33.08.gif

对话完成后提示按 D 开始跳舞。

我们实现下跳舞的逻辑。

有同学说,不对啊,士兵的模型好像没有跳舞的骨骼动画。

image.png

确实,但可以复制女孩的骨骼动画给他。

我们先封装两个工具方法:

image.png

分别是查找骨骼的 Mesh 和按照名字查找动画的。

function findSkinnedMesh(obj) {
  let result = null;
  obj.traverse((child) => {
    if (child.isSkinnedMesh && child.skeleton && !result) result = child;
  });
  return result;
}

function findAnimationByName(animations, keyword) {
  if (!animations.length) return null;
  const lower = keyword.toLowerCase();
  const found = animations.find((clip) => clip.name.toLowerCase().includes(lower));
  return found || animations[0];
}

核心代码是这里:

image.png

用 SkeletonUtils.retargetClip 把女孩的骨骼动画,重定向到士兵身上。

// 从 Michelle.glb 重定向跳舞动画到士兵(重定向后的 clip 需在 SkinnedMesh 的 mixer 上播放)
const soldierSkin = findSkinnedMesh(characterModel);
if (soldierSkin) {
new GLTFLoader(loadingManager).load('./Michelle.glb', (michelleGltf) => {
  const michelleSkin = findSkinnedMesh(michelleGltf.scene);
  const danceClip = findAnimationByName(michelleGltf.animations, 'dance');
  if (!michelleSkin || !danceClip) return;
  try {
    const retargetedClip = SkeletonUtils.retargetClip(soldierSkin, michelleSkin, danceClip, {
      hip: 'mixamorigHips',
      scale: 1,
      getBoneName: (bone) => bone.name
    });
    danceMixer = new THREE.AnimationMixer(soldierSkin);
    danceAction = danceMixer.clipAction(retargetedClip);
    danceAction.setLoop(THREE.LoopRepeat);
  } catch (err) {
    console.warn('跳舞动画重定向失败:', err);
  }
});
}

再分别定义两个开始和结束动画的方法:

image.png

export function startPlayerDance() {
  if (!danceMixer || !danceAction || isDancing) return;
  isDancing = true;
  if (currentAction) currentAction.fadeOut(0.2);
  danceAction.reset().fadeIn(0.2).play();
  currentAction = danceAction;
}

export function stopPlayerDance() {
  if (!danceMixer || !isDancing) return;
  isDancing = false;
  danceAction.fadeOut(0.2);
  if (idleAction) {
    idleAction.reset().fadeIn(0.2).play();
    currentAction = idleAction;
  }
}

定义用到的变量:

image.png

image.png

因为 D 键和行走冲突了,我们换一个:

image.png

如果靠近了,并且对话完成了,就可以跳舞:

image.png

分别加上 J 和 L 的处理逻辑就好了。

我们试一下:

2026-03-15 18.36.52.gif

2026-03-15 18.37.52.gif

这样,就可以一起跳舞了。

我们把女孩的骨骼动画重定向到了士兵身上!

案例代码上传了小册仓库

总结

这节我们通过骨骼动画的重定向实现了士兵和女孩一起跳舞的功能。

核心的代码就是 SketchUtils.retargetClip,但是其他的比如快捷键的改动也很多,如果有漏掉的可以看仓库的代码。

评论