Official Spine runtime extension - BETA testing

Another thing found.
If we have set the skin in editor here:


it has no effect on runtime. It will show the default skin.

and switches the skin after code only

spine.set_skin("#spinemodel", "skin1")

In old Spine extension editor’ property ‘skin’ had effect on runtime too.

1 Like

@dragosha If you could try this fix for the setting of the skin?

1 Like

@Mathias_Westerdahl yes, this fix works correct:

5 Likes

Thanks, the version 2.0.6 is now released!

6 Likes

A small test of the spine.set_attachment function.

Short guide

In the old runtime, we combined skins in the right slots to dress the doll character in different clothing/stuff combinations.

Was:

spine.set_skin(model, skin, slot)

Became:
Now we set one base skin for the model and then turn on the atachments for the desired slots.

spine.set_skin(model, skin)
spine.set_attachment(model, slot, attachment)

Where the attachment is a name of picture or mesh within the Spine slot:

Only one skin can be visible at a time. And only one atachment can be visible inside a slot at a time.
So, for correct works with the new extension we need to prepare the Spine model: move attachments from skins to slots.

From video example above:

spine.set_skin("#spinemodel", "skin1")
spine.set_attachment("#spinemodel", "weapon", "fox_weapon1")
spine.set_attachment("#spinemodel", "head", "head2")
spine.set_attachment("#spinemodel", "face", "pd_face")

If we want to turn off all attachments in a slot, then we simply pass “” as attachment value:

spine.set_attachment("#spinemodel", "weapon", "")

It’s a way what we have right now.


Also the Spine software has other way to combine skins and attachments. It seems more flexible and capable of showing combined skins in the Spine Editor. (see Skins view - Spine User Guide)

The main idea is to create a new skin in runtime and attach other skins from the skeleton to it. These skins can include multiple attachments and even new combinations of bones for easier work in the animation editor.

Skin newSkin = new Skin("new-skin"); // 1. Create a new empty skin
newSkin.addSkin(skeletonData.findSkin("shirt/pink"); // 2. Add items
newSkin.addSkin(skeletonData.findSkin("pants/green");
newSkin.addSkin(skeletonData.findSkin("shoes/sneakers");

Description here:
http://esotericsoftware.com/spine-runtime-skins#Grouping-attachments

For do that in the new runtime we need to get access to some lua functions and possible some object (Skin)? Possible it may be a feature request for NE.

Please correct me if I’m wrong somewhere. I’m just diving into the new version of Spine.

7 Likes

Correct, we changed the api to better follow the api of the Spine runtime.

Yes, please add feature requests to the Spine extension.
Hopefully now, as an extension, our community can help update and shape the future for this extension!

7 Likes

Mathias is making a very important point here. The maintenance burden of the integrated Spine support in Defold has been huge and it was the source of a lot of problems.

With the Spine support moved to an extension which uses the official runtime we hope to see community contributions to make this extension better. It will also free up time for us to work on other things in the engine and in extension. I am personally really excited to see an upgrade of the Rive extension once it supports mesh deformation. This will make Rive a very interesting competitor to Spine.

10 Likes

Seems there is a some different with animation mixing compared to the old version.
Left animation with blend_duration = 0.25, right without any blend setting. The same model and animation sequence.

function init(self)
	spine.set_skin("#spinemodel", "skin1")
	spine.set_attachment("#spinemodel", "weapon", "fox_sword2")

	local a = {"slash", "moon", "dodge", "run", "run", "powerslash", "idle"}
	local i = 1 
	local prop = {blend_duration  = 0.25, playback_rate = 1 }
	local function boo(self, message_id, message, sender)
		if message_id == hash("spine_animation_done") then
			local anim = a[i]
			i = i + 1
			if i > #a then i = 1 end
			print("Done!, new:", anim)
			
			spine.play_anim("#spinemodel", anim, go.PLAYBACK_ONCE_FORWARD, prop, boo)
		elseif message_id == hash("spine_event") then
			print("spine event", message.event_id)
		end
	end

	spine.play_anim("#spinemodel", "idle", go.PLAYBACK_ONCE_FORWARD, prop, boo)
end

Also, I noticed that Spine events now only come to the callback, no longer come to the on_message script method, it seems this should be highlighted in the migration guide.

7 Likes

Hmm, that’s strange. I don’t think we do anything special, I’ll look into it ofc.

The migration guide covers the callback behavior change:
“* If a callback is set to spine.play_anim() it will now receive all spine events (e.g. foot steps etc)”

Update:
This is one of the major differences between how our previous internal rig animation engine works and how Spine official runtime works.
You assign the animation to a “track” and as such, you can play multiple animations at the same time.

An example is in the example project, where we blend 3 animations (idle, aim and shoot).

It may very well be that we want helper functions to do what you want.
In this particular case I’m not sure about what the best practice is.
E.g. what official runtime calls needs to be made and which functions need to be exposed to Lua.
We (the community) need to figure this out together.

They have some technical documentation here:
http://esotericsoftware.com/spine-c#Tracks-%26amp%3B-Queueing

I’m also looking into why our documentation for the extension hasn’t updated properly. (e.g. listing the new track option)

Looking at the documentation, it seems like it should have worked:
http://esotericsoftware.com/spine-applying-animations#Playback

4 Likes

Continuing to migrate the project to 1.3 and encountered that in some cases the hero animation ends with a message

WARNING:GAMEOBJECT: Failed to call message response callback function, has it been deleted?

after that script loses control and the hero is stuck. The callback function is just local script method setted in spine.play_anim. :thinking:

1 Like

I seem to have figured out the error in the blending between animations. Issue was in the skeleton setup of my animation. For example a full turn of the hand bone in some animations, this is not visible in the editor Spine until you look at the numbers in the frames, and the old runtime “forgave” such errors. So, revised animation with blend_duration = 0.25 looks correct:


except for one thing: for some reason the runtime ignores stepped curve between frames. You can see it as a reverse character somersault. In this case have to switch the bone rotation from -360 degrees to 0 without interpolation. In the Spine editor it looks correct:

But runtime ignore it, and make intermediate frames.

1 Like

Was it by any chance deleted?

(I’m trying to figure out a repro case)

I’m trying to figure out at what point this happens. But since the “freezes” the main character, which does not disappear from the screen never, I think that the callback itself can not be deleted. I will try to localize the problem on a level with only one Spine model.

1 Like

Yes, I think we need a small repro for this, as I’m not sure how to do it.

As for callbacks to a deleted script, it may very well be that I’ve fixed such an issue while implementing this.

1 Like

Not ready yet. But i found another strange case.

In this case the timer will run every second and print in console timer cb

	spine.play_anim("#spinemodel", "animation", go.PLAYBACK_LOOP_FORWARD, {})
	timer.delay(1, true, function()
		print("timer cb")
		spine.play_anim("#spinemodel", "animation", go.PLAYBACK_LOOP_FORWARD, {})
	end)

But, if we add callback function into first spine.play_anim(), timer works only once! :thinking:

    spine.play_anim("#spinemodel", "animation", go.PLAYBACK_LOOP_FORWARD, {}, function() print("spine cb") end)
	timer.delay(1, true, function()
		print("timer cb")
		spine.play_anim("#spinemodel", "animation", go.PLAYBACK_LOOP_FORWARD, {})
	end)

upd: quick repro:
coin.zip (288.7 KB)

3 Likes

I seem to be able to reproduce the issue with 100% repetition. A bit of an odd example, but anyway.
Repro:
cbtest.zip (288.2 KB)

code:

local function cb(self, message_id, message, sender) 
	print("spine cb", message_id, cb)
	timer.delay(0.1, false, function()
		print("timer cb")
		spine.play_anim("#spinemodel", "animation", go.PLAYBACK_ONCE_FORWARD, {}, cb)
	end)
end
function init(self)
	spine.play_anim("#spinemodel", "animation", go.PLAYBACK_ONCE_FORWARD, {}, cb )
	timer.delay(1, false, function()
		print("timer 2 cb")
		spine.play_anim("#spinemodel", "animation", go.PLAYBACK_ONCE_FORWARD, {}, 
		function(self, message_id, message, sender)
			print("spine cb 2", sender)
			cb(self, message_id, message, sender) 
		end)
	end)
end

console:

DEBUG:SCRIPT: timer 2 cb
DEBUG:SCRIPT: spine cb 2	url: [main:/go#spinemodel]
DEBUG:SCRIPT: spine cb	hash: [spine_animation_done]	function: 0x02029c86d0d0
DEBUG:SCRIPT: timer cb
WARNING:GAMEOBJECT: Failed to call message response callback function, has it been deleted?
4 Likes

I have a fix for the callbacks, and PR pending review. SHould be able to make a new release today.

4 Likes

I tried to bundle using the new extension in a Github Actions Linux runner and I got this: https://github.com/defold/extension-spine/issues/71

2 Likes

I encountered new error while bundling for Windows in Debug mode:

/defold-spine/plugins/lib/x86_64-win32/libSpineExt.dll
	D:\Defold\Zoo-Economy\build\plugins\defold-spine\plugins\lib\x86_64-win32\libSpineExt.dll (The process cannot access the file because the file is used by another process.)

What can it be?

Try restarting the editor. Are you using the latest version of the extension?