How to keep animation at the last frame?

I made an animation of an NPC in my experience getting out of his bed, but when the animation finishes, he teleports right back into his bed. How can I make it stay at the last frame once it finishes? Thanks

4 Likes

You could try calling Animation:AdjustSpeed(0) right before it actually finishes to pause it.

1 Like

I would do something like this

local track = humanoid.Animator:LoadAnimation(animation)
track:Play()
task.wait(track.Length * 0.99)
track:AdjustSpeed(0)

I haven’t tested it but it should work

EDIT: this is the first post ive seen use “experience”

19 Likes

Well it’s because my game is more a real-life simulation instead of a game and as Roblox and Apple define it, a game goes on forever while an experience is only played once.

if it dont work try using task.wait(track.Length * 0.8) (it worked for me with 0.8)

7 Likes

Sorry for necro, couldn’t find anything anywhere that answered this question though.

If anyone else happens upon this thread, something I found that doesn’t use task.wait is:

animationTrack.Stopped:Connect(function()
	animationTrack:Play(0, 1, 0) --Will restart the track at the beginning, fadeIn 0 so it immediately has appropriate weight, speed 0, so that it pauses
	animationTrack.TimePosition = animationTrack.Length --Set to the last key in the sequence
end)

Edit - It’s come to my attention the reason that works is because it gets Stopped / restarted at track.Length every single frame. For one that doesn’t see below:

animationTrack.Stopped:Connect(function()
	animationTrack:Play(0, 1, 0) --Will restart the track at the beginning, fadeIn 0 so it immediately has appropriate weight, speed 0, so that it pauses
	animationTrack.TimePosition = animationTrack.Length - .000001 --Something small enough that it's not truly at the end, causing a reset
end)

Edit 2/3/4 The case of negative speed:

local speed = animationTrack.Speed
animationTrack.Stopped:Once(function()
	animationTrack:Play(0, 1, 0) --Will restart the track at the beginning, fadeIn 0 so it immediately has appropriate weight, speed 0, so that it pauses
	if speed > 0 then
		animationTrack.TimePosition = track.Length - 0.000001  --Something small enough that it's not truly at the end, causing the animation to Loop
	else
		animationTrack.TimePosition = 0 -- Leave at the beginning
	end
end)
30 Likes

I reply to this post, because it’s on the first result of Google, so I am treating this as a “stackoverflow” post now. Because I am sure that people scroll around here, and will eventually land to this post.

 

There’s an interesting story about this.

Basically, you need to be really really clever if you’re using .Stopped or anything.

If an Animation is stopped, you can’t play it. In order to freeze an animation like that, within the same AnimationTrack, it needs to be Playing

This means, it’s going to re-trigger .Stopped if you Stop the animation again, causing unwanted results. So you should use :Once before you use :Play(), to eliminate the connection. “Trigger Once”

Do not use .Ended for this, it triggers way too late.

 

I was testing around with this a lot. And I don’t like any of the workarounds for this, unfortunately.

You can get this to work with :Once() then check if :Once() should still trigger, by keeping track of your “animation” states e.g. I can assume someone using this for e.g. an Opening Animation that should stop on its “Open Position”.

But you also gotta handle the situation where opening should stop the animation, while it’s not finished.

 

Facts

I don’t like .Stopped. If you control animations by the server, things will trigger at the wrong time, due to replication.

This causes “position teleports suddenly” in your animation. .Stopped is great ONLY if the animation is played through a Client Script.

It’s not an issue with StreamingEnabled. But if it’s client-side, you need to keep track of the current poses, for any new client joining.

 

You need to know, that if an animation stops, it tries to go back to its original pose. This can be overwritten by having Animations playing layered in Animation Priorities. E.g. an Idle pose and an Action pose.

 

Hence why an more questionable and appropriate method for keeping an animation at last frame, is by using Idle Animation.

Idle Poses

This is not something new. Idle Poses are used by a lot of games, even Source Engine.
This goes back into old games. e.g. Source Engine games. In these games, Animations use an idle pose.

Basically, you create an Animation that has only one keyframe inside of it, which is the pose. The Animation Priority should be lower than any other layering animation, to prevent issues with “Weight”, hence why “Idle” Priority is good.

 

As I said, when an animation ends, it tries to go back to a position. This would be the default Motor6D Position, or the other playing animation tracks.

With Idle poses, you do not need to use .Stopped at all. You can just use :Play(). One just needs to be clever with swapping them out.

With Idle Poses, you can conveniently let the Server trigger :Play()

 

If you do decide to play this through the Server, there’s one thing you should do. Which is to pre-cache the animations. Using ContentProvider:PreloadAsync.

Doesn’t matter what you do, if you play Animations through the Server, it will cause a de-sync. The Client will first load the animation, the Server won’t wait. This is why .Stopped is terrible to trigger visual actions through the Server. (Seen from Architecture, Client should be visual only, but Roblox can be an exception due to the lack of features regarding replication)

Playing animations from the server will still have a drawback, if the Client “Alt Tabs”. I am not sure what happens, but there’s something special going on if Roblox is minimized.

 

Conclusion

.Stopped is fine on the Client. It’s NOT fine on the Server, but you can pre-load the animations on the Client and minimize issues. But keep in mind, if the Server triggers .Stopped it doesn’t mean that the Client sees their Animation visually as already stopped (while you are trying to freeze and modify the position of the animation).

Idle Poses seem to be fine on both Client and Server. You don’t need to use .Stopped (which resolves the Server triggering it too early on a Client), all you need to make sure of, is to pre-load the animations on the Client using ContentProvider.

 

Both solutions are really depending by case. It depends what you’re trying to do. And I’d highly re-evaluate all solutions, before trying one for something final.

 

Don’t forget, out-streamed Animators will memorize their last animaton pose. Shouldn’t be an issue for the client. Just make sure to load the right animation on new joined clients that haven’t initialized custom objects yet.

The Server does this automatically with its own implemented Replication…

 

Don’t forget, Animations that modify Bones don’t change physics collisions apparently. So, if you’re doing anything physics related, try to avoid Animations with Bone Meshes if possible.

You should avoid Animations on very simple things that you can do with Physics Instances. If you believe it’s way too complex, then try whatever works best for you.

 

Additionally, I’d like to mention @tasavix as once I’ve mentioned something about 3D Model Import and Animations. Eventually there’s something interesting into this which can be forward to the right Team.

 

If you do decide to make it server and client-side (for the visuals) based. You’d have to replicate the current animation and initialize your object with the Animation… which is complex because at the end you’re just remaking an Animation system…

But what you could do, is store a tick on when the animation was supposed to start, and then calculate the elapsed time, to sorta get the current position (for slow animations)

17 Likes