Open chillbert opened 8 months ago
Unfortunately, at the moment, you will need to combine the animations to your character.glb to make it work. I tried to create a featrue using external fbx animations before, but the transition in between was really bad. I will get back to this feature maybe latter on 😅
So, how to increase the animation amount? e.g. action1, action2 ... action8
I was able to add support for loading external fbx animations in my project.
Here is the updated EcctrlAnimation.tsx file:
import { useEffect, useRef, Suspense, useState, useMemo } from "react";
import * as THREE from "three";
import { useGame, type AnimationSet } from "./stores/useGame";
import React from "react";
import { useFrame, useLoader } from "@react-three/fiber";
import { FBXLoader } from "three-stdlib";
export const ANIMATIONS = {
idle: '/resources/animation/Happy.fbx',
walk: '/resources/animation/Happy Walk.fbx',
run: '/resources/animation/Fast Run.fbx',
dancing: '/resources/animation/Silly Dancing.fbx',
sad: '/resources/animation/Sad Idle.fbx',
excited: '/resources/animation/Excited.fbx',
point: '/resources/animation/Angry Point.fbx',
clap: '/resources/animation/Clapping.fbx',
rally: '/resources/animation/Rallying.fbx',
thankful: '/resources/animation/Thankful.fbx',
jump: '/resources/animation/Jump.fbx',
tpose: '/resources/animation/T-Pose.fbx',
sleep: '/resources/animation/Sleep.fbx',
eating: '/resources/animation/eating.fbx',
};
export function EcctrlAnimation(props: EcctrlAnimationProps) {
const [mixer, setMixer] = useState<THREE.AnimationMixer | null>(null);
// Change the character src to yours
const group = useRef();
// const { animations } = useGLTF(props.characterURL);
const animations = useLoader(FBXLoader, Object.values(ANIMATIONS)).map(f => f.animations[0]);
const actions = useMemo(() => mixer ? Object.keys(ANIMATIONS).reduce<{ [key: string]: THREE.AnimationAction }>((acc, key, index) => {
acc[key] = mixer.clipAction(animations[index], props.character);
return acc;
}, {}) : {}, [mixer, animations]);
useFrame((_, delta) => {
mixer?.update(delta);
// TWEEN.update();
});
useEffect(() => {
if (!props.character) return;
const newMixer = new THREE.AnimationMixer(props.character);
setMixer(newMixer);
return () => {
newMixer.stopAllAction();
newMixer.uncacheRoot(newMixer.getRoot());
};
}, [props.character]);
/**
* Character animations setup
*/
const curAnimation = useGame((state) => state.curAnimation);
const resetAnimation = useGame((state) => state.reset);
const initializeAnimationSet = useGame(
(state) => state.initializeAnimationSet
);
useEffect(() => {
// Initialize animation set
initializeAnimationSet(props.animationSet);
}, [actions]);
useEffect(() => {
// Play animation
const action =
actions[curAnimation ? curAnimation : props.animationSet.jumpIdle];
if(!action) return;
// For jump and jump land animation, only play once and clamp when finish
if (
curAnimation === props.animationSet.jump ||
curAnimation === props.animationSet.jumpLand ||
curAnimation === props.animationSet.action1 ||
curAnimation === props.animationSet.action2 ||
curAnimation === props.animationSet.action3 ||
curAnimation === props.animationSet.action4
) {
action
.reset()
.fadeIn(0.2)
.setLoop(THREE.LoopOnce, undefined as number)
.play();
action.clampWhenFinished = true;
} else {
action?.reset().fadeIn(0.2).play();
}
// When any action is clamp and finished reset animation
mixer?.addEventListener("finished", () => resetAnimation());
return () => {
// Fade out previous action
action.fadeOut(0.2);
// Clean up mixer listener, and empty the _listeners array
// (action as any)._mixer.removeEventListener("finished", () =>
// resetAnimation()
// );
// (action as any)._mixer._listeners = [];
};
}, [curAnimation]);
return (
<Suspense fallback={null}>
<group ref={group} dispose={null} userData={{ camExcludeCollision: true }}>
{/* Replace character model here */}
{props.children}
</group>
</Suspense>
);
}
export type EcctrlAnimationProps = {
character: any;
animationSet: AnimationSet;
children: React.ReactNode;
};
feel free to remove this if you dislike me pointing out my own creation but it does not intefere or overwrite this in anyway.I just created and launched a threejs based site to do this, it is all browser with no backend. You can drag models (fbx, obj, glb or gltf) in, then drag all your animations in and click export and it will export a new glb with all the fbx anmations in it. you can use fbx as your starter model as well as fbx for your animations from mixamo etc.
As always if its animations based on bones etc you would probably want to drag the fbx to mixamo first then download the animations you want. then you can use my site to get them all into the main character (or multiple characters if they all have the same bone layout).
You can also use it for a batch fbx/obj to glb converter :)
My setup is usually so that I have a character.glb file and then lots of different animations from one or multiple fbx files, which I apply on the character like this:
(this setup makes totally sense when you have multiple characters in a multiplayer game with small size and have only once to load all the animations for all players (since they are all the same right?):
how would I achieve this in this case:
Do I need to manipulte the EcctrlAnimation.tsx by myself?