Search Unity

Duplicate or copy Tiles and Palettes

Discussion in 'Editor & General Support' started by RemDust, May 1, 2018.

  1. RemDust

    RemDust

    Joined:
    Aug 28, 2015
    Posts:
    432
    hi there !

    I'm having a hard time wrapping my head around the new tilemap tools.

    Basically what I m looking for is a simple way to duplicatea palette (with the rule tiling already implemented) and just change the sprites/tiles in the palette/rule so I can create quick variation of my levels : ice ground, stone ground etc.

    Right now I have to repeat the whole process everytime wich is kind of a pain in the neck ^^

    Thank u all ! <3
     
  2. nicmarxp

    nicmarxp

    Joined:
    Dec 3, 2017
    Posts:
    406
    Great question! Did you solve it? Just spent 4 hours making rule tiles for a topdown game. Daarn it was complex. Should be able to replace the image files, maybe with a script? :)
     
  3. basbuit

    basbuit

    Joined:
    Nov 22, 2019
    Posts:
    1
    i am completly new so dont ask me
     
    AndrewLGN likes this.
  4. HenryStrattonFW

    HenryStrattonFW

    Joined:
    Jul 27, 2012
    Posts:
    18
    So I came across the same issue when dealing with tile maps. I tend to have a set pattern for my different tile sets (see attached example of a basic tile set for convex shapes) and then each different tile set has the same layout in the source image. Given these assumptions I've made the following script to effectively duplicate a RuleTile and swap out the sprites for those from another texture.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5. using System.Linq;
    6.  
    7. public class RuleTileCloner : EditorWindow
    8. {
    9.     private RuleTile referenceTile;
    10.     private Texture2D sprite;
    11.  
    12.     [MenuItem("Tools/RuleTile Cloner")]
    13.     public static void ShowTool()
    14.     {
    15.         GetWindow<RuleTileCloner>().Show();
    16.     }
    17.  
    18.     public void OnGUI()
    19.     {
    20.         referenceTile = EditorGUILayout.ObjectField(referenceTile, typeof(RuleTile), false) as RuleTile;
    21.         sprite = EditorGUILayout.ObjectField(sprite, typeof(Texture2D), false) as Texture2D;
    22.  
    23.         if (GUILayout.Button("Clone"))
    24.         {
    25.             string origPath = AssetDatabase.GetAssetPath(referenceTile);
    26.             string spritePath = AssetDatabase.GetAssetPath(sprite);
    27.             string targetPath = $"{spritePath.Substring(0, spritePath.LastIndexOf('/'))}/{sprite.name}_RuleTile.asset";
    28.  
    29.             AssetDatabase.CopyAsset(origPath, targetPath);
    30.             RuleTile oldTile = AssetDatabase.LoadAssetAtPath<RuleTile>(origPath);
    31.             RuleTile newTile = AssetDatabase.LoadAssetAtPath<RuleTile>(targetPath);
    32.  
    33.             if (newTile != null)
    34.             {
    35.                 CloneBySpriteIndex(spritePath, newTile, oldTile);
    36.             }
    37.  
    38.             AssetDatabase.SaveAssets();
    39.         }
    40.     }
    41.  
    42.     private void CloneBySpriteIndex(string spritePath, RuleTile newTile, RuleTile refTile)
    43.     {
    44.         // First load in the data for the reference tile.
    45.         Texture2D refTex = newTile.m_TilingRules[0].m_Sprites[0].texture;
    46.         string refPath = AssetDatabase.GetAssetPath(refTex);
    47.         Sprite[] refSprites = AssetDatabase.LoadAllAssetsAtPath(refPath).OfType<Sprite>().ToArray();
    48.  
    49.         // New rule tile created, now to swap out the sprites.
    50.         Sprite[] newSprites = AssetDatabase.LoadAllAssetsAtPath(spritePath).OfType<Sprite>().ToArray();
    51.         for (int i = 0; i < newTile.m_TilingRules.Count; i++)
    52.         {
    53.             RuleTile.TilingRule rule = newTile.m_TilingRules[i];
    54.  
    55.             for (int j = 0; j < rule.m_Sprites.Length; j++)
    56.             {
    57.                 int refIndex = FindIndex(refSprites, rule.m_Sprites[j]);
    58.                 rule.m_Sprites[j] = newSprites[refIndex];
    59.             }
    60.         }
    61.  
    62.         if (referenceTile.m_DefaultSprite != null)
    63.         {
    64.             newTile.m_DefaultSprite = newSprites[15];
    65.         }
    66.     }
    67.  
    68.     private int FindIndex(Sprite[] spriteArray, Sprite sprite)
    69.     {
    70.         for (int i = 0; i < spriteArray.Length; i++)
    71.         {
    72.             if (spriteArray[i] == sprite)
    73.                 return i;
    74.         }
    75.  
    76.         return -1;
    77.     }
    78. }

    NOTE: This script only supports rule tiles using a single source texture for all it's tiles. There are also various cases that I've not tested for sprite settings on the rules. So this is provided AS IS with any errors it may contain, It's just a starting point for providing a duplication feature that can be built on by others to be more flexible if necessary.

    To use:
    • Open the window from the toolbar (Tools / RuleTile Cloner).
    • Select the RuleTile that you wish to duplicate.
    • Select the new texture that you want to use for the new RuleTile.
    • Click the clone button.
    The new RuleTile will be created in the same location as the selected texture, with the suffix "_RuleTile" to the name.

    Hope this helps out anyone who finds this post and fancies speeding up their work-flow just a little bit.
     

    Attached Files:

    • Test.png
      Test.png
      File size:
      617 bytes
      Views:
      314
  5. coatline

    coatline

    Joined:
    Jul 31, 2019
    Posts:
    17
    @HenryStrattonFW this is amazingly wonderful! You should put this on the asset store. Thank you.
     
  6. AutumnFireGames

    AutumnFireGames

    Joined:
    May 16, 2021
    Posts:
    1
    @HenryStrattonFW This script is a must for anyone who wants to be as productive as possible when making a game. It saves so much time. Thank you so much!
     
  7. SgtGustav

    SgtGustav

    Joined:
    Jan 20, 2022
    Posts:
    1
    I've been using this in my project, and it worked well aside from one major issue: the script wouldn't copy the sprites in the correct order, meaning I'd have to manually set each sprite on each rule.

    In case anybody else has been havinf this problem, I tracked down the issue to the AssetDatabase.LoadAllAssetsAtPath() function sometimes returning out of order, and wrote fix to it here:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5. using System.Linq;
    6.  
    7. public class RuleTileCloner : EditorWindow
    8. {
    9.     private RuleTile referenceTile;
    10.     private Texture2D sprite;
    11.  
    12.     [MenuItem("Tools/RuleTile Cloner")]
    13.     public static void ShowTool()
    14.     {
    15.         GetWindow<RuleTileCloner>().Show();
    16.     }
    17.  
    18.     public void OnGUI()
    19.     {
    20.         referenceTile = EditorGUILayout.ObjectField(referenceTile, typeof(RuleTile), false) as RuleTile;
    21.         sprite = EditorGUILayout.ObjectField(sprite, typeof(Texture2D), false) as Texture2D;
    22.  
    23.         if (GUILayout.Button("Clone"))
    24.         {
    25.             string origPath = AssetDatabase.GetAssetPath(referenceTile);
    26.             string spritePath = AssetDatabase.GetAssetPath(sprite);
    27.             string targetPath = $"{spritePath.Substring(0, spritePath.LastIndexOf('/'))}/{sprite.name}_RuleTile.asset";
    28.  
    29.             AssetDatabase.CopyAsset(origPath, targetPath);
    30.             RuleTile oldTile = AssetDatabase.LoadAssetAtPath<RuleTile>(origPath);
    31.             RuleTile newTile = AssetDatabase.LoadAssetAtPath<RuleTile>(targetPath);
    32.  
    33.             if (newTile != null)
    34.             {
    35.                 CloneBySpriteIndex(spritePath, newTile, oldTile);
    36.             }
    37.  
    38.             AssetDatabase.SaveAssets();
    39.         }
    40.     }
    41.  
    42.     private void CloneBySpriteIndex(string spritePath, RuleTile newTile, RuleTile refTile)
    43.     {
    44.         // First load in the data for the reference tile.
    45.         Texture2D refTex = newTile.m_TilingRules[0].m_Sprites[0].texture;
    46.         string refPath = AssetDatabase.GetAssetPath(refTex);
    47.         Sprite[] refSprites = AssetDatabase.LoadAllAssetsAtPath(refPath).OfType<Sprite>().ToArray();
    48.         refSprites = SortArrayByAtlasIndex(refSprites);
    49.         //sort the refSprites array, since it might be out of order
    50.         //LoadAllAssetsAtPath() can give weird orders on sprite atlases sometimes, idk why
    51.        
    52.  
    53.  
    54.         // New rule tile created, now to swap out the sprites.
    55.         Sprite[] newSprites = AssetDatabase.LoadAllAssetsAtPath(spritePath).OfType<Sprite>().ToArray();
    56.         newSprites = SortArrayByAtlasIndex(newSprites);
    57.         //sort the newSprites array, since it might be out of order as well.
    58.  
    59.         for (int i = 0; i < newTile.m_TilingRules.Count; i++)
    60.         {
    61.             RuleTile.TilingRule rule = newTile.m_TilingRules[i];
    62.  
    63.             for (int j = 0; j < rule.m_Sprites.Length; j++)
    64.             {
    65.                 int refIndex = FindIndex(refSprites, rule.m_Sprites[j]);
    66.                 rule.m_Sprites[j] = newSprites[refIndex];
    67.             }
    68.         }
    69.  
    70.  
    71.         if (referenceTile.m_DefaultSprite != null)
    72.         {
    73.             newTile.m_DefaultSprite = newSprites[^1];
    74.         }
    75.     }
    76.  
    77.     private Sprite[] SortArrayByAtlasIndex(Sprite[] sprites)
    78.     {
    79.         //This code rearranges the sprites in the sprite array to be in the same order as they are in the sprite atlas
    80.  
    81.         Sprite[] returnArray = new Sprite[sprites.Length];
    82.         int highestID = int.MinValue;
    83.  
    84.         List<int[]> indexList = new List<int[]>();
    85.         for(int i = 0; i < sprites.Length; i++)
    86.         {
    87.             indexList.Add(new int[2]);
    88.             indexList[i][0] = i;
    89.             string idString = "";
    90.  
    91.             for(int j = 1; j < sprites[i].name.Length; j++)
    92.             {
    93.                 if (char.IsDigit(sprites[i].name[^j]))
    94.                 {
    95.                     idString = sprites[i].name[^j] + idString;
    96.                 }
    97.                 else break;
    98.             }
    99.  
    100.             int id = int.Parse(idString);
    101.             if (id > highestID) highestID = id;
    102.             indexList[i][1] = id;
    103.         }
    104.  
    105.  
    106.         int curIndex = 0;
    107.         for(int i = 0; curIndex <= returnArray.Length && i <= highestID; i++)
    108.         {
    109.             foreach(int[] index in indexList)
    110.             {
    111.                 if(index[1] == i)
    112.                 {
    113.                     returnArray[i] = sprites[index[0]];
    114.                     curIndex++;
    115.                 }
    116.             }
    117.         }
    118.  
    119.         for(int i = 0; i < returnArray.Length; i++)
    120.         {
    121.             if (returnArray[i] == null) Debug.Log(i);
    122.         }
    123.  
    124.         return returnArray;
    125.     }
    126.  
    127.     private int FindIndex(Sprite[] spriteArray, Sprite sprite)
    128.     {
    129.         for (int i = 0; i < spriteArray.Length; i++)
    130.         {
    131.             if (spriteArray[i] == sprite)
    132.                 return i;
    133.         }
    134.  
    135.         return -1;
    136.     }
    137. }
    138.  
     
    coatline likes this.