joken-前端工程师

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: :: :: 管理 ::
  404 随笔 :: 39 文章 :: 8 评论 :: 20万 阅读

在 Three.js 中,如果一个 3D 模型文件包含多个动画(例如 GLTF 文件中的多个 AnimationClip),你可以将这些动画收集到一个数组中,并通过代码逐一调用或管理它们。以下是详细的说明和实现方法。


结论

可以将模型文件中的多个动画收集为一个数组,并在 Three.js 中通过 THREE.AnimationMixerTHREE.AnimationAction 来管理和播放这些动画。


详细展开

1. 加载模型中的动画剪辑

当使用 GLTFLoader 加载模型时,模型文件中的所有动画剪辑会被自动解析并存储在 gltf.animations 数组中。你可以直接访问这个数组来获取所有动画。

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

const loader = new GLTFLoader();

loader.load('model.glb', (gltf) => {
    const model = gltf.scene; // 获取模型对象
    const animations = gltf.animations; // 获取动画剪辑数组

    console.log('模型包含的动画:', animations);
});

2. 创建动画混合器

为了播放动画,需要为模型创建一个 THREE.AnimationMixer 实例。每个动画剪辑可以通过 mixer.clipAction(clip) 方法创建一个对应的 AnimationAction 对象。

const mixer = new THREE.AnimationMixer(model); // 创建动画混合器

3. 将动画收集到数组中

你可以将所有动画剪辑的 AnimationAction 对象存储在一个数组中,以便后续管理和调用。

const animationActions = []; // 用于存储所有动画动作

animations.forEach((clip, index) => {
    const action = mixer.clipAction(clip); // 为每个动画剪辑创建动作
    animationActions.push(action); // 将动作添加到数组中

    console.log(`动画 ${index}:`, clip.name || `未命名动画 ${index}`);
});

4. 播放特定动画

通过索引或名称从数组中选择某个动画并播放。

// 播放第一个动画
if (animationActions.length > 0) {
    animationActions[0].play();
}

// 或者根据名称播放动画
const targetAnimationName = 'run'; // 假设有一个名为 "run" 的动画
const targetAction = animationActions.find(action => action.getClip().name === targetAnimationName);

if (targetAction) {
    targetAction.play();
} else {
    console.warn(`未找到名为 "${targetAnimationName}" 的动画`);
}

5. 切换动画

可以通过停止当前动画并播放新动画来实现动画切换。

function switchToAnimation(index) {
    // 停止所有正在播放的动画
    animationActions.forEach(action => action.stop());

    // 播放指定索引的动画
    if (animationActions[index]) {
        animationActions[index].play();
    } else {
        console.warn(`索引 ${index} 超出范围`);
    }
}

// 示例:切换到第二个动画
switchToAnimation(1);

6. 淡入淡出切换动画

使用 crossFadeTo 方法可以在两个动画之间进行平滑过渡。

function fadeToAnimation(targetIndex, duration = 1) {
    // 找到目标动画
    const targetAction = animationActions[targetIndex];
    if (!targetAction) {
        console.warn(`索引 ${targetIndex} 超出范围`);
        return;
    }

    // 遍历所有动画,停止其他动画并淡入目标动画
    animationActions.forEach((action, index) => {
        if (index !== targetIndex && action.isRunning()) {
            action.crossFadeTo(targetAction, duration); // 淡出当前动画
        }
    });

    targetAction.play(); // 播放目标动画
}

// 示例:淡入到第三个动画,持续时间 2 秒
fadeToAnimation(2, 2);

7. 完整示例代码

以下是一个完整的示例,展示如何加载模型、收集动画并切换动画:

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

const loader = new GLTFLoader();

loader.load('model.glb', (gltf) => {
    const model = gltf.scene;
    scene.add(model);

    const animations = gltf.animations; // 获取动画剪辑数组
    const mixer = new THREE.AnimationMixer(model); // 创建动画混合器

    const animationActions = []; // 存储动画动作

    animations.forEach((clip, index) => {
        const action = mixer.clipAction(clip); // 创建动画动作
        animationActions.push(action);

        console.log(`加载动画 ${index}:`, clip.name || `未命名动画 ${index}`);
    });

    // 默认播放第一个动画
    if (animationActions.length > 0) {
        animationActions[0].play();
    }

    // 切换到第二个动画
    function switchToSecondAnimation() {
        if (animationActions.length > 1) {
            switchToAnimation(1);
        }
    }

    // 更新动画
    let clock = new THREE.Clock();
    function animate() {
        requestAnimationFrame(animate);

        mixer.update(clock.getDelta()); // 更新动画混合器
        renderer.render(scene, camera);
    }
    animate();
});

总结

在 Three.js 中,可以将模型文件中的多个动画收集到一个数组中,并通过 THREE.AnimationMixerTHREE.AnimationAction 来管理和播放这些动画。通过这种方式,你可以轻松实现动画的切换、淡入淡出等效果,从而增强场景的交互性和视觉表现力。

posted on   joken1310  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示