• Unity
  • [Unity] Spine Unity Examples

Can you send me your exported Spine data? Preferably everything included, but I only really need the .json and the .atlas.txt if something is super secret.
(PM me a private link or some such)


Received.

Still works fine on my end. Sending you a packaged project (import into a blank Unity 5 project)

Related Discussions
...

Just want to say for the record that Mitch is a legend and was able to fix the issue even with a face full of burrito!
Just in case anyone comes across this same issue I had to set the sprite material of the weapon to match the sprite that contained the weapon slot. I was using Spine/Skeleton lit

skeletonRenderer.skeleton.AttachUnitySprite("blank_weapon",.weaponSprite,"Spine/Skeleton Lit") ;

thanks again
G

Could you explain what you are doing in this code?

Texture2D tex = sprite.texture;
int instanceId = tex.GetInstanceID();
AtlasRegion atlasRegion;

//check cache first
if (atlasTable.ContainsKey(instanceId)) {
   atlasRegion = atlasTable[instanceId];
} else {
   //Setup new material
   Material mat = new Material(shader);
   if (sprite.packed)
      mat.name = "Unity Packed Sprite Material";
   else
      mat.name = sprite.name + " Sprite Material";
   mat.mainTexture = tex;

   //create faux-region to play nice with SkeletonRenderer
   atlasRegion = new AtlasRegion();
   AtlasPage page = new AtlasPage();
   page.rendererObject = mat;
   atlasRegion.page = page;

   //cache it
   atlasTable[instanceId] = atlasRegion;
}

Rect texRect = sprite.textureRect;

//normalize rect to UV space of packed atlas
texRect.x = Mathf.InverseLerp(0, tex.width, texRect.x);
texRect.y = Mathf.InverseLerp(0, tex.height, texRect.y);
texRect.width = Mathf.InverseLerp(0, tex.width, texRect.width);
texRect.height = Mathf.InverseLerp(0, tex.height, texRect.height);

No.

j/k

It creates an easy way to interface the Unity Sprite-packing system with the RendererObject system of the Spine SkeletonRenderer. It also caches a particular sprite/attachment in case it has already been converted. Unity Packed Sprite Altases must also be Pre-multiplied at runtime, so i some code near where you pasted that from does that as well and caches the instance id of the texture to a static lookup table to prevent premultiplying a texture twice.

Hehe

Im trying to re-write this script so it can equip 4 slots (helmet, armor, weapons). Right now its working if I do 1 attacher script per unit, but I want to change it so I can have 1 attacher script that handles all 4 equipment slots.

One part of your code I still dont get is this:

//normalize rect to UV space of packed atlas
texRect.x = Mathf.InverseLerp(0, tex.width, texRect.x);
texRect.y = Mathf.InverseLerp(0, tex.height, texRect.y);
texRect.width = Mathf.InverseLerp(0, tex.width, texRect.width);
texRect.height = Mathf.InverseLerp(0, tex.height, texRect.height);

Why do you have to InverseLerp this? Whats going on here? 🙂

(Also whats pre-multiplying a texture?)

UVs are expressed as values 0 to 1. InverseLerp converts the part/total (x/width or y/height) ratio into the appropriate number.
Though it could have just as well been that simple division since the first parameter of InverseLerp is 0.
Except InverseLerp clamps? And handles negatives? I'm not actually sure what other contribution it has.

Check the links provided here to see what Premultiply Alpha means: Premultiply Alpha
tl;dr. You need to pre-process the pixels so it shows up correctly with the Spine/Skeleton shader (which is a premultiply-alpha shader).

Ahh Ok I will read up on that thanks.

//create faux-region to play nice with SkeletonRenderer
atlasRegion = new AtlasRegion();
AtlasPage page = new AtlasPage();
page.rendererObject = mat;
atlasRegion.page = page;

//cache it
atlasTable[instanceId] = atlasRegion;

Does this keep creating an Atlas page for EACH sprite?

If so can I take an existing Atlas page and see if there's room and then add the sprite to that? Or am I not understanding how this is working ? 🙂

Those objects (AtlasRegion and AtlasPage) are just dummy objects here. They don't actually create extra textures that hold the sprite.

Ok Im going to give this a good 4 hours tonight and get back to you with my progress. I'd be happy to share my multi-item adder if I get it working 🙂


Ok I've made some good progress. I successfully have it adding and item of the correct sprite to the correct spot. Its working via the inspector so this should help people if they want it. I am going to tweak it to work via code too.

Question:
You guys have a really awesome [SpineSlot] serializable thingy. Can I do this for sprites too and get a list of all my sprite objects?

Sometimes it is, sometimes its not. For example, here is one of my characters with the weapons and armor working fine, but the helmet is the wrong direction. The sprite is rendered upright, but its being rendered at 90*.

I've pasted my code below but I have a 2 issues:
1. My sprite packer is always empty (even if I set packing tags and click pack). I have Unity 5 pro.

  1. The sprite.packed value is false.

Is there a way I can set the rotation of the sprite in the below code? Where is this rotation data actually parsed and used and how can I catch issues like that in my code.

Here is my code for multi-slot attachment

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Spine;

public class ItemAttacher : MonoBehaviour {
   

public bool attachOnStart = true; public bool keepLoaderInMemory = true; public SlotAttachment[] slotAttachments;
[System.Serializable] public class SlotAttachment { [SpineSlot] public string slot; public Sprite sprite; }
private ItemAttachmentLoader loader; private RegionAttachment attachment;
void Start () { if (attachOnStart) Attach(); }
public void Attach () { var skeletonRenderer = GetComponent<SkeletonRenderer>(); foreach(SlotAttachment slotAttachment in slotAttachments) { //create loader instance, tell it what sprite and shader to use loader = new ItemAttachmentLoader(slotAttachment.sprite, Shader.Find("Sprites/Default")); attachment = loader.NewRegionAttachment(null, slotAttachment.sprite.name, ""); skeletonRenderer.skeleton.FindSlot(slotAttachment.slot).Attachment = attachment; if (!keepLoaderInMemory) loader = null; } } } public class ItemAttachmentLoader : AttachmentLoader {
//TODO: Memory cleanup functions
//IMPORTANT: Make sure you clear this when you don't need it anymore. Goodluck. public static Dictionary<int, AtlasRegion> atlasTable = new Dictionary<int, AtlasRegion>();
//Shouldn't need to clear this, should just prevent redoing premultiply alpha pass on packed atlases public static List<int> premultipliedAtlasIds = new List<int>();
Sprite sprite; Shader shader;
public ItemAttachmentLoader (Sprite sprite, Shader shader) {
if (sprite.packed && sprite.packingMode == SpritePackingMode.Tight) { Debug.LogError("Tight Packer Policy not supported yet!"); return; } this.sprite = sprite; this.shader = shader; Texture2D tex = sprite.texture; //premultiply texture if it hasn't been yet int instanceId = tex.GetInstanceID(); if (!premultipliedAtlasIds.Contains(instanceId)) { try { var colors = tex.GetPixels(); Color c; float a; for (int i = 0; i < colors.Length; i++) { c = colors[i]; a = c.a; c.r *= a; c.g *= a; c.b *= a; colors[i] = c; } tex.SetPixels(colors); tex.Apply(); premultipliedAtlasIds.Add(instanceId); } catch { //texture is not readable! Can't pre-multiply it, you're on your own. Debug.Log("Texture was not readable"); } } }
public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) { RegionAttachment attachment = new RegionAttachment(name);
Texture2D tex = sprite.texture; int instanceId = tex.GetInstanceID(); AtlasRegion atlasRegion; //check cache first if (atlasTable.ContainsKey(instanceId)) { atlasRegion = atlasTable[instanceId]; } else { //Setup new material Material mat = new Material(shader); if (sprite.packed) mat.name = "Unity Packed Sprite Material"; else mat.name = sprite.name + " Sprite Material"; mat.mainTexture = tex; //create faux-region to play nice with SkeletonRenderer atlasRegion = new AtlasRegion(); AtlasPage page = new AtlasPage(); page.rendererObject = mat; atlasRegion.page = page; //cache it atlasTable[instanceId] = atlasRegion; } Rect texRect = sprite.textureRect; //normalize rect to UV space of packed atlas texRect.x = Mathf.InverseLerp(0, tex.width, texRect.x); texRect.y = Mathf.InverseLerp(0, tex.height, texRect.y); texRect.width = Mathf.InverseLerp(0, tex.width, texRect.width); texRect.height = Mathf.InverseLerp(0, tex.height, texRect.height); Bounds bounds = sprite.bounds; Vector3 size = bounds.size; //TODO: make sure this rotation thing actually works bool rotated = false; Debug.Log("Sprite is" + sprite.packed); if (sprite.packed) rotated = sprite.packingRotation == SpritePackingRotation.Any; //do some math and assign UVs and sizes attachment.SetUVs(texRect.xMin, texRect.yMax, texRect.xMax, texRect.yMin, rotated); attachment.RendererObject = atlasRegion; attachment.SetColor(Color.white); attachment.ScaleX = 1; attachment.ScaleY = 1; attachment.RegionOffsetX = sprite.rect.width * (0.5f - Mathf.InverseLerp(bounds.min.x, bounds.max.x, 0)) / sprite.pixelsPerUnit; attachment.RegionOffsetY = sprite.rect.height * (0.5f - Mathf.InverseLerp(bounds.min.y, bounds.max.y, 0)) / sprite.pixelsPerUnit; attachment.Width = size.x; attachment.Height = size.y; attachment.RegionWidth = size.x; attachment.RegionHeight = size.y; attachment.RegionOriginalWidth = size.x; attachment.RegionOriginalHeight = size.y; attachment.UpdateOffset(); return attachment; }
public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) { //TODO: Unity 5 only throw new System.NotImplementedException(); }
public SkinnedMeshAttachment NewSkinnedMeshAttachment (Skin skin, string name, string path) { throw new System.NotImplementedException(); }
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) { throw new System.NotImplementedException(); } }
Mitch a écrit

Looks like they changed how they did script updates... latest github repo (not hte unity package) shouldn't need to be API-updated for Unity 5. try doing a clean pull. I'll try and address this with a few other Unity 5 issues sometime today.

Do you know if this is sorted now, please? Still doesn't seem to be working for me no matter how I try and run it.

@Aggressor:
Are your checks done at runtime/play mode or in the editor? 'cause there's some weird stuff about sprite textures not having their packed versions returned unless it's actually at runtime.

MarkB a écrit
Mitch a écrit

Looks like they changed how they did script updates... latest github repo (not hte unity package) shouldn't need to be API-updated for Unity 5. try doing a clean pull. I'll try and address this with a few other Unity 5 issues sometime today.

Do you know if this is sorted now, please? Still doesn't seem to be working for me no matter how I try and run it.

Try it with not Unity 5 beta-release :/ They're up to 5.0.1 now. No one else is having the problem you're having.

@[supprimé]

Yes it is at runtime

Mitch a écrit

Try it with not Unity 5 beta-release :/ They're up to 5.0.1 now. No one else is having the problem you're having.

Sweet! That sorted it - thanks 😃

un mois plus tard

I need to attach a GameObject to slot (not the sprite). Can anyone tell me how to make this work? Thank!

BoneFollower or SkeletonUtility.

i try it but not work! Because my animation change scale and alpha of slot during the anim, so when i attach gameobject as bone follower or SkeletonUtility, nothing playing on the screen


any help ? This gameobject has a lot of child as sprite. I want to attach to slot so that the animation can affect all child sprites (scale or alpha).

un mois plus tard

Hey guys,
Im just wondering if it is possible to add an overlay/decal to a spine character in unity. Basically I have my character but during the game I would like to apply blood or dirt decals to the arms, head etc. Rather then having to create new sprite sheets with blood applied for each character part and swapping them out that way is there a way to add textures to a spine model individual parts at runtime??
What is the best way to do this to try keep assets at a minimum?
Cheers
Gav

17 jours plus tard

Hi all,

Is there a bug with the latest runtime, as it is not automatically creating a prefab for the exported project. I have the option to Generate a Mecaniam Controller and bake it, but would prefer the prefab option instead. Is there even a way that I can manually create the prefab?

Thanks!

[Edit] And just as I post, I see the right click option to instantiate SkeletonAnimation ha!

(1) Thank goodness you didn't bake it.
If you're new to Spine, or new to Unity, absolutely do not use Baking.
It was originally created as a workaround for the Asset Store, and it remains an advanced feature (for people looking to sacrifice Spine features for performance-on-a-case-by-case-basis like for environment animation or something.)

(2) This isn't a bug. You probably have the expectation of it creating a prefab for you from the old video.
The new version doesn't do that anymore. It leaves generating prefabs up to you 'cause not everyone needs prefabs for every single Spine skeleton they import.

(3) Yes, right click on the SkeletonDataAsset (the asset with the Spine icon) and choose one of the Instantiate options.