Animations
Model Entities, which are entities that have a modelUri
when created, can have their in-game animation state completely controlled and blended using the Entity animation methods.
Animations do not work with Block Entities.
GLTF Animations
The HYTOPIA SDK expects all possible animations of a model to be included in a model's .gltf file. Model animations are referenced by their name. For example, if your .gltf file has an animation called idle
that you want to play and loop as soon as an entity spawns, you'd do that as follows:
const myEntity = new Entity({
modelUri: 'models/npcs/cow.gltf',
modelLoopedAnimations: [ 'idle' ], // animations here will play looped when entity spawns
// .. the rest of your entity options
});
Types Of Animations
HYTOPIA currently supports two types of animations.
Looped Animations - These are animations that will contiue to play over and over again from start to finish in a smooth manner until they are explicitly stopped.
One-shot Animations - These are animations that will play from start to finish only once, or until they are explicitly stopped or restarted.
Animation Blending & Playback
At this time, all played animations for a model entity are equally blended in the visual playback state of the entity in game. This means you can play multiple GLTF model animations at the same time and they will blend equally between the same model node that may be involved in multiple playing animations.
Starting And Stopping Animations
For model entities, we can easily start, stop and filter the played animations of an entity. Here's an example of how our DefaultCharacterController internally handles animation state changes based on player inputs.
Animation state is internally tracked - so invoking start or stop of animations that are in an irrelevant state won't cause unusual animation states or performance overhead.
// tickPlayerMovement is a method from BaseCharacterController
// it is called each tick when associated with an entity
// controlled by a player (PlayerEntity)
public tickPlayerMovement(inputState: PlayerInputState, orientationState: PlayerOrientationState, deltaTimeMs: number) {
// current pressed keystate of the controlling
// player of the entity has pressed this tick.
const { w, a, s, d, sp, sh, ml } = inputState;
// ... Other character controller logic
if (this.isGrounded && (w || a || s || d)) {
if (isRunning) {
// Stop all animations that aren't run
this.entity.stopModelAnimations(Array.from(this.entity.modelLoopedAnimations).filter(v => v !== 'run'));
// Play run animation, if it's already playing it won't be restarted since it's looped
this.entity.startModelLoopedAnimations([ 'run' ]);
} else {
// Stop all animations that aren't walk
this.entity.stopModelAnimations(Array.from(this.entity.modelLoopedAnimations).filter(v => v !== 'walk'));
// Play walk animation, if it's already playing it won't be restarted since it's looped
this.entity.startModelLoopedAnimations([ 'walk' ]);
}
} else {
// Stop all animations that aren't idle
this.entity.stopModelAnimations(Array.from(this.entity.modelLoopedAnimations).filter(v => v !== 'idle'));
// Play idle animation, if it's already playing it won't be restarted since it's looped
this.entity.startModelLoopedAnimations([ 'idle' ]);
}
if (ml) { // player pressed the left mouse button
// Play our animation named 'simple_interact' once./
this.entity.startModelOneshotAnimations([ 'simple_interact' ]);
}
// ... Other character controller logic
}
One-shot Animations
One-shot animations are exactly what they sound like. An animation that plays only once. This is great for things like a gun-shooting effect when the player presses a button in game to fire a weapon, or a movement of a player's hand each time they click to interact with something, etc.
Let's assume our PlayerEntity model assigned to a player when they join a game has a dance
animation, and a hand waving animation called wave
. We'll play then as a one-shot when a player joins.
world.on(PlayerEvent.JOINED_WORLD, ({ player }) => {
const playerEntity = new DefaultPlayerEntity({
player,
name: 'Player',
});
playerEntity.spawn(world, { x: 0, y: 10, z: 0 });
// Note: An entity must be spawned to start/stop animations,
// otherwise the invoked animation methods will throw an error.
// dance and wave animation is assumed to exist in the my-player.gltf
// file. Both animations will play and blend together.
playerEntity.startModelOneshotAnimations([ 'dance', 'wave' ]);
});
Animations & Entity Colliders
It's important to note that the animation state of model entities is entirely visual. Played animations will not automatically alter or move the collider(s) of an entity even if the entity is visually moving relative to it's collider because of the animation.
If you need your collider to move along with the animation state played, you'll need to implement this manually with your own timings and logic to alter the translation and rotation of the entity collider(s) relative to the animation played.
Last updated