Godot just had a new release: https://godotengine.org/article/maintenance-release-godot-4-2-2-and-4-1-4/
*Meant Godot 4.2.2
Godot just had a new release: https://godotengine.org/article/maintenance-release-godot-4-2-2-and-4-1-4/
*Meant Godot 4.2.2
I just updated your GitHub workflow. You can check on the build status here:
EsotericSoftware/spine-runtimesactions/runs/8737255547
Once the build succeeded, you can download the binaries from https://esotericsoftware.com/spine-godot
Excited for this, we've got some Godot 4.2.1 problems that are supposed to be fixed in 4.2.2!
Unfortunately I'm currently getting an error on the "Godot 4.2.2 with C# support" > "Editor Windows" download link:
Does that mean there are build problems that we're waiting on?
FWIW, the link is working now for me!
I tried Godot 4.2.2, and it somehow broke my current Animation system. I'm not sure I can pinpoint the reason, but I wanted to bring this up. My player character stay at initial setup pose for about 1 second, until properly transitioning to idle. However, the stand, walk, run animations sometimes do not transition (based on the Blend position code).
Also, can you give an update on this issue yet? EsotericSoftware/spine-runtimes2460
I currently don't have a problem on my Godot 4.2.1 project, but I'm afraid having this many red errors will cause unexpected issues. It's also a major inconvenience to be unable to use Folders for the Animation Player.
@skarasuko did you convert your assets from Spine Editor version 4.1 to 4.2 as well? The new Godot builds are for Spine Editor/Runtimes version 4.2. Your old 4.1 skeletons will likely not work correctly if you imported from JSON, and will definitely not load at all if you imported from .skel.
Just based on your screenshot, it's impossible to say what's going on I'm afraid. It seems you are using Godot's animation trees, which might have changed between Godot 4.2.1 and 4.2.2 as well. On our end, the responsible AnimationState
class has not changed in a way that could cause this issue between Spine 4.2 and 4.1.
The folder issue is something I'm investigating now that we're done with the 4.2 release. I should have it resolved in the next few days.
Mario My Spine sprite is ver. 4.2. Like I said, Godot 4.2.1 (with Spine 4.2 support) plays my animations as expected, with the Animation Player + Animation Tree set up. Godot 4.2.2 breaks the Animation Tree behavior, such as keeping my sprite in setup pose for 3 seconds and being unresponsive when attempting to change animation (which is handled by Blend Position).
I handed this over to a friend, and they observed the exact same problem. Both of us still haven't figured out the cause.
Then this is likely a problem in Godot 4.2.2, not spine-godot 4.2 (or spine-cpp), as those have not changed in any meaningful way that could interfere animation player + animation tree.
If you can boil it down to a simple scene that reproduces the issue, I can take a look.
Mario I have e-mailed a replication project.
skarasuko Thank you for sending us the repro project!
I was able to confirm the state you mentioned, but I think this is a rather peculiar use case of Animation Player and am not sure what made you choose this method. Is there something AnimationState
does not support to accomplish what you want? The Animation Player is useful for creating cutscenes, but I think it is very cumbersome and tedious to use if you want to animate the player character based on player input.
You can find an example of a common implementation in "04-simple-input", which is included in the example project we provide. Sorry my answer is off about the change from 4.2.1 to 4.2.2, but the current approach does not seem like a very good one, so I would like to find your concerns and advise you to achieve what you want based on this example.
Misaki I actually tried to refactor the code to eliminate the Animation Player and Animation Tree, but I encountered a confusing problem with my sprite. It's broken in a different way, like not playing most animations at all.
func _physics_process(delta):
var animation_state = sprite.get_animation_state()
var direction = Input.get_axis("left", "right")
if direction > 0:
sprite.scale.x = 1
if velocity.x < 500:
animation_state.set_animation("walk", true, 10).set_time_scale(0.25 + velocity.x / 750)
elif velocity.x > 500:
animation_state.set_animation("run", true, 10).set_time_scale(0.25 + velocity.x / 750)
elif direction < 0:
sprite.scale.x = -1
if velocity.x > -500:
animation_state.set_animation("walk", true, 10).set_time_scale(0.25 + velocity.x / 750)
elif velocity.x < -500:
animation_state.set_animation("run", true, 10).set_time_scale(0.25 + velocity.x / 750)
elif velocity.x == 0:
animation_state.set_animation("idle", true, 10).set_time_scale(1.0)
if Input.is_action_pressed("confirm"):
velocity.x = SPEED * direction
else:
velocity.x = 500 * direction
move_and_slide()
The reason for this setup is so I get the animations play at an appropriate speed, which is controlled precisely by the joystick.
I also wanted to set the animation mix based on specific transitions, but SpineSprite Node's Animation Mixes list is very difficult to manage, as it cannot be re-organized.
skarasuko After a little investigating, I see that "set_animation" is effectively resetting the animation each time it's called, but I haven't figured out how to toggle it without repeatedly calling it in _physics_process.
skarasuko Thanks for show me the code!
It's broken in a different way, like not playing most animations at all.
The problem lies in the condition of your if statements. With that code, I think it would mean that set_animation
would be called every frame while accepting left or right input. This would cause the animation in the specified AnimationState track to be set over and over again, and only the pose at frame 0 would be repeated all the time. And as a result, your skeleton will look like it is not animated at all.
The code in our example project sets the animation as follows:
func _process(_delta):
if Input.is_action_just_pressed("ui_left"):
get_animation_state().set_animation("run", true, 0)
is_action_just_pressed
returns true when the user has started pressing the action event in the current frame or physics tick. So even if the player keeps pressing the "ui_left" button, setting the run animation will not be repeated.
There are several patterns for writing code to avoid setting the same animation repeatedly when the animation is already set. For example, it can be written as follows (This is not complete code, so please consider it a kind of pseudo code):
func _physics_process(delta: float) -> void:
# Set the current state of Player
set_player_state()
# Set an animation that match the current state of Player
set_skeleton_animation()
func set_player_state() -> void:
# If Player is on the floor and velocity is zero, set "current_player_state" to "idle"
if is_on_floor() and is_zero_approx(_velocity.x):
current_player_state = "idle"
# If Player is on the floor but velocity is not zero, set "current_player_state" to "run"
elif is_on_floor() and not is_zero_approx(_velocity.x):
current_player_state = "run"
func set_skeleton_animation() -> void:
# Get the current animation playing on track 0
current_animation = spine_sprite_anim_state.get_current(0).get_animation().get_name()
if current_player_state == current_animation:
pass
elif current_player_state == "run" and current_animation != "run":
spine_sprite_anim_state.set_animation("run", true, 0)
elif current_player_state == "idle" and current_animation != "idle":
spine_sprite_anim_state.set_animation("idle", true, 0)
This is an example of code that uses get_current(track_id).get_animation().get_name()
to check the name of the animation playing on a track, setting the appropriate animation if it does not match the player's state, and nothing if it does.
Review your code to ensure that animations are only set once, when needed.
Misaki Thank you for giving me ideas. I managed to work on my code, and I am hoping to rework everything from the old system.
extends CharacterBody2D
const SPEED = 700.0
const JUMP_VELOCITY = -400.0
# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
@onready var sprite = $SpineSprite
@onready var current_anim : String
@onready var next_anim : String
@onready var anim_mix : float = 0.2
@onready var animation_state = sprite.get_animation_state()
func _ready():
current_anim = "idle"
animation_state.set_animation(current_anim, true, 10)
func _process(_delta):
if current_anim != next_anim:
current_anim = next_anim
animation_state.set_animation(current_anim, true, 10).set_mix_duration(anim_mix)
func _physics_process(_delta):
var direction = Input.get_axis("left", "right")
if direction > 0:
sprite.scale.x = 1
animation_state.set_time_scale(0.25 + velocity.x / 750)
elif direction < 0:
sprite.scale.x = -1
animation_state.set_time_scale(0.25 + velocity.x / -750)
elif direction == 0:
animation_state.set_time_scale(1.0)
next_anim = "idle"
anim_mix = 0.2
if Input.is_action_pressed("left") and velocity.x >= -500 and velocity.x < 0:
next_anim = "walk"
if current_anim == "run":
anim_mix = 0.0
else:
anim_mix = 0.2
if Input.is_action_pressed("right") and velocity.x <= 500 and velocity.x > 0:
next_anim = "walk"
if current_anim == "run":
anim_mix = 0.0
else:
anim_mix = 0.2
if Input.is_action_pressed("confirm"):
velocity.x = SPEED * direction
if velocity.x > 500 or velocity.x < -500:
next_anim = "run"
anim_mix = 0.0
else:
velocity.x = 500 * direction
move_and_slide()
skarasuko However, I still have an issue with the Inspector window containing the Animation Mixes because I think it also tends to reset when making major changes to the Spine Sprite. I'd like to know how to store all of that data within a Script.
skarasuko Hmmm, I would like to reproduce the problem, but I am not sure how to do it. For example, if I set up the idle
to run
animation and the run
to idle
animation Animation Mixes, and then try to overwrite the SkeletonFileResource
with skeleton data that has the run
animation removed, the animation disappears from the list, but the mix settings themselves remain:
(The field left blank is where "run" was originally specified.)
Could you tell me what specific operation would cause the Animation Mixes to be reset?
Misaki Sorry, my recollection might not be reliable since I avoided the window for a long time. In any case, I'm discouraged from using it because it would not take into account of major changes to the animation names and the lack of organization features.
skarasuko I understand. In fact, in the Animation Mixes section, pressing the Reset button next to the section title will reset all settings without prompting. Pressing the Remove button does not prompt you either. So you might be inclined to reset them by accident. And you are right, there is no way to change the order of the mix settings. Therefore, it may be better to set the mix duration by code each time, as you are already doing.
If your code could be improved a bit, it might be easier to manage if you create a function to set the "anim_mix" and write the specific mix combination into it, and call the function before setting a new animation like the following:
func _process(_delta):
if current_anim != next_anim:
current_anim = next_anim
set_anim_mix()
animation_state.set_animation(current_anim, true, 10).set_mix_duration(anim_mix)
func set_anim_mix() -> void:
if current_anim == "run" and next_anim == "walk":
anim_mix = 0.0
else:
anim_mix = 0.2