Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Method at time 0 in animation gets called repeatedly if paused and resumed #85950

Closed
djrain opened this issue Dec 9, 2023 · 15 comments
Closed

Comments

@djrain
Copy link

djrain commented Dec 9, 2023

Tested versions

  • reproduced in 4.1.2, 4.1.3, 4.2, 4.2.1 RC1

System information

macOS Ventura

Issue description

I'm using AnimationPlayer for cutscenes, some of which have dialogue. I do this with a method track function that pauses the animation, waits for the dialogue to finish, and then resumes the animation:

func show_dialogue():
	$AnimationPlayer.pause()
	await dialogue.show()
	$AnimationPlayer.play()

Note that the play() function here is resuming the animation, not restarting it.
This is mostly working - the function gets called once, then the animation resumes. The one exception is when the method is called exactly at time 0 in the animation. In this case:

in 4.1.2 / 4.1.3, the method gets called twice, then animation finishes.
in 4.2 stable / 4.2.1 RC1, the method keeps getting called repeatedly, animation never finishes.

Steps to reproduce

run MRP scene, animation will autostart. observe "calling the method" printed two or infinite times

Minimal reproduction project (MRP)

AnimationPlayerIssue.zip

@TokageItLab
Copy link
Member

It is intended behavior that this triggers an infinite loop as we can see it in the code. The fact that it was only processed twice before 4.1 is in some ways not the correct behavior, but it may be due to differences in the timing of the application of delta between 4.1 and 4.2.

@djrain
Copy link
Author

djrain commented Dec 9, 2023

Sorry don't understand, how is this intended behavior? There should be no infinite loop from the code here, I expect the call method to only be triggered once, as I'm not rewinding or restarting the animation. And furthermore, it's an inconsistency that this only happens if the method is at time zero.

@AThousandShips
Copy link
Member

Does pausing work differently on the first frame? Does it normally play the next frame after calling play? Otherwise this should happen where ever you are, as the method should be called on the frame

@TokageItLab
Copy link
Member

TokageItLab commented Dec 9, 2023

I expect the call method to only be triggered once, as I'm not rewinding or restarting the animation.

No, your method call play(), it means restart. The code is apparently calling stop and play repeatedly on the same time.

@djrain
Copy link
Author

djrain commented Dec 9, 2023

play() with no arguments resumes the animation from where it was, according to docs. The code is sound

@TokageItLab
Copy link
Member

TokageItLab commented Dec 9, 2023

Since there is a method key in the play() position, it is fired, the method is called again, and the frame is processed indefinitely.

I don't know what you are trying to do, but a straightforward way would be to return the method early by flagging it.

@djrain
Copy link
Author

djrain commented Dec 9, 2023

Since there is a method key in the play() position, it is fired, the method is called again

If that's by design, then there are two problems:

  • The behavior is unintuitive. As a user I expect an animation to call all my methods exactly once, provided I don't restart or rewind it. AnimationPlayer should internally keep track of what methods have been called already, if needed. I shouldn't have to do that.
  • The behavior is inconsistent. At time 0, the method repeats infinitely, anywhere else in the animation it gets called once. Why the difference?

@TokageItLab
Copy link
Member

The behavior is unintuitive. As a user I expect an animation to call all my methods exactly once, provided I don't restart or rewind it. AnimationPlayer should internally keep track of what methods have been called already, if needed. I shouldn't have to do that.

No. If there is a key there at playback time, the method must be played. Your mistake is that you are doing stop and play in the same method.

The behavior is inconsistent. At time 0, the method repeats infinitely, anywhere else in the animation it gets called once. Why the difference?

Your method triggers an infinite loop even if time is not 0.

@djrain
Copy link
Author

djrain commented Dec 9, 2023

If there is a key there at playback time, the method must be played.

According to what? The design? The AnimationPlayer code? Not sure what you mean exactly.

Your method triggers an infinite loop even if time is not 0.

It doesn't for me, did you run the project? If you can't reproduce on your end, perhaps it depends on the machine.

@TokageItLab
Copy link
Member

TokageItLab commented Dec 9, 2023

According to what? The design? The AnimationPlayer code? Not sure what you mean exactly.

It is an affordance rather than a design; there are tons of use cases that put method keys on 0 of the timeline, which need to be fired by play().

I have seen several use cases where one can move to another animation in a method key or seek to another time in the same animation, such as in an A-B loop. However, play() of the same animation at the same current time, as you do, is an abnormal use case, as it obviously leads to an infinite loop.

It doesn't for me, did you run the project? If you can't reproduce on your end, perhaps it depends on the machine.

I do not know why you cannot reproduce, I think everyone can reproduce it with the MRP. Maybe you are using a different project.

@djrain
Copy link
Author

djrain commented Dec 10, 2023

It is an affordance rather than a design; there are tons of use cases that put method keys on 0 of the timeline, which need to be fired by play().

Sure, and that can be accomplished with play("name") to restart completely, or using seek() if you need to jump around. It makes sense that those would call the methods again. But simply resuming an animation where it left off should absolutely not call any methods again, this is completely unintuitive IMO.

However, play() of the same animation at the same current time, as you do, is an abnormal use case, as it obviously leads to an infinite loop.

Well, I'd say it wasn't at all obvious, or I wouldn't have opened this issue :) haha.

I do not know why you cannot reproduce, I think everyone can reproduce it with the MRP

Huh, interesting. I'm using the same MRP, in Godot 4.2

2023-12-09.17-06-42.mov

@TokageItLab
Copy link
Member

Huh, interesting. I'm using the same MRP, in Godot 4.2

4.2 has a bug where the key retrieval is not accurate, so 4.2.1 must be used.

@TokageItLab
Copy link
Member

TokageItLab commented Dec 10, 2023

Well, I'd say it wasn't at all obvious, or I wouldn't have opened this issue

It is obvious from the programmer's point of view that when there is a key at the 1s point, when you stop at the 1-second point and play at the 1s point, it is natural to continue processing the same key. As commented above, straightforward workaround is add a flag to avoid infinity loop.

In conclusion, this is the intended behavior.

If I had to say so, it is a documentation issue. However, in case such a use case is absolutely necessary, you can send a proposal for an option that play() does not process the current position key, like the update_only argument of seek(), although I think is is not necessary into core as it can be handled with a few lines of flags.

@TokageItLab TokageItLab closed this as not planned Won't fix, can't repro, duplicate, stale Dec 10, 2023
@djrain
Copy link
Author

djrain commented Dec 10, 2023

I'll be making a proposal for sure, thanks for the info!

@TokageItLab
Copy link
Member

TokageItLab commented Dec 16, 2023

#86227 should solve this problem as far as the key position is not 0 and "while playing"; The pause() actually stops the animation a little after the current position of the key, so for example, if the pause() method key exists at 1.0, the animation will stop at 1.0001 (although it is odd a bit).

The reason this problem occurs especially at 0 is that the animation's playback position is not moving immediately after start. Therefore, the following code can be used to avoid the problem.

var current_delta = 0

func _process(delta):
	current_delta = delta

func test():
	$AnimationPlayer.pause()
	prints("calling the method")
	$AnimationPlayer.advance(current_delta)
	$AnimationPlayer.play()

Also, if you put the method key at 0.5 and run the following code, you will have the same problem as when the key position is 0 as consistent behavior.

extends Node2D

func _ready():
	$AnimationPlayer.seek(0.5, true)
	$AnimationPlayer.pause()

func test():
	$AnimationPlayer.pause()
	prints("calling the method")
	$AnimationPlayer.play()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants