Search Unity

Question Update Sprite Assets before building

Discussion in 'UGUI & TextMesh Pro' started by rfuhrer, Oct 16, 2023.

  1. rfuhrer

    rfuhrer

    Joined:
    Oct 2, 2015
    Posts:
    52
    Hey, I use TextMeshPro to show inputs in speech bubbles. The sprites are defined in an atlas. Whenever I change something in the atlas the sprite location breaks. This is easily fixed by updating the sprite asset via the inspector.


    However, this process is prone to errors since I might forget to update before I build. Actually, this happens quite regularly. Therefore I would like to add this to a script to update this Sprite Asset before I build my game.

    Since these Sprite Asset files are not really part of the game but rather some sort of dictionary I am not sure how to access and alter these files via script.

    Do you have any idea how to do this, is it even possible?
     
  2. rfuhrer

    rfuhrer

    Joined:
    Oct 2, 2015
    Posts:
    52
    I started using this:

    Code (CSharp):
    1. [MenuItem("TMPTest/UpdateTMP")]
    2.     private static void UpdateSpriteAssets()
    3.     {
    4.         TMP_SpriteAsset  inputGamepad;
    5.         inputGamepad = Resources.Load<TMP_SpriteAsset>("Sprites/UI_Input_Gamepad");
    6.  
    7.         inputGamepad.UpdateLookupTables();
    8.      
    9.      
    10.     }
    However - it does not work. Nothing happens. No error message - nothing.
     
    Last edited: Oct 16, 2023
  3. rfuhrer

    rfuhrer

    Joined:
    Oct 2, 2015
    Posts:
    52
    With the help of a friend, I managed to come up with this here. Which works fine.
    I use methods from a locked-away script within Unity, so I copied and pasted these functions into my script.

    I hope you find this useful. Or if you know a much easier way, you can let me know as well.

    Code (CSharp):
    1. using UnityEditor.Build;
    2. using UnityEditor.Build.Reporting;
    3. using UnityEngine;
    4. using UnityEditor;
    5.  
    6. using TMPro;
    7. using System.Linq;
    8. using System.Collections.Generic;
    9. using UnityEngine.TextCore;
    10.  
    11. /// <summary>
    12. /// This script updates the sprite assets before building to make sure the correct symbols are available.
    13. /// </summary>
    14.  
    15. class UpdateSpriteAssets : IPreprocessBuildWithReport
    16. {
    17.     public int callbackOrder { get { return 0; } }
    18.    
    19.     public void OnPreprocessBuild(BuildReport report)
    20.     {
    21.        
    22.         UpdateSpriteAssetsFunc();
    23.     }
    24.  
    25.     [MenuItem("TMPAssetUpdateTest/UpdateTMP_SpriteAssets")]
    26.     private static void UpdateSpriteAssetsFunc()
    27.     {
    28.         TMP_SpriteAsset inputGamepad;
    29.         inputGamepad = Resources.Load<TMP_SpriteAsset>("Sprites/UI_Input_Gamepad");
    30.        
    31.         if(inputGamepad != null)
    32.         {
    33.            
    34.             UpdateSpriteAssetsFunc(inputGamepad);
    35.         }
    36.         else
    37.         {
    38.             Debug.LogError("TMPSpriteAsset UI_InputGamepad was null!");
    39.         }
    40.    
    41.        
    42.         TMP_SpriteAsset inputKeyboard;
    43.         inputKeyboard = Resources.Load<TMP_SpriteAsset>("Sprites/UI_Input_Keyboard");
    44.  
    45.         if (inputKeyboard != null)
    46.         {
    47.             UpdateSpriteAssetsFunc(inputKeyboard);
    48.         }
    49.         else
    50.         {
    51.             Debug.LogError("TMPSpriteAsset UI_InputKeyboard was null!");
    52.         }
    53.  
    54.         TMP_SpriteAsset inputSwitch;
    55.         inputSwitch = Resources.Load<TMP_SpriteAsset>("Sprites/UI_Input_Switch");
    56.  
    57.         if (inputSwitch != null)
    58.         {
    59.             UpdateSpriteAssetsFunc(inputSwitch);
    60.         }
    61.         else
    62.         {
    63.             Debug.LogError("TMPSpriteAsset UI_InputSwitch was null!");
    64.         }
    65.        
    66.     }
    67.  
    68.     private static void UpdateSpriteAssetsFunc(TMP_SpriteAsset spriteAsset)
    69.     {
    70.         // Get a list of all the sprites contained in the texture referenced by the sprite asset.
    71.         // This only works if the texture is set to sprite mode.
    72.         string filePath = AssetDatabase.GetAssetPath(spriteAsset.spriteSheet);
    73.  
    74.         if (string.IsNullOrEmpty(filePath))
    75.             return;
    76.  
    77.         // Get all the sprites defined in the sprite sheet texture referenced by this sprite asset.
    78.         Sprite[] sprites = AssetDatabase.LoadAllAssetsAtPath(filePath).Select(x => x as Sprite).Where(x => x != null).ToArray();
    79.  
    80.         // Return if sprite sheet texture does not have any sprites defined in it.
    81.         if (sprites.Length == 0)
    82.         {
    83.             Debug.Log("Sprite Asset <color=#FFFF80>[" + spriteAsset.name + "]</color>'s atlas texture does not appear to have any sprites defined in it. Use the Unity Sprite Editor to define sprites for this texture.", spriteAsset.spriteSheet);
    84.             return;
    85.         }
    86.  
    87.         List<TMP_SpriteGlyph> spriteGlyphTable = spriteAsset.spriteGlyphTable;
    88.  
    89.         // Find available glpyh indexes
    90.         uint[] existingGlyphIndexes = spriteGlyphTable.Select(x => x.index).ToArray();
    91.         List<uint> availableGlyphIndexes = new List<uint>();
    92.  
    93.         uint lastGlyphIndex = existingGlyphIndexes.Length > 0 ? existingGlyphIndexes.Last() : 0;
    94.         int elementIndex = 0;
    95.         for (uint i = 0; i < lastGlyphIndex; i++)
    96.         {
    97.             uint existingGlyphIndex = existingGlyphIndexes[elementIndex];
    98.  
    99.             if (i == existingGlyphIndex)
    100.                 elementIndex += 1;
    101.             else
    102.                 availableGlyphIndexes.Add(i);
    103.         }
    104.  
    105.         // Iterate over sprites contained in the updated sprite sheet to identify new and / or modified sprites.
    106.         for (int i = 0; i < sprites.Length; i++)
    107.         {
    108.             Sprite sprite = sprites[i];
    109.  
    110.             // Check if current sprites is already contained in the sprite glyph table of the sprite asset.
    111.             TMP_SpriteGlyph spriteGlyph = spriteGlyphTable.FirstOrDefault(x => x.sprite == sprite);
    112.  
    113.             if (spriteGlyph != null)
    114.             {
    115.                 // update existing sprite glyph
    116.                 if (spriteGlyph.glyphRect.x != sprite.rect.x || spriteGlyph.glyphRect.y != sprite.rect.y || spriteGlyph.glyphRect.width != sprite.rect.width || spriteGlyph.glyphRect.height != sprite.rect.height)
    117.                     spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
    118.             }
    119.             else
    120.             {
    121.                 TMP_SpriteCharacter spriteCharacter;
    122.  
    123.                 // Check if this sprite potentially exists under the same name in the sprite character table.
    124.                 if (spriteAsset.spriteCharacterTable != null && spriteAsset.spriteCharacterTable.Count > 0)
    125.                 {
    126.                     spriteCharacter = spriteAsset.spriteCharacterTable.FirstOrDefault(x => x.name == sprite.name);
    127.                     spriteGlyph = spriteCharacter != null ? spriteGlyphTable[(int)spriteCharacter.glyphIndex] : null;
    128.  
    129.                     if (spriteGlyph != null)
    130.                     {
    131.                         // Update sprite reference and data
    132.                         spriteGlyph.sprite = sprite;
    133.  
    134.                         if (spriteGlyph.glyphRect.x != sprite.rect.x || spriteGlyph.glyphRect.y != sprite.rect.y || spriteGlyph.glyphRect.width != sprite.rect.width || spriteGlyph.glyphRect.height != sprite.rect.height)
    135.                             spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
    136.                     }
    137.                 }
    138.  
    139.                 // Add new Sprite Glyph to the table
    140.                 spriteGlyph = new TMP_SpriteGlyph();
    141.  
    142.                 // Get available glyph index
    143.                 if (availableGlyphIndexes.Count > 0)
    144.                 {
    145.                     spriteGlyph.index = availableGlyphIndexes[0];
    146.                     availableGlyphIndexes.RemoveAt(0);
    147.                 }
    148.                 else
    149.                     spriteGlyph.index = (uint)spriteGlyphTable.Count;
    150.  
    151.                 spriteGlyph.metrics = new GlyphMetrics(sprite.rect.width, sprite.rect.height, -sprite.pivot.x, sprite.rect.height - sprite.pivot.y, sprite.rect.width);
    152.                 spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
    153.                 spriteGlyph.scale = 1.0f;
    154.                 spriteGlyph.sprite = sprite;
    155.  
    156.                 spriteGlyphTable.Add(spriteGlyph);
    157.  
    158.                 spriteCharacter = new TMP_SpriteCharacter(0xFFFE, spriteGlyph);
    159.                 spriteCharacter.name = sprite.name;
    160.                 spriteCharacter.scale = 1.0f;
    161.  
    162.                 spriteAsset.spriteCharacterTable.Add(spriteCharacter);
    163.             }
    164.         }
    165.  
    166.         // Update Sprite Character Table to replace unicode 0x0 by 0xFFFE
    167.         for (int i = 0; i < spriteAsset.spriteCharacterTable.Count; i++)
    168.         {
    169.             TMP_SpriteCharacter spriteCharacter = spriteAsset.spriteCharacterTable[i];
    170.             if (spriteCharacter.unicode == 0)
    171.                 spriteCharacter.unicode = 0xFFFE;
    172.         }
    173.  
    174.         // Sort glyph table by glyph index
    175.         spriteAsset.SortGlyphTable();
    176.         spriteAsset.UpdateLookupTables();
    177.         TMPro_EventManager.ON_SPRITE_ASSET_PROPERTY_CHANGED(true, spriteAsset);
    178.     }
    179. }