Next.js中react-three/drei的useFBX无法读取FBX动画文件求助
解决Next.js中useFBX加载Mixamo动画返回undefined的问题
问题核心
你错误解构了useFBX的返回值——useFBX直接返回加载后的FBX Object3D对象,而非包含animation属性的结构,这是typingAnimation为undefined的直接原因。
修复步骤
- 修正useFBX的使用逻辑
useFBX加载完成后,动画数据存储在返回对象的animations数组中(Mixamo下载的FBX通常仅包含一个动画,取索引0即可)。修改代码如下:
// 错误写法 // const {animation:typingAnimation} = useFBX("/animations/TypingAgain.fbx") // 正确写法 const fbxAsset = useFBX("/animations/TypingAgain.fbx"); const typingAnimation = fbxAsset.animations[0];
- 添加FBX资源预加载
和useGLTF一样,useFBX需要预加载资源确保组件渲染时资源就绪,在组件外部添加预加载代码:
useFBX.preload("/animations/TypingAgain.fbx");
- 验证文件路径一致性
检查/public/animations目录下是否存在TypingAgain.fbx文件,确保代码中使用的文件名与实际文件完全匹配(注意大小写、拼写)。
修改后的完整代码示例
import React, { useEffect, useRef } from "react"; import { useFBX, useGLTF } from "@react-three/drei"; export function Avatar(props) { const { nodes, materials } = useGLTF("/images/3d/avatar.glb"); // 修正FBX加载逻辑 const fbxAsset = useFBX("/animations/TypingAgain.fbx"); const typingAnimation = fbxAsset.animations[0]; useEffect(()=>{ // 加载完成后再执行动画相关逻辑 if(typingAnimation) { console.log(typingAnimation); // 后续可通过AnimationMixer绑定并播放动画 } }, [typingAnimation]) return ( <group {...props} dispose={null}> <primitive object={nodes.Hips} /> <skinnedMesh name="EyeLeft" geometry={nodes.EyeLeft.geometry} material={materials.Wolf3D_Eye} skeleton={nodes.EyeLeft.skeleton} morphTargetDictionary={nodes.EyeLeft.morphTargetDictionary} morphTargetInfluences={nodes.EyeLeft.morphTargetInfluences} /> <skinnedMesh name="EyeRight" geometry={nodes.EyeRight.geometry} material={materials.Wolf3D_Eye} skeleton={nodes.EyeRight.skeleton} morphTargetDictionary={nodes.EyeRight.morphTargetDictionary} morphTargetInfluences={nodes.EyeRight.morphTargetInfluences} /> <skinnedMesh name="Wolf3D_Head" geometry={nodes.Wolf3D_Head.geometry} material={materials.Wolf3D_Skin} skeleton={nodes.Wolf3D_Head.skeleton} morphTargetDictionary={nodes.Wolf3D_Head.morphTargetDictionary} morphTargetInfluences={nodes.Wolf3D_Head.morphTargetInfluences} /> <skinnedMesh name="Wolf3D_Teeth" geometry={nodes.Wolf3D_Teeth.geometry} material={materials.Wolf3D_Teeth} skeleton={nodes.Wolf3D_Teeth.skeleton} morphTargetDictionary={nodes.Wolf3D_Teeth.morphTargetDictionary} morphTargetInfluences={nodes.Wolf3D_Teeth.morphTargetInfluences} /> <skinnedMesh geometry={nodes.Wolf3D_Hair.geometry} material={materials.Wolf3D_Hair} skeleton={nodes.Wolf3D_Hair.skeleton} /> <skinnedMesh geometry={nodes.Wolf3D_Glasses.geometry} material={materials.Wolf3D_Glasses} skeleton={nodes.Wolf3D_Glasses.skeleton} /> <skinnedMesh name="Wolf3D_Outfit_Top" geometry={nodes.Wolf3D_Outfit_Top.geometry} material={materials.Wolf3D_Outfit_Top} skeleton={nodes.Wolf3D_Outfit_Top.skeleton} morphTargetDictionary={nodes.Wolf3D_Outfit_Top.morphTargetDictionary} morphTargetInfluences={nodes.Wolf3D_Outfit_Top.morphTargetInfluences} /> <skinnedMesh name="Wolf3D_Outfit_Bottom" geometry={nodes.Wolf3D_Outfit_Bottom.geometry} material={materials.Wolf3D_Outfit_Bottom} skeleton={nodes.Wolf3D_Outfit_Bottom.skeleton} morphTargetDictionary={nodes.Wolf3D_Outfit_Bottom.morphTargetDictionary} morphTargetInfluences={nodes.Wolf3D_Outfit_Bottom.morphTargetInfluences} /> <skinnedMesh name="Wolf3D_Outfit_Footwear" geometry={nodes.Wolf3D_Outfit_Footwear.geometry} material={materials.Wolf3D_Outfit_Footwear} skeleton={nodes.Wolf3D_Outfit_Footwear.skeleton} morphTargetDictionary={ nodes.Wolf3D_Outfit_Footwear.morphTargetDictionary } morphTargetInfluences={ nodes.Wolf3D_Outfit_Footwear.morphTargetInfluences } /> <skinnedMesh name="Wolf3D_Body" geometry={nodes.Wolf3D_Body.geometry} material={materials.Wolf3D_Body} skeleton={nodes.Wolf3D_Body.skeleton} morphTargetDictionary={nodes.Wolf3D_Body.morphTargetDictionary} morphTargetInfluences={nodes.Wolf3D_Body.morphTargetInfluences} /> </group> ); } useGLTF.preload("/images/3d/avatar.glb"); useFBX.preload("/animations/TypingAgain.fbx"); // 添加FBX预加载
额外提示
- 动画加载是异步过程,
useEffect中需先判断typingAnimation是否存在再执行后续逻辑。 - 后续播放动画需使用
AnimationMixer,可通过useAnimations钩子或手动创建Mixer绑定到Avatar骨骼上。
内容的提问来源于stack exchange,提问作者Petrando Richard
相关产品推荐
相关产品推荐

