- Editado
[tk2d] Creating skin programmatically
Hello!
I just got Spine last night and I think it's great. I do have a question regarding creating skins programmatically though.
I'm using 2d toolkit and I'm trying to create a skeleton with attachments that are a composite of sprites from different sprite collections. I don't even know if this is possible, but I thought I'd give it a go.
This is the code I use to create and set the custom skin...
Skin skin = new Skin("Custom");
SpriteCollectionAttachmentLoader spriteCollectionAttachmentLoader = new SpriteCollectionAttachmentLoader(spritecollectionData);
int slotIndexForHead = skeleton.FindSlotIndex("Head01");
skin.AddAttachment(slotIndexForHead, "Leg01", spriteCollectionAttachmentLoader.NewAttachment(skin, AttachmentType.region, "Leg01"));
skeleton.data.AddSkin(skin);
skeleton.skin = skin;
skeleton.SetSkin("Custom");
skeleton.SetToSetupPose();
...but it doesn't seem to do anything. The skin is added (I can select it in the SkeletonAnimation's "Initial skin"-combobox in Unity). But the attachment "Head01" still shows the original attachment. Not "Leg01".
I'm a Spine noob so I might've very well misunderstood how I'm supposed to create skins in code, so any help would be appreciated!
Regards/Per
That code looks ok. You only need SetSlotsToSetupPose, not SetToSetupPose, but that won't fix your issue. After SetSkin and SetSlotsToSetupPose, try calling skeleton.GetAttachment and verify the slot has the attachment you expect it to have. Also verify that the NewAttachment method is assigning the correct region to the attachment.
I am also having trouble with creating a new skin and getting it to appear... any help would be appreciated
Any luck with this Shaka?
This is the example code I'm using, basically I'm creating a clone of the current skin, then applying it. But my Spine character appears blank afterwards (all images dissapear when I set the skin and call SetSlotsToSetupPose)
The skin appears to by applied correctly afterwards, I can verify the attachments by calling skeleton.GetAttachment() so I think it may be something to do with SpriteCollectionAttachmentLoader not creating the NewAttachment correctly, but then why would this work differently from when it is used in Initialisation?
void cloneCurrentSkinAndApply(){
Spine.Skeleton skeleton = skelAnim.skeleton;
Spine.Skin currSkin = skeleton.Skin;
//create new skin to populate
Spine.Skin newSkin = new Spine.Skin("New Skin");
//create an attachment loader to use
SpriteCollectionAttachmentLoader spriteCollectionAttachmentLoader = new SpriteCollectionAttachmentLoader(skelAnim.skeletonDataAsset.spriteCollection);
//vars to use in loop
System.Collections.Generic.List<Spine.Slot> slots = skeleton.slots;
System.Collections.Generic.List<string> attachmentNames;
System.Collections.Generic.List<Spine.Attachment> slotAttachments;
// for each slot in the skin, get the attachment names, image names, make copies of attachments to the new skin
foreach(Spine.Slot slot in slots){
//get the slot index
int slotIndex = skeleton.FindSlotIndex(slot.data.name);
//get attachments from this slot
slotAttachments = new System.Collections.Generic.List<Spine.Attachment>();
currSkin.FindAttachmentsForSlot(slotIndex,slotAttachments);
//get the names
attachmentNames = new System.Collections.Generic.List<string>();
currSkin.FindNamesForSlot(slotIndex,attachmentNames);
//for each attachment in this slot, copy it to new skin
for(int i=0;i<slotAttachments.Count;i++){
Spine.Attachment att = slotAttachments[i];
string attName = attachmentNames[i];
string spriteImageName = att.Name;
Debug.Log("In Slot "+slotIndex+" '"+slot.data.name+"', add attachment '"+attName+"' source image name:'"+spriteImageName+"'");
Spine.Attachment newAtt = spriteCollectionAttachmentLoader.NewAttachment(newSkin, Spine.AttachmentType.region, spriteImageName);
newSkin.AddAttachment(slotIndex, attName, newAtt);
}
}
skeleton.data.AddSkin(newSkin);
skeleton.SetSkin(newSkin);
skeleton.SetSlotsToSetupPose();
}
Update... got it working
needed to set properties on the new RegionAttachment
code below again, copies current skin to new one and applies it.
void cloneCurrentSkinAndApply(){
Spine.Skeleton skeleton = skelAnim.skeleton;
Spine.Skin currSkin = skeleton.Skin;
//create new skin to populate
Spine.Skin newSkin = new Spine.Skin("New Skin");
//create an attachment loader to use
SpriteCollectionAttachmentLoader spriteCollectionAttachmentLoader = new SpriteCollectionAttachmentLoader(skelAnim.skeletonDataAsset.spriteCollection);
//vars to use in loop
System.Collections.Generic.List<Spine.Slot> slots = skeleton.slots;
System.Collections.Generic.List<string> attachmentNames;
System.Collections.Generic.List<Spine.Attachment> slotAttachments;
// for each slot in the skin, get the attachment names, image names, make copy of the attachement to the new skin
foreach(Spine.Slot slot in slots){
//get the slot index
int slotIndex = skeleton.FindSlotIndex(slot.data.name);
//get attachments from this slot
slotAttachments = new System.Collections.Generic.List<Spine.Attachment>();
currSkin.FindAttachmentsForSlot(slotIndex,slotAttachments);
//get the names
attachmentNames = new System.Collections.Generic.List<string>();
currSkin.FindNamesForSlot(slotIndex,attachmentNames);
//for each attachment in this slot, copy it to new skin
for(int i=0;i<slotAttachments.Count;i++){
Spine.Attachment att = slotAttachments[i];
string attName = attachmentNames[i];
string spriteImageName = att.Name;
Debug.Log("In Slot "+slotIndex+" '"+slot.data.name+"', add attachment '"+attName+"' source image name:'"+spriteImageName+"'");
Spine.Attachment newAtt = spriteCollectionAttachmentLoader.NewAttachment(newSkin, Spine.AttachmentType.region, spriteImageName);
Spine.RegionAttachment newRegAtt = newAtt as Spine.RegionAttachment;
Spine.RegionAttachment oldRegAtt = att as Spine.RegionAttachment;
//copy properties from old regionattachmet to new one
newRegAtt.x = oldRegAtt.x;
newRegAtt.y = oldRegAtt.y;
newRegAtt.scaleX = oldRegAtt.scaleX;
newRegAtt.scaleY = oldRegAtt.scaleY;
newRegAtt.rotation = oldRegAtt.rotation;
newRegAtt.width = oldRegAtt.width;
newRegAtt.height = oldRegAtt.height;
newRegAtt.UpdateOffset();
newSkin.AddAttachment(slotIndex, attName, newRegAtt);
}
}
skeleton.data.AddSkin(newSkin);
skeleton.SetSkin(newSkin);
skeleton.SetSlotsToSetupPose();
}
rjohnn escribióUpdate... got it working
needed to set properties on the new RegionAttachment
code below again, copies current skin to new one and applies it.
void cloneCurrentSkinAndApply(){
Spine.Skeleton skeleton = skelAnim.skeleton; Spine.Skin currSkin = skeleton.Skin; //create new skin to populate Spine.Skin newSkin = new Spine.Skin("New Skin"); //create an attachment loader to use SpriteCollectionAttachmentLoader spriteCollectionAttachmentLoader = new SpriteCollectionAttachmentLoader(skelAnim.skeletonDataAsset.spriteCollection); //vars to use in loop System.Collections.Generic.List<Spine.Slot> slots = skeleton.slots; System.Collections.Generic.List<string> attachmentNames; System.Collections.Generic.List<Spine.Attachment> slotAttachments; // for each slot in the skin, get the attachment names, image names, make copy of the attachement to the new skin foreach(Spine.Slot slot in slots){ //get the slot index int slotIndex = skeleton.FindSlotIndex(slot.data.name); //get attachments from this slot slotAttachments = new System.Collections.Generic.List<Spine.Attachment>(); currSkin.FindAttachmentsForSlot(slotIndex,slotAttachments); //get the names attachmentNames = new System.Collections.Generic.List<string>(); currSkin.FindNamesForSlot(slotIndex,attachmentNames); //for each attachment in this slot, copy it to new skin for(int i=0;i<slotAttachments.Count;i++){ Spine.Attachment att = slotAttachments[i]; string attName = attachmentNames[i]; string spriteImageName = att.Name; Debug.Log("In Slot "+slotIndex+" '"+slot.data.name+"', add attachment '"+attName+"' source image name:'"+spriteImageName+"'"); Spine.Attachment newAtt = spriteCollectionAttachmentLoader.NewAttachment(newSkin, Spine.AttachmentType.region, spriteImageName); Spine.RegionAttachment newRegAtt = newAtt as Spine.RegionAttachment; Spine.RegionAttachment oldRegAtt = att as Spine.RegionAttachment; //copy properties from old regionattachmet to new one newRegAtt.x = oldRegAtt.x; newRegAtt.y = oldRegAtt.y; newRegAtt.scaleX = oldRegAtt.scaleX; newRegAtt.scaleY = oldRegAtt.scaleY; newRegAtt.rotation = oldRegAtt.rotation; newRegAtt.width = oldRegAtt.width; newRegAtt.height = oldRegAtt.height; newRegAtt.UpdateOffset(); newSkin.AddAttachment(slotIndex, attName, newRegAtt); } } skeleton.data.AddSkin(newSkin); skeleton.SetSkin(newSkin); skeleton.SetSlotsToSetupPose(); }
Bump this,
I update this code to work with the new runtime implementations.
Change this line:
Spine.Attachment newAtt = spriteCollectionAttachmentLoader.NewAttachment(newSkin, Spine.AttachmentType.region, spriteImageName);
For this:
Spine.Attachment newAtt = spriteCollectionAttachmentLoader.NewRegionAttachment(newSkin, spriteImageName, spriteImageName);
But if I add FFD to the skeleton this code doesn't work. What I did is add this code:
if(att is Spine.MeshAttachment)
{
Spine.Attachment newMeshAtt = spriteCollectionAttachmentLoader.NewMeshAttachment(newSkin,spriteImageName,spriteImageName);
Spine.MeshAttachment NewMatt = newMeshAtt as Spine.MeshAttachment;
Spine.MeshAttachment oldMatt = att as Spine.MeshAttachment;
NewMatt.vertices = oldMatt.vertices;
NewMatt.UVs = oldMatt.UVs;
NewMatt.regionUVs = oldMatt.regionUVs;
NewMatt.Triangles = oldMatt.Triangles;
newSkin.AddAttachment(slotIndex, attName, NewMatt);
}
Now the MeshAttachments works, but they are not doing changes when the animations have keyframes with FFD. They are just visible like setup mode.
What is wrong?
Thank you
Different attachments may have different meshes, so FFD is per done per attachment. FfdTimline is only applied if the current attachment matches the FfdTimline's attachment.
- Editado
For this line:
Spine.Attachment newAtt = spriteCollectionAttachmentLoader.NewRegionAttachment(newSkin, spriteImageName, spriteImageName);
I think I've found that the second "spriteImageName" is expecting a path, but a path to what exactly? Nothing I try seems to work.
Edit: Okay, looks like I got it, but I don't understand why. I was trying the path to the sprite collection, "Assets/Collections/Accessories", which throws an error saying that the sprite "Accessories" was not found, even though I was just trying to supply the folder. So I changed it to "Assets/Collections/hat", "hat" being the name of the sprite I was trying to create an attachment for, and for whatever reason it works, even though the path isn't specifying the "Accessories" folder. I don't get it, but hey, it works.
The path is what is used to find the image. Most loaders don't use the name, except for error messages. Usually the path and name are the same, but not always. Passing spriteImageName
for both is fine.
Nate escribióThe path is what is used to find the image. Most loaders don't use the name, except for error messages. Usually the path and name are the same, but not always. Passing
spriteImageName
for both is fine.
I don't know what was going wrong before... I tried putting my sprite name for both and it didn't work, but now that I tried it again so I could give you the error message, it works. :o Eh, I probably screwed something else up. Thanks.
Nate escribióDifferent attachments may have different meshes, so FFD is per done per attachment. FfdTimline is only applied if the current attachment matches the FfdTimline's attachment.
I think I don't follow you,
Do I need to copy the FfdTimLine from the old skin to the new skin?
This code just copy the skin attachments to a new runtime skin. What I need to copy in order to make FFD works?
FfdTimeline is a timeline. Animations have a list of timelines. For every animation, you'd need to copy any FfdTimeline who has the attachment that has the FFD you want to copy.
Take a look at this task I just added, and especially the thread linked from the task:
https://trello.com/c/ljyJfNVt/63-way-to ... -be-reused
Nate escribióFfdTimeline is a timeline. Animations have a list of timelines. For every animation, you'd need to copy any FfdTimeline who has the attachment that has the FFD you want to copy.
Take a look at this task I just added, and especially the thread linked from the task:
https://trello.com/c/ljyJfNVt/63-way-to ... -be-reused
I don't get why from all timelines the only that don't work is FFDTimeLine, When I use this script all works perfect (Animations, Colors, events, etc), without need of copy any timeline or change anything.
From the post you linked, I am on the group that get advantage from tweak every FFD as unique.
From what you say, copy the FFDTimeLine for every animation who has attachments with FFD sounds very complex.
Can you post a code how to do that with my script?
Well, it would be good if you explain exactly what you are trying to achieve.
Nate escribióWell, it would be good if you explain exactly what you are trying to achieve.
Im copying skins with attachments(some have FFD) to create a new runtime skin.
My skins are divided in body parts. Ex: Head1, Head2, Body1, Body2, Legs1, legs2.
At runtime I make a random combination, ex: Head2, body1, legs1. Then I create the skin with this script.
All works well except from animated FFD.
In Spine they look like:
Skin: HEAD1
Slot: HEAD
Skin placeholder: regular head
MeshAttachment: regular-head1
Skin placeholder: big head
Attachment: big-head1
You don't need to create a copy of an attachment to put it in a new skin. Attachments are stateless. The same attachment can be in multiple skins, attached to multiple skeletons, etc.
Nate escribióYou don't need to create a copy of an attachment to put it in a new skin. Attachments are stateless. The same attachment can be in multiple skins, attached to multiple skeletons, etc.
I add a copy of the skin to the new runtime skin. So the new runtime skin is a combination of multiples skins.
Ex:
New Runtime skin:
Head1 skin
Body2 skin
Legs1 skin
All merged in one skin.
Create a new, empty skin. Iterate each attachment in the Head1 skin and add it to the new skin. You don't need to make a copy of the attachment. Do the same for Body2 and Legs1 skins. The resulting skin has the attachments from the Head1, Body2 and Legs1 skins.
Yeah I do that, what Im copying is the properties, triangles, uvs, etc. If i don't that the attachments in the new skin are not visible in unity.
Look at the post of the guy that created the script, He had the same problem and the solution was copying the properties.
Anyway, if I do what you say the FFD animations will work?
I don't think the OP is doing the same. You do not need to create new attachments, you can just add the existing attachment to multiple skins. You shouldn't create new attachments. If it doesn't render, focus on that problem, not on copying the attachments.
Yes, if you copy the FFD timelines and change the attachment to the new attachment then your new attachment will animate using the same FFD.