Hey everyone!
I have a question regarding animations which I have been stuck for a while. I have decided to use the animations inside Babylon Js, and use a simple mesh for now as the character instead of importing a glb file.
I was going through the docs for animating a character but my animations don’t seem to get applied to my mesh, possibly because it has a physics shape and body attached to it?
I’d like the visible mesh to move up and down within the cylinder physicsShape, for a hover effect. I’d also like it to tilt forward, backward, left and right slightly when moving in those directions. I have created a playerAnimations file as follows:
import { Mesh, AnimationGroup, Animation } from '@babylonjs/core';
export class PlayerAnimations {
private idleAnimationGroup: AnimationGroup;
private forwardAnimationGroup: AnimationGroup;
private leftAnimationGroup: AnimationGroup; // If you're planning to add left animation
private rightAnimationGroup: AnimationGroup; // If you're planning to add right animation
private backwardAnimationGroup: AnimationGroup; // If you're planning to add right animation
constructor(private mesh: Mesh) {
this.idleAnimationGroup = this.createIdleAnimation();
this.forwardAnimationGroup = this.createForwardAnimation();
// Initialize other animations here if needed
this.leftAnimationGroup = this.createLeftAnimation();
this.rightAnimationGroup = this.createRightAnimation();
this.backwardAnimationGroup = this.createBackwardAnimation();
}
playIdleAnimation() {
// Stop all other animations
this.stopAllAnimations();
// Start the idle animation
this.idleAnimationGroup.start(true);
}
playForwardAnimation() {
// Stop all other animations
this.stopAllAnimations();
// Start the forward animation
this.forwardAnimationGroup.start(true);
}
playRightAnimation() {
// Stop all other animations
this.stopAllAnimations();
// Start the forward animation
this.rightAnimationGroup.start(true);
}
playLeftAnimation() {
// Stop all other animations
this.stopAllAnimations();
// Start the forward animation
this.leftAnimationGroup.start(true);
}
playBackwardAnimation() {
// Stop all other animations
this.stopAllAnimations();
// Start the forward animation
this.backwardAnimationGroup.start(true);
}
// Methods for playing left and right animations...
private stopAllAnimations() {
this.idleAnimationGroup.stop();
this.forwardAnimationGroup.stop();
// Stop other animation groups as needed
this.leftAnimationGroup.stop();
this.rightAnimationGroup.stop();
}
createIdleAnimation(): AnimationGroup {
const animationGroup = new AnimationGroup("Idle");
const upDownAnim = new Animation("idleUpDown", "position.y", 30, Animation.ANIMATIONTYPE_FLOAT, Animation.ANIMATIONLOOPMODE_CYCLE);
const keyFrames = [];
keyFrames.push({ frame: 0, value: this.mesh.position.y });
keyFrames.push({ frame: 15, value: this.mesh.position.y + 0.1 });
keyFrames.push({ frame: 30, value: this.mesh.position.y });
upDownAnim.setKeys(keyFrames);
animationGroup.addTargetedAnimation(upDownAnim, this.mesh);
// No need to start the animation here; it can be started when needed
return animationGroup; // Return the animation group
}
createForwardAnimation(): AnimationGroup {
const animationGroup = new AnimationGroup("Forward");
const upDownAnim = this.createUpDownAnimation("forwardUpDown");
animationGroup.addTargetedAnimation(upDownAnim, this.mesh);
const tiltAnim = new Animation("forwardTilt", "rotation.x", 30, Animation.ANIMATIONTYPE_FLOAT, Animation.ANIMATIONLOOPMODE_CYCLE);
const tiltKeys = [
{ frame: 0, value: 0 },
{ frame: 15, value: Math.PI / 18 },
{ frame: 30, value: 0 },
];
tiltAnim.setKeys(tiltKeys);
animationGroup.addTargetedAnimation(tiltAnim, this.mesh);
// No need to start the animation here; it can be started when needed
return animationGroup; // Return the animation group
}
createLeftAnimation(): AnimationGroup {
const animationGroup = new AnimationGroup("Left");
// Up and Down animation (reusing createUpDownAnimation method)
const upDownAnim = this.createUpDownAnimation("leftUpDown");
animationGroup.addTargetedAnimation(upDownAnim, this.mesh);
// Left Tilt animation
const tiltAnim = new Animation("leftTilt", "rotation.z", 30, Animation.ANIMATIONTYPE_FLOAT, Animation.ANIMATIONLOOPMODE_CYCLE);
const tiltKeys = [
{ frame: 0, value: 0 },
{ frame: 15, value: -Math.PI / 18 }, // Slight left tilt
{ frame: 30, value: 0 },
];
tiltAnim.setKeys(tiltKeys);
animationGroup.addTargetedAnimation(tiltAnim, this.mesh);
return animationGroup;
}
createRightAnimation(): AnimationGroup {
const animationGroup = new AnimationGroup("Right");
// Up and Down animation (reusing createUpDownAnimation method)
const upDownAnim = this.createUpDownAnimation("rightUpDown");
animationGroup.addTargetedAnimation(upDownAnim, this.mesh);
// Right Tilt animation
const tiltAnim = new Animation("rightTilt", "rotation.z", 30, Animation.ANIMATIONTYPE_FLOAT, Animation.ANIMATIONLOOPMODE_CYCLE);
const tiltKeys = [
{ frame: 0, value: 0 },
{ frame: 15, value: Math.PI / 18 }, // Slight right tilt
{ frame: 30, value: 0 },
];
tiltAnim.setKeys(tiltKeys);
animationGroup.addTargetedAnimation(tiltAnim, this.mesh);
return animationGroup;
}
createBackwardAnimation(): AnimationGroup {
const animationGroup = new AnimationGroup("Backward");
// Up and Down animation (reusing createUpDownAnimation method)
const upDownAnim = this.createUpDownAnimation("backwardUpDown");
animationGroup.addTargetedAnimation(upDownAnim, this.mesh);
// Backward Tilt animation
const tiltAnim = new Animation("backwardTilt", "rotation.x", 30, Animation.ANIMATIONTYPE_FLOAT, Animation.ANIMATIONLOOPMODE_CYCLE);
const tiltKeys = [
{ frame: 0, value: 0 },
{ frame: 15, value: -Math.PI / 18 }, // Slight backward tilt
{ frame: 30, value: 0 },
];
tiltAnim.setKeys(tiltKeys);
animationGroup.addTargetedAnimation(tiltAnim, this.mesh);
return animationGroup;
}
createUpDownAnimation(name: string) {
const upDownAnim = new Animation(name, "position.y", 30, Animation.ANIMATIONTYPE_FLOAT, Animation.ANIMATIONLOOPMODE_CYCLE);
const keyFrames = [];
keyFrames.push({ frame: 0, value: this.mesh.position.y });
keyFrames.push({ frame: 15, value: this.mesh.position.y + 0.5 });
keyFrames.push({ frame: 30, value: this.mesh.position.y });
upDownAnim.setKeys(keyFrames);
return upDownAnim;
}
}
And in my inputControls.ts have added to play these animations when the keys are pressed (or idle when nothing is pressed)
public getInputVector(): Vector3 {
let inputVector = Vector3.Zero();
let moving = false;
// Ensure that the keyboard is connected
if (this.keyboard) {
if (this.keyboard.getInput(87) || this.keyboard.getInput(38)) { // W
inputVector.addInPlace(Vector3.Forward());
moving = true;
this.currentAnimation = "forward";
}
if (this.keyboard.getInput(83) || this.keyboard.getInput(40)) { // S
inputVector.addInPlace(Vector3.Backward());
moving = true;
this.currentAnimation = "backward"; // Assuming you have a backward animation
}
if (this.keyboard.getInput(65) || this.keyboard.getInput(37)) { // A
inputVector.addInPlace(Vector3.Left());
moving = true;
this.currentAnimation = "left";
}
if (this.keyboard.getInput(68) || this.keyboard.getInput(39)) { // D
inputVector.addInPlace(Vector3.Right());
moving = true;
this.currentAnimation = "right";
}
}
// If no movement keys are pressed, revert to idle animation
if (!moving) {
this.currentAnimation = "idle";
}
return inputVector.normalize();
}
Then I try to call this in my Player Class update method:
public update(animationRatio: number) {
const currentTime = performance.now();
const inputVector = this.inputControls.getInputVector();
const isSprinting = this.inputControls.isSprinting();
// Check the current animation state and play the corresponding animation
// Inside your player class's update method
switch (this.inputControls.currentAnimation) {
case "forward":
this.playerAnimations.playForwardAnimation();
break;
case "backward":
this.playerAnimations.playBackwardAnimation(); // Make sure you have implemented this method
break;
case "left":
this.playerAnimations.playLeftAnimation();
break;
case "right":
this.playerAnimations.playRightAnimation();
break;
case "idle":
default:
this.playerAnimations.playIdleAnimation();
break;
}
if (currentTime - this.lastLogTime > 300) {
console.log(`Animation Ratio: ${animationRatio}`);
this.lastLogTime = currentTime; // Update the last log time
}
I don’t get any errors in the Console or in my IDE, but the animations don’t play. I can see the animationGroups in the inspector, but no animations are assigned to the mesh when you check playerCapsule Node:
I hope this makes sense, I’ve created a repo with this example here:
It should just run after:
git clone Open Source / BabylonTemplate · GitLab
cd babylontest
npm install
npm run dev
Any help on this would be really appreciated! Thanks