EsotericSoftware / spine-runtimes

2D skeletal animation runtimes for Spine.
http://esotericsoftware.com/
Other
4.37k stars 2.9k forks source link

[godot] no support for world position/rotation physics #2573

Open yoont4 opened 2 months ago

yoont4 commented 2 months ago

As the title describes, setting the position/rotation of a SpineSprite doesn't cause any of the physics constraints to do anything. You have to do some direct transform modification which is very clunky and error prone:

var cog = get_skeleton().find_bone("cog")
var current_transform = cog.get_global_transform()
var pos_delta = (target_bone_position + bone_offset) - current_transform.get_origin()
var new_transform = current_transform.translated(pos_delta)
cog.set_global_transform(new_transform)

Whereas I'd much rather have it respond to physics changes from typical sprite-level interactions:

# Ideally it would respond to sprite/node-level changes
global_position = target_position
rotation = target_rotation

Would it be possible to have a toggle if necessary to enable world-space physics calculation, rather than requiring direct modification to the internal skeleton positions?

yoont4 commented 2 months ago

Also unrelated question, but would like some clarification before I open a separate ticket. Does the Spine runtime calculate physics on its own tick-rate? Because it looks perfect on a locked 60FPS project, but if I uncap it and run at 144FPS, even if I raise the godot physics tick rate to match, I still get that classic jittering that usually comes from differing tick-rates.

badlogic commented 2 months ago

To answer your second question, you can configure when SpineSprite ticks: https://esotericsoftware.com/spine-godot#Setting-the-Update-Mode

Regarding physics constraints, SpineSkeleton has two methods:

At the moment, you have to call those manually. I have to see if I can wire up global_position on SpineSprite to (optionally, via a flag) call physics_translate() internally.

yoont4 commented 2 months ago

Thanks for the reply, I did not know about physics_translate/rotate, those are very useful to know!

Unfortunately the update mode flag doesn't really effect it. I've tried every setting, including manual and calling it on each _process() call.

I've found an additional detail that helps repro it easily though. It seems that for any FPS above or below 60, there is a weird "jolt" that happens every 1/X seconds. So at 61FPS, it jolts every ~1 second, at 62FPS every ~0.5 seconds, and so on. Which makes sense why uncapped at 144FPS (my monitor refresh rate), it seems to jitter every frame.

It's very easy to visualize by calling physics_rotate(0, 0, 10) on a simple 2-bone physics constraint:

61 FPS:

https://github.com/EsotericSoftware/spine-runtimes/assets/7411742/6cc31719-4333-49e5-93ca-d6a6abddccb6

62 FPS:

https://github.com/EsotericSoftware/spine-runtimes/assets/7411742/869ee4cc-5bd9-4021-9d7a-2085eb496975

70 FPS:

https://github.com/EsotericSoftware/spine-runtimes/assets/7411742/5dd6de78-0bdf-43c5-b260-30d864920c25

At 60FPS, there's no jittering. This makes me think that the internal physics update logic is somehow desynced with the actual sprite mesh updating, since normal animations stay smooth regardless of FPS on process update mode.

NOTE: I am only applying the physics rotation to the skeleton so that it's easier to see. It still happens if I rotate the SpineSprite with it like I would in practice.

badlogic commented 3 days ago

Sorry, I just found time to get back to this. @yoont4 could you share the scene script and assets?

All timing related code is contained here: https://github.com/EsotericSoftware/spine-runtimes/blob/4.2/spine-godot/spine_godot/SpineSprite.cpp#L676

This method receivces it's delta time either by manually calling SpineSprite::update_skeleton(), or in SpineSprite::_notification(), which passes either the process delta time, or the physics process delta time, depending on the update mode.

There's nothing anywhere in the code that would make things only work at 60fps, so I'm a bit puzzled. I also can't reproduce the issue using the celeste example on a 120Hz screen.