I’m trying to setup a simple controller that using two keys - will play an animation either forwards or backwards. All using Anim component.
Can’t get my head around it to work in good way. My initial tests has been with a simple 1 state anim state graph and then controller code that will set the speed to either 1, -1 or 0 depending on a keypress. Like so:
code: this.animComponent.speed = 1 // Set forward speed
Is this a good practice?
It kind of works, but I run into some issues:
If I press A key to forward the animation all the way to it’s end. I can no longer run it backwards from there. It stops.
If I run it backwards, I can see the activeStateCurrentTime goes below zero. And if I want to forwards from there I need to first wind it up to 0 again before it starts moving. Does it need to be clamped manuallu to the range of the duration?
Alright. There are a couple of forum topics about playing animation in reverse. At least the topic below looks related. It seems the GitHub issue about it has still an open status.
Thanks, I’ve been experiencing with fractional values a bit from the end and start, to try to cap it but so far (using 1 - 0.01 and 1 - 0.05) it just ended up snapping to 1 anyway. maybe AnimationComponent will behave differently.
Seems my monitoring might have been what was wrongly implemented. I can confirm that if I monitor in update loop, I get it to workaround the bug where the animation stops and become unplayable if it ever reaches 100%.
// Update method to monitor animation progress
AnimationController.prototype.update = function (dt) {
if (this.animComponent && this.animComponent.baseLayer) {
const currentTime = this.animComponent.baseLayer.activeStateCurrentTime;
const duration = this.animComponent.baseLayer.activeStateDuration;
// Prevent the animation from reaching 100%
if (currentTime / duration > 0.99 && this.animComponent.speed > 0) {
this.animComponent.speed = -this.speed; // Reverse the animation
console.log(`Reversing animation to prevent reaching 100%`);
} else if (currentTime / duration < 0.01 && this.animComponent.speed < 0) {
this.animComponent.speed = this.speed; // Play forward again
console.log(`Playing animation forward to prevent freezing`);
}
}
};
I don’t know, I spoke too soon I think., Still fighting with this silly issue.
Seems that this.animComponent.baseLayer.activeStateCurrentTime report NaN in certain cases. Anim system is probably very competent and flexible but when I’ve been fighting with it for two straight days just to play or reverse an animation with code I feel it’s convoluted. Going to try the legacy system and see how it behaves.
Thanks for chirping in @yaustar, really appreciate it!
What I’m trying to do is to set up an interactive robotarm controller for this asset. I tried with physics package and motors/constraints but it was a trip down crazy lane. Found a much simpler approach just rotating the parts of the hierarchy and it’s very stable.
For this final joint/gripper however - the movement is so complex it have been rigged and animated in blender - and my simple goal is to play that animation forwards when a certain key is pressed, and backwards when another key is pressed.
It feels like such a simple thought so it’s very frustrating I can’t figure out how to do it.
I just tried the transition code you linked to in an old thread, but that one just plays a certain animation in a state. Not sure it solved my issue alltogether, does it?
If all you need to play is a single animation, what @yaustar is suggesting is a good option. Simply play the animation from script, avoid the state graph completely.
If there is problem with playing it backwards, you could consider exporting another animation that is time-reversed, and playing this from code as needed as well.
I’m now experimenting with the activeStateProgress value, but can’t really understand it. Sometimes it returns negative values, is that expected?
I’ve established two states in the state graph just like you and used the same animation but set the speed to -1 for the backwards state. Perhaps that’s what’s messing up the progress, have no clue.
Was going to try the activeStateCurrentTime instead, but that one also report negative time values in certain cases.
So this is my latest setup, just to tie it all together. This is now working quite good, and what I had to do in the end was to save my own “Current Progress” using the absolute value. I have some oddities when it comes to boundary conditions left to solve.
Yes, why not. Seems much more straightforward here. Thanks @yaustar, now things work just like I intended. Here is the final code I used, most importantly, updating and clamping activeStateCurrentTime manually:
// Update the current time and clamp it within the animation duration
this.currentTime += dt * speed;
this.currentTime = pc.math.clamp(this.currentTime, 0, this.entity.anim.baseLayer.activeStateDuration);
this.entity.anim.baseLayer.activeStateCurrentTime = this.currentTime;
// initialize code called once per entity
AnimationControllerTransition.prototype.initialize = function () {
if (!this.entity.anim || !this.entity.anim.baseLayer) {
console.error('Animation component or base layer is missing.');
return;
}
this.entity.anim.speed = 0;
this.currentTime = 0;
};
// update code called every frame
AnimationControllerTransition.prototype.update = function (dt) {
if (this.app.keyboard.isPressed(this.forwardKey)) {
this.playAnimation(dt, this.speedFactor, 'forwards');
}
if (this.app.keyboard.isPressed(this.backwardKey)) {
this.playAnimation(dt, -this.speedFactor, 'backwards');
}
if (this.app.keyboard.wasReleased(this.forwardKey) || this.app.keyboard.wasReleased(this.backwardKey)) {
this.pauseAnimation();
}
};
// Play the animation in the specified direction
AnimationControllerTransition.prototype.playAnimation = function (dt, speed, direction) {
if (!this.isPlaying) {
this.isPlaying = true;
console.log(`Playing ${direction} from ${this.currentTime.toFixed(2)}`);
}
// Update the current time and clamp it within the animation duration
this.currentTime += dt * speed;
this.currentTime = pc.math.clamp(this.currentTime, 0, this.entity.anim.baseLayer.activeStateDuration);
this.entity.anim.baseLayer.activeStateCurrentTime = this.currentTime;
};
AnimationControllerTransition.prototype.pauseAnimation = function () {
this.isPlaying = false;
console.log(`Paused at ${this.currentTime.toFixed(2)} s`);
};