Spine Skin Mix and Match, aka Combining Skin or Avatar system

Hi.

I want to mix and match skins with Spine Animation file.(also known as combining skins in runtime, or commonly, Avatar system)

Cocos2d-x version: 3.16
Spine version: 3.6.52 Essential

It is well known that Spine editor’s Skin function is to switch all parts in whole, not to mix and match each parts, combining multiple skins. But some documents mentioned that it is possible in runtime.

This is the page where I get the ideas, written by Unity C#

https://github.com/pharan/spine-unity-docs/blob/master/Mix-and-Match.md

with this info, I want to re-write the code with Cocos2d-x.

first I prepared the data.

I modified “goblns.spine” in the Spine samples like below:

  • removed all the skin placeholders except each hands (2 skin place holders for lower layer and upper layer of the right hand and 1 for left hand)
  • left hand has two skin attachment: dagger and spear
  • right hand has two skin attachment: dagger and shield (top)
  • Skins has only one skin: template

then the code:
SceneGame.h and .cpp is the typical HelloWorld scene with some menuItems.

.h

class SceneGame : public cocos2d::Layer
{
	...

	spine::SkeletonAnimation *mSpine;

}

.cpp

bool SceneGame::init()
{
...

mSpine = spine::SkeletonAnimation::createWithJsonFile("goblins.json", "goblins.atlas");
mSpine->setSkin("template");
mSpine->setPosition(Vec2(320, 100));
mSpine->setAnimation(0, "walk", true);
this->addChild(mSpine);

...
//	created some menuitem buttons
}

and this is the actual skin changing code. (callback for button)
It’s actually a re-writing of the unity C# code in the link above.

void SceneGame::Callback_Button(Ref *pSender)
{
spSkeleton *tSkel = mSpine->getSkeleton();

int lhandSlotIndex = spSkeleton_findSlotIndex(tSkel, "left hand item");
int rhandSlotIndex = spSkeleton_findSlotIndex(tSkel, "right hand item");

spAttachment *lDaggerAttachment = spSkeleton_getAttachmentForSlotIndex(tSkel, lhandSlotIndex, "left hand dagger");
spAttachment *rDaggerAttachment = spSkeleton_getAttachmentForSlotIndex(tSkel, rhandSlotIndex, "right hand dagger");

spSkin *newSkin = spSkin_create("newskin");
spSkin_addAttachment(newSkin, lhandSlotIndex, "lf hand dagger", lDaggerAttachment);
spSkin_addAttachment(newSkin, rhandSlotIndex, "rt hand dagger", rDaggerAttachment);

spSkeleton_setSkin(tSkel, newSkin);
spSkeleton_setSlotsToSetupPose(tSkel);
mSpine->update(0);
}

but when I run the scene and pressed the button, this is what I get:
BEFORE:


AFTER:

Fortunately it did not crashed but the hands, where all weapons were supposed to be changed to daggers, were empty.

I am in my witt’s end. Can anyone please help me?

I’m guessing you want to create new skins… but why not just change the attachments you need on runtime?

@bilalmirza
Good idea! I was able to change the weapon to daggers by changing the Callback_Button like this:

void SceneGame::Callback_Button(Ref *pSender)
{
	spSkeleton *tSkel = mSpine->getSkeleton();
	
	int lhandSlotIndex = spSkeleton_findSlotIndex(tSkel, "left hand item");
	int rhandSlotIndex = spSkeleton_findSlotIndex(tSkel, "right hand item");
	
	spSkeleton_setAttachment(tSkel, "left hand item", "left hand dagger");
	spSkeleton_setAttachment(tSkel, "right hand item", "right hand dagger");		
}

(with the exception of the shield item, which I forgot to hide :wink: )

Strange thing is, after calling this routine, mSpine->setSkin(“template”); does not work anymore…

Actually I wanted to implement combining skins with the “official” way of Estoric Software (creator of the Spine tool) but hey, if it’s working, all is well!

i do something like this. When initiating the node.

 if(FileHandler::getInstance()->_frontGunStr == kLaser) {
        spSkeleton_setAttachment(node->getSkeleton(), "laserGun", "laserGun");
        
    }