• RuntimesUnity
  • Skin runtime changes and GetRepackedSkin

I have a question about skeleton realtime skin updates and GetRepackedSkin.

We have a customizable character that mix and match skins at runtime. For our current setup we have, for example, skins combined in this order: default skin -> round eye skin -> round eye makeup -> smudged round eye makeup. The eye and eye makeups slots has several attachments for expressions that is animated, for example, neutral eyes and closed eyes attachments.

So what can change at runtime is that the eye makeup gets smudged, and adds the smudged round eye makeup skin(if that is the eye shape of the eye skin) that replaces images for each skeleton eye-slot attachments (the animated expressions).
But the smudge can also be fixed and the smudge skin is removed - which seems to require a fully new skeleton skin to be created.

The tricky part is now when we want to have a repacked skin so that we have optimized materials and draw calls. So if recombining the skin it would require repacking every time the new skin is added and removed? Which is too costly for the projects target devices and when it needs to be done.

To summarize:
We want to change the skeleton skin at runtime by adding and removing addition skins, we also want to use GetRepackedSkin only once without having to call it each time the skin changes.

Is there any solutions to this?
Last option is we just pack the textures manually per character (since customization isn't too wide per character).

    Related Discussions
    ...

    DanielThomas skins combined in this order: default skin -> round eye skin -> round eye makeup -> smudged round eye makeup.

    This looks as if you're using skins for something where they are not the best suitable solution. If I understood you correctly, you would instead rather have one skin for round eyes, containing all variants of the eye:

    1. round eye without makeup,
    2. round eye covered with clean makeup
    3. round eye covered with smudged makeup.

    The above is intended to be seen similar to how Spineboy has different mouth shapes at a single slot which are all swapped out to switch between smiling, frowning, and so on. This can then be done via animation, or via code.

    If you really want to use skins for each of these eye-makeup states, you could also consider using GetRepackedAttachments() instead of GetRepackedSkin(), which would allow you to pack "overlapping" attachments of skins by packing all attachments of all skins.

    DanielThomas Last option is we just pack the textures manually per character (since customization isn't too wide per character).

    This could perhaps be the best solution, if you know your typical sets of skins in advance.

    Thanks for the reply! The more I think about it the best path (performance and complexity) is probably to create the textures and materials before, and I feel to agree. I've looked at Spine commands line and it seems this would be something that can be automated. If not we just create them by hand at a later stage in development.

    Thanks again!

    I might have hit another obstacle.
    To clarify a little, the skeleton is used between different premade characters that creates a skin a runtime from data.
    But since they share some common things, like some has the same body. It would mean the body would appear on several atlases and the skeleton would have several atlases, I'm guessing the attachment will use the first one found - meaning it can be from a different characters atlas - meaning the draw calls are going up again.

    I couldn't fin, but would ask just in case: Is there a way to specify which atlases the skeleton/renderer instance should use at runtime? Or even manipulate what order they should be read to get the attachment regions?

    If not would a solution be to go through all the attachments and remap them to the regions of the character specific atlas?

    Just finding the regions in the character specific atlas and replace them on the attachments worked great, and I also saw the loading on demand package which looks interesting.

      DanielThomas Just finding the regions in the character specific atlas and replace them on the attachments worked great, and I also saw the loading on demand package which looks interesting.

      Very glad to hear, thanks for letting us know!

      un mois plus tard

      I'm bumping this as I seem to having an issue. So as mentioned I have packed a texture for each character sharing the same skeleton/spine project. I then combine the skins and then remap the regions to a new atlas. This is the method:

      public void OptimizeSkin()
      {
      	if (_skeletonRenderer == null) return;
      	var atlasAsset = _skinData.AtlasAsset;
      	var newAtlas = atlasAsset.GetAtlas();
      	var attachments = _mainSkin.Attachments;
      	foreach (var skinEntry in attachments)
      	{
      		var attachment = skinEntry.Attachment;
      	
      		if (attachment is RegionAttachment regionAttachment)
      		{
      			var newRegion = newAtlas.FindRegion(regionAttachment.Name);
      			if (newRegion != null)
      			{
      				regionAttachment.Region = newRegion;
      				regionAttachment.UpdateRegion();
      			}
      		}
      		else if (attachment is MeshAttachment meshAttachment)
      		{
      			var newRegion = newAtlas.FindRegion(meshAttachment.Name);
      			if (newRegion != null)
      			{
      				meshAttachment.Region = newRegion;
      				meshAttachment.UpdateRegion();
      			}
      		}
      	}
      
         _skeletonRenderer.LateUpdate();
      }

      It works great and all and skeleton animation only uses the one material from the atlas. The issue is when I have several characters. It seems only the last one to be running this method gets one material, all the others seems to get materials from the other characters. I assume that the skinEntries/attachments are references directly from the skeleton and not per skin? Meaning they are shared between all the skins? Or is there another reason the skeleton animations would use materials from the other characters setting the attachments after them.

      Any suggestions on how would I go about fixing this?

        DanielThomas It seems only the last one to be running this method gets one material, all the others seems to get materials from the other characters.

        I'm afraid I don't understand this sentence. What do you mean by "materials from the other characters" here? Which materials from which other characters?

        DanielThomas I assume that the skinEntries/attachments are references directly from the skeleton and not per skin? Meaning they are shared between all the skins?

        What do you mean by "references from the skeleton"?

        It depends on where you get _mainSkin from. If _mainSkin comes directly from a skeleton's SkeletonData (e.g. via SkeletonData.FindSkin()) without modification, then the skin itself is shared data, and it references Attachments which are shared between skeletons. If _mainSkin was created manually via newSkin = new Skin(); newSkin.AddSkin(existingSkin), the skin itself is not shared between skeletons (unless you're assigning the skin at every skeleton), but the Attachment references are shared between both skins newSkin and existingSkin now.

        In case the documentation helps:
        http://esotericsoftware.com/spine-runtime-skins
        http://esotericsoftware.com/spine-api-reference#Skin

        @DanielThomas In general it depends how you have created _mainSkin.

        If the _mainSkin attachments are original shared Attachments (and not copies of Attachments, e.g. when already remapped), you may want to create and assign a copy of the original Attachments and not modify e.g. the region of skinEntry.Attachment.

        Thank you for the quick reply. You were on point, the _mainSkin was adding skins by getting them with FindSkin directly. Making a new skin after a FindSkin and copy everything before adding it did the trick.
        Thanks!

          DanielThomas Glad to hear you've figured it out, thanks for getting back to us!