• Unity
  • Tween Color Of Attached Sprites

We are using the SpriteAttacher script to attach sprites and I I was curious if you know how we can solve an issue with dynamic coloring.

When a unit gets hit, I want to tween its entire sprite to red then back to full color. I am currently using AllIn1Shader https://assetstore.unity.com/packages/vfx/shaders/all-in-1-sprite-shader-156513 and it works on the base unit sprite, but as you can see above it is not working for the attached sprites (the gear is not red its still its base color).

Do you have a suggestion on how I can make sure all the attached sprites always update with the same color changes the root sprite has?

Related Discussions
...

IIRC, Nintendo uses tint-black functionality for the typical flashing effect, which tints the darker colors instead of the light ones (this functionality was added to Spine upon their request). If you need to tint whole materials only, Skeleton Tint offers a Dark Color property for this purpose. You can see it in action in the Spine Examples/Other Examples/Per Instance Material Properties example scene.
spine-unity Runtime Documentation: Shaders

If you still prefer normal tinting as in the screenshot or don't want to change your setup, it could be that you have changed the color of each Attachment (and then switched Attachments) instead of changing colors of each Slot. Or you just modified the first Material of multiple materials at your skeleton.

In any case, we would need additional context information about your tint code used and your material setup at the problematic skeleton.

Thanks Harald,

I took a look at those examples and I do like how the Dark Color tint works using the Skeleton Tint shader.

When I use the MaterialPropertyBlock to set the color, I really like how that looks compared to the flat red I was applying. Its looks good for the base unit, but the gear (the stuff in darker red) is still off when I set the slot color.

I went through each gear slot and manually set it to red, but the colors don't match.

public void ChangeColor()
{
    mpb = new MaterialPropertyBlock();
    mpb.SetColor("_Black", Color.red);
    var mesh = GetComponent<MeshRenderer>();
    mesh.SetPropertyBlock(mpb);

var skeletonComponent = GetComponent<ISkeletonComponent>();
foreach (Slot slot in skeletonComponent.Skeleton.Slots)
{
    // _gearSlots is just a reference to each slot which can have a gear sprite attached, e.g. body armor
    if (_gearSlots.Contains(slot))
    {
        slot.SetColor(Color.red);
    }
}
}

I assume its because I am not setting the MaterialPropertyBlock to get the same effect?

I am not that familiar with shaders (and it's the first thing I plan to learn in depth after I get this freaking game finished).

In order to fix this and have the attachments get the same coloring, do I need to get the mesh of the attachment (and how would I do that)? Or is there something else I need to do?

Thank you.

P.S. How do I change it back to no coloring after? I tried using Color.white but it changes it to white. [Update I was able to clear the mesh property block by setting it to null]

Glad to hear it helped.

Aggressor escribió

Its looks good for the base unit, but the gear (the stuff in darker red) is still off when I set the slot color.

Is your whole skeleton using a single atlas texture, or multiple textures? You can check this via the number of materials displayed at the bottom of your SkeletonRenderer's Inspector. If you have multiple materials, you need to set the MaterialPropertyBlock for each material via the second method listed in the documentation here:
https://docs.unity3d.com/ScriptReference/Renderer.SetPropertyBlock.html

public void SetPropertyBlock(MaterialPropertyBlock properties, int materialIndex); 

If you are using a single material:
Does it change anything if you comment out the if condition if (_gearSlots.Contains(slot)) and set the slot color for every slot? It could also be that your animations are overriding slot colors, then it depends on the execution order whether the slot color will be set last by the animation or by your code. You should then use the callback delegate skeletonRenderer.OnMeshAndMaterialsUpdated to call your code that sets the slot colors, to ensure it's called at the right time and not overwritten by animation logic. It's described here on the spine-unity documentation page:
spine-unity Runtime Documentation: Life cycle

The gear is attached at runtime and can change dynamically (it uses the legacy SpriteAttacher script) like so

public void Attach(Sprite sprite, Slot slot)
        {
            var skeletonComponent = GetComponent<ISkeletonComponent>();

        if (sprite == null)
        {
            slot.Attachment = null;
            return;
        }

        if (attachmentShader == null)
        {
            attachmentShader = Shader.Find(DefaultStraightAlphaShader);
        }

        // This is required to prevent the arms getting distorted with the wrong rotation
        RegionAttachment attachment;
        if (slot.Attachment != null)
        {
            float rotation = ((RegionAttachment)slot.Attachment).Rotation;
            attachment = sprite.ToRegionAttachmentPMAClone(attachmentShader, rotation: rotation);
        } else
        {
            attachment = sprite.ToRegionAttachmentPMAClone(attachmentShader);
        }
        slot.Attachment = attachment;
    }

I believe there is just the 1 material as well. However given sprites are attached at runtime Im not sure if that makes it more than one at runtime?

If I comment out the slots that red overtakes and ruins the look (because its not using the SetPropertyBlock). For context, there are additional empty slots on the body where we attach the runtime sprites (like helmet and armor, so that list just makes sure when I do the coloring its to those special slots which can change at runtime).

By attaching sprites to the slots at runtime, does this mean I have multiple textures? I would assume so since the coloring is separate.

Given that I am passing in sprites that get attached, is there a way to get access to mesh renderer from the slot? If so I am hoping something like this would work?:

slot.getMeshSomehow().SetPropertyBlock(mpb);

Aggressor escribió

I believe there is just the 1 material as well. However given sprites are attached at runtime Im not sure if that makes it more than one at runtime?

I repeat:

Harald escribió

You can check this via the number of materials displayed at the bottom of your SkeletonRenderer's Inspector. If you have multiple materials, you need to set the MaterialPropertyBlock for each material via the second method listed in the documentation here:
https://docs.unity3d.com/ScriptReference/Renderer.SetPropertyBlock.html

After adding Sprites you would need to repack all attachments to a single texture, otherwise they remain separate textures. Or you set the MPB for each material as described above.

You can also see this in the Mix and Match Equip example scene, which leads to multiple materials unless you hit the Done button which repacks everything to a single texture.

Given that I am passing in sprites that get attached, is there a way to get access to mesh renderer from the slot? If so I am hoping something like this would work?:

slot.getMeshSomehow().SetPropertyBlock(mpb);

There is no MeshRenderer for each slot, there is only a single MeshRenderer component at your skeleton GameObject. You can see it in the Inspector. The mesh generator generates multiple submeshes, one for each texture (material) that is needed. Just set the MPB for the other material indices.

I was checking the prefab not the runtime instance so I when I did at runtime I was able to see the materials after attaching.

The other issue was the the SpriteAttacher script was not using the right shader, so I updated it to point to the Tint one

public class SpriteAttacher : MonoBehaviour
{
    public const string DefaultPMAShader = "Spine/Skeleton Tint";
    public const string DefaultStraightAlphaShader = "Spine/Skeleton Tint";

Then I did the following to update the tinting and it worked.

public void ChangeColor(Color color)
{
    mpb = new MaterialPropertyBlock();
    mpb.SetColor("_Black", color); // Special Spine material refernce to do a Dark Tint 
    _meshRenderer = GetComponent<MeshRenderer>();
    _meshRenderer.SetPropertyBlock(mpb);
    for (int i = 1;  i < _meshRenderer.sharedMaterials.Length; i++)
    {
        _meshRenderer.SetPropertyBlock(mpb, i);
    }
}

Thank you for the extra help.

I will now see if I can get the tweening to work...

Aggressor escribió

I was checking the prefab not the runtime instance so I when I did at runtime I was able to see the materials after attaching.

Ah, that makes sense.

Aggressor escribió

Then I did the following to update the tinting and it worked.

Very glad that you've figured it out, thanks for letting us know!