• Runtimes
  • About the proper way of using Spine for my current project

Hello there!

Thank you for taking the time to read my post, I am fairly new to Spine, and am currently facing a rather interesting problem.

I am struggling to understand what the best, or rather, correct way of using Spine for my current situation is. Let me try to explain my situation briefly:
Our game has some 10k character NFT's, each one Unique of course, with some dozen different body types, each body type having hundreds of different designs. Every one of these sprites has 3 images, down facing, up facing, and left facing.

Our spine artist currently has created a spine skeleton with skins for 3 of these characters, and it is working very well in Unity. However, I am concerned with the number of skins we have, it seems that having all possible skins in a Spine file, and then swapping it in engine is a huge overhead.

Theoretically, a single player will most likely only have a couple of these NFT's, and he should not care about the look of the others, but he will still have this data in his client application.

What I am trying to achieve is the following:
Since all our NFT's use the exact same skeleton, I want to create an Atlas for each NFT, and then when a user connects their wallet, I would grab the respective atlas, and apply it to the Skeleton. I would want to do this for each NFT he owns, so the same Skeleton is used but the Atlas is created on our backend.
I have tried to create an Atlas myself, by duplicating the already exsting atlas and changing a few things, I have encountered a few minor problems with that approach so I've decided to write to you for help.

Hopefully I was able to explain my problem coherently, once again thank you for your time and support.

Related Discussions
...

You don't need to define all skins in the Spine project or skeleton data. You can assemble a skin at runtime. See:
http://esotericsoftware.com/spine-runtime-skins

Similarly, you don't need to create all your attachments in the Spine editor. You could create attachments in the editor only as templates. At runtime you can get each "template" attachment and change the texture region. See:
http://esotericsoftware.com/spine-runtime-skins#Creating-attachments

To generate an atlas you could base it on the texture packer in libgdx:
https://libgdx.com/wiki/tools/texture-packer
https://github.com/libgdx/libgdx/blob/master/extensions/gdx-tools/src/com/badlogic/gdx/tools/texturepacker/TexturePacker.java
We are libgdx authors and that's our code, so naturally Spine's texture packer is based on that. Spine's packer does more though, in particular it knows the Spine project context so when it packs regions that are used by meshes, it can do whitespace trimming using the mesh vertices. Spine's packer can also pack polygons (which is difficult) while libgdx's cannot. If you aren't using meshes then you don't lose anything with libgdx's packer. Even if you are, it may not matter depending on the complexity of your images and how tightly they must be packed.

@SkamoJ Please note that if you're using Unity, the spine-unity runtime also supports replacing attachments from Unity Sprites. You can check out the example scene Spine Examples/Other Examples/Mix and Match Equip which demonstrates how to do that. You can see it briefly in this tutorial video. You mainly need to ensure that the size of the Unity Sprites matches the original attachment image size (before whitespace stripping).

Hey guys, thanks a lot for the response and the help.

@Nate I might be wrong but it is not possible to create a runtime skin from a sprite or texture not defined in the Spine project or skeleton data.
Even in the example given, you find the skin from the skeleton data, and then create a new one with it.

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");

So this means that I would have to have all possible sprites on the skeleton data, for me to be able to find it and add it to a new skin? I am trying to avoid this.

As for attachments, we are not using attachments at all currently, so this is not helping, unless I am again missing something.

I am currently working on creating a new Atlas, but I have a problem, what I am doing:
I store all atlas regions into a rect[], then I update the regions with texture2d.SetPixel() afterwords I make a new texture with all the updated regions, with the same size as the Atlas. This works but the issue is that the rects are flipped on the Y axis.

`private void SwitchAtlas()
{
var atlas = skeletonAnimation.skeletonDataAsset.atlasAssets[0].GetAtlas().Regions;
rects = new Rect[skeletonAnimation.skeletonDataAsset.atlasAssets[0].GetAtlas().Regions.Count];
textures = new Texture2D[rects.Length];


    
    for (int i = 0; i < rects.Length; i++)
    {
        //textures.Add(atlas[i].ToTexture());
        textures[i] = atlas[i].ToTexture();
        textures[i].Apply();
        rects[i] = new Rect(new Vector2(atlas[i].x,
                atlas[i].y),
            new Vector2(atlas[i].packedWidth,
                atlas[i].packedHeight));

        rects[i].y += skeletonAnimation.skeletonDataAsset.atlasAssets[0].PrimaryMaterial.mainTexture.height;
    }

    var editedTexture = SetAtlasTexture(textures[39],
        textures[12]);
    
    texture = editedTexture;
    
    textures[39] = editedTexture;
    
    newAtlas = new Texture2D(skeletonAnimation.skeletonDataAsset.atlasAssets[0].PrimaryMaterial.mainTexture.width,
        skeletonAnimation.skeletonDataAsset.atlasAssets[0].PrimaryMaterial.mainTexture.height);

    for (int i = 0; i < rects.Length; i++)
    {
        for (int x = 0; x < rects[i].width; x++)
        {
            for(int y = 0; y < rects[i].height; y++)
            {
                Color newPixel = textures[i].GetPixel(x, y);
                newAtlas.SetPixel(x + (int)rects[i].position.x, y + (int)rects[i].position.y, newPixel);
            }
        }
        newAtlas.Apply();
    }
    skeletonAnimation.skeletonDataAsset.atlasAssets[0].PrimaryMaterial.mainTexture = newAtlas;
}`
public Texture2D SetAtlasTexture(Texture2D background, Texture2D overlay)
    {
        for (int x = 0; x < background.width; x++)
        {
            for(int y = 0; y < background.height; y++)
            {
                Color bgColor = background.GetPixel(x, y);
                Color wmColor = overlay.GetPixel(x, y);
                
                //Color final_color = Color.Lerp(bgColor, wmColor, wmColor.a / 1.0f);
                Color replacedColor = wmColor;
                background.SetPixel(x, y, replacedColor);
            }
        }
        background.Apply();
        return background;
    }

I am yet to use the texture packer you mentioned, perhaps this would solve my issue?

Thank you for your time once again.

  • Nate replied to this.

    SkamoJ I might be wrong but it is not possible to create a runtime skin from a sprite or texture not defined in the Spine project or skeleton data.
    Even in the example given, you find the skin from the skeleton data, and then create a new one with it.

    That example is combining skins. It is not creating attachments. What I outlined is possible.

    SkamoJ As for attachments, we are not using attachments at all currently, so this is not helping, unless I am again missing something.

    If you had no attachments, you'd have nothing to draw (except bones).
    http://esotericsoftware.com/spine-runtime-skins

    A Skin is a map where the key is a slot and a name, while the value is an attachment.

    SkamoJ This works but the issue is that the rects are flipped on the Y axis.

    It sounds like you might be using Y-down some places and Y-up in others. You can do something like textureHeight - y to flip over everything, or y + rectHeight - rectY to flip each rectangle.

    SkamoJ I am yet to use the texture packer you mentioned, perhaps this would solve my issue?

    It sounds like you are using an existing atlas page and writing different image data to it. This could work if your replacement images are the same size and it saves you from needing to pack your own atlas. The texture packer is a completely different approach, so won't help with your current approach. Packing your own atlas is harder, but is more powerful in that it allows you to pack any collection of images into an atlas, rather than replacing images.

    عام واحد لاحقا