godotengine / godot-docs

Godot Engine official documentation
https://docs.godotengine.org
Other
3.94k stars 3.22k forks source link

AnimatedSprite2D.play() behavior change between Godot 3 and Godot 4 should be documented #8372

Open fuzhouch opened 1 year ago

fuzhouch commented 1 year ago

Godot version

4.1.2.stable.flathub [399c9dc39]

System information

Godot v4.1.2.stable (399c9dc39) - Freedesktop SDK 23.08 (Flatpak runtime) - Wayland - Vulkan (Compatibility) - Mesa Intel(R) Xe Graphics (TGL GT2) () - 11th Gen Intel(R) Core(TM) i5-11320H @ 3.20GHz (8 Threads)

Issue description

I find a behavior difference between Godot 3 and Godot 4, that when an AnimatedSprite2D node plays a non-looping animation, it actually becomes looping when .play() API is called within _process() or _physics_process().

func _process(delta) -> void: # Same behavior on _physics_process() as well
    $AnimatedSprite.play("collected") # Animation "collected" is marked as non-looping in Editor 

This behavior can be workarounded by moving the .play() call out from _process(), but put it to _ready(). Example is in demo_animated_sprite_2d_non_loop_good.tscn.

However, the code above is different with Godot 3. When running the code above in Godot 3, the sprite animation plays once, and stops at last frame. There's no looping happening, matching the settings in SpriteFrames.

The same behavior is observed on both Linux and macOS.

Additional Context

The issue was discovered from a migration of an action game project from Godot 3.5.3 to Godot 4.1.2. In original version, a lot of my code rely on behavior above, to implement a pattern of separating status change to different signal handlers (simplified code sample is shown below). Due to the change in Godot 4, a lot of code needs to be changed, because many non-looping actions (e.g. throwing fireballs) now play in a repeated manner.

I tried to find from Godot engine Q&A and Stackoverflow but not lucky enough to see a thread talking about this.

I'm not blocked because the workaround is available, but I would like to understand why the behavior between Godot 3 and 4 is inconsistent. Is it an intended behavior change in Godot 4?

var action_status = "animation_name"
func _physics_process(delta):
    $AnimatedSprite.play(action_status)

func _signal_handler_being_hit():
    action_status = "hit"

func _signal_handler_being_bomb_blown_off():
    action_status = "fly_hight"

Steps to reproduce

  1. Checkout project https://github.com/fuzhouch/godot-3to4-comparison/ to local, say ./godot-3to4-comparison.
  2. Launch Godot 4.1.2. Open godot 4 project, ./godot-3to4-comparison/godot4/project.godot
  3. From Godot 4 editor, Launch scene, demo_animation_sprite_2d_non_loop_bad.tscn

NOTE: The project is kinds of as minimal as possible. It contains multiple scenes, while each of which are presenting a single bug. Every scene is self-contained, and do not depends on each other.

We can see the gold coin keep flashing on launch.

As a comparison, please

  1. Launch Godot 3.5.3, then open Godot 3 project, ./godot-3to4-comparison/godot3/project.godot
  2. From Godot 3 editor, Launch scene, Demo_AnimatedSprite2D_NonLoop.tscn

The gold coin flashes only once and stop at last frame, a mini-coin.

Minimal reproduction project

Project repository: https://github.com/fuzhouch/godot-3to4-comparison/ Godot 4 project path (for reproducing issue): godot4/ Godot 3 project path (for behavior comparison): godot3/

AThousandShips commented 1 year ago

This is expected, play plays the animation, not setting what animation should be played, so when the animation is finished it starts over

fuzhouch commented 1 year ago

@AThousandShips thank you man! So is it a designed behavior that we should expect Godot 3 and 4 behaves differently?

AThousandShips commented 1 year ago

I'm not sure how it works in 3.x, but it's expected in 4.x, see the tutorial relying on this behaviour

fuzhouch commented 1 year ago

I have attached an exmaple in Godot 3 (see reproduce steps above). In Godot 3 it does not loop.

AThousandShips commented 1 year ago

The difference between 3.x and 4.x is by design I suspect, but this is at least not an oversight in 4.x, the code specifically checks if the animation is at the very end and then plays it from the start again

Changes between major versions are to be expected, and worked with

fuzhouch commented 1 year ago

Hey @AThousandShips , I read the tutorial (both version 4 and version 3. It seems both tutorial use a setting of setting Loop = On, thus they are different case than mine. My case explicitly set Loop = off.

You may see screenshot below.

I'm fine that Godot 3 and 4 can have behavior changes, and yes, it this by design, it will be of great help to explicitly document it. But I would like to confirm whether this is an intended change, not suspecting it.

Tutorial 3.5, https://docs.godotengine.org/en/3.5/tutorials/2d/2d_sprite_animation.html#controlling-the-animation image

Tutorial 4, https://docs.godotengine.org/en/stable/tutorials/2d/2d_sprite_animation.html#controlling-the-animation image

AThousandShips commented 1 year ago

It doesn't explicitly mention looping in the tutorial, it can only be seen from the screenshot

But from the code it is clear this behaviour is intended, not an oversight, what I mean by suspecting the change is intentional is that the fact this wasn't the case in 3.x might not be intended, but the current behaviour in 4.x is by design

A note somewhere about this difference from 3.x could be added, or a note about how it will restart the animation if it's at the end

But in all it's not a bug

fuzhouch commented 1 year ago

OK no problem. Once we have somewhere in documentation to tell the difference, then it's good enough.

KoBeWi commented 1 year ago

AnimatedSprite in Godot 4 is more similar to AnimationPlayer, as evident by the upgraded editor and animation autoplay property.

timothyqiu commented 1 year ago

This is not about looping. The looping behavior you observed is mainly caused by looping nature of _process().

The difference is actually about a change of behavior when calling play() for an already stopped animation.

This behavior change is by design and was introduced in godotengine/godot#71907. The corresponding proposal is https://github.com/godotengine/godot-proposals/issues/6043.

fuzhouch commented 1 year ago

Thank you Haoyu! @timothyqiu So this is really a designed behavior. And we can safely treat it as a documentation improvement (The official documentation does not mention it).

timothyqiu commented 1 year ago

Ah yeah. There's a dedicated article about different behaviors between Godot 3 and Godot 4. This behavior change can be mentioned in it.

I'll transfer this issue to godot-docs.