Search Unity

Question Swap Tiles On Run Time

Discussion in '2D' started by quakemisterius, Nov 30, 2022.

  1. quakemisterius

    quakemisterius

    Joined:
    Oct 16, 2022
    Posts:
    2
    Greetings everyone!
    Like many before me, I am new to the unity and I need your help. Please consider that I do not have much experience with unity.
    So basically I am making a seasonal game where for each season a tile must change. I have 4 sprite sheets for each season and each slice of the spirte sheet is exactly in the same position on all 4 sheets.
    I found that tilemap has the function TileSwap() which in theory does all I need. There are just some slight problems.
    Mainly, this function requires me to pass a tileA and tileB through out the inspector. The problem is I have around 200 tiles, and it does not seem like the best way to do it. Making a list in inspector and manually assign each tile isn't really the best solution.
    I would prefer to access a tiles through the code and basically loop each of them with TileSwap. Problem is that I do not know how to get those tiles like in the inspector.
    So I looked into documentation again and found GetUsedTilesCount() which tells me how many unique tiles I have placed in the layer. That function works great and gives me the right amount. However, I still do not have reference to those tiles. Someone suggested to use GetTilesBlock(area) to receive a tiles in an area.
    It kinda works, it returns me a TileBase list but how do I access tile from it ?
    It returns me a:
    "summer tilemap_8 (UnityEngine.Tilemaps.Tile)
    UnityEngine.MonoBehaviour:print (object)
    TileMapSeasons:Start () (at Assets/TileMapSeasons.cs:28)"

    When I looked into TileBase there is a function GetTileData but it requires a position of tile which is unknown for me.

    What would be the best solution to solve the problem ? Are they any alternatives ?
    To put it simply I just want to replace each tile sprite with a new sprite. How do I get list of tiles ? Maybe I can just a pallet ?
    Hope someone could help me answer those questions or provide some helpful links.


    Code (CSharp):
    1.   public Tile tileA;
    2.     public Tile tileB;
    3.     public BoundsInt area;
    4.  
    5.     void Start()
    6.     {
    7.  
    8.         Tilemap tilemap = GetComponent<Tilemap>();
    9.         Debug.Log(tilemap.GetUsedTilesCount() + "Number of Tiles:");
    10.         TileBase[] tileArray = tilemap.GetTilesBlock(area);
    11.         for (int index = 0; index < tileArray.Length; index++)
    12.         {
    13.          
    14.        
    15.             print(tileArray[index]);
    16. }
    17. }
    18.  
    19.            
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,736
    It's just coordinates. The part you're glossing over is that in the inspector when editing you stare at the tilemap, see what needs to be changed, then instantly change it and save it.

    To replicate that in code you would need to capture 100% of all those tile coordinates that you elect to touch, along with the tile that you replace it with, and these tile coordinates and tile choices are things that you do not even bother remembering when editing normally in the Tilemap editor. To do it in code you need to write down 100% of those things.

    So if you INSIST on coding it, start by making a Vector2Int collection of coordinates and the replacement tiles and drag them all in and type them all out, then write code to change them out.

    But remember, essentially you will be (partially) rewriting the Tilemap editor yourself. Why not just USE it?

    Doing it in the Tilemap editor is ALWAYS the best way: make a different tilemap (or partial tilemap) for the parts of your scene(s) that change with the seasons.
     
    quakemisterius likes this.
  3. Lo-renzo

    Lo-renzo

    Joined:
    Apr 8, 2018
    Posts:
    1,513
    Alternative 1: have "seasonal" tiles. Each tile owns multiple sprites. You call tilemap.RefreshAllTiles() after you change the season. If you're using simple tiles this is viable, whereas if you're using RuleTiles then editing the RuleTile and RuleTileEditor is bound to be a bit of a pain.
    Alternative 2: have seasonal tile groupings.
    It's probably the best solution actually. You need to somewhere input the data to say which tiles are associated with which season. That shouldn't be so bad if you arrange your data conveniently.
    Code (CSharp):
    1. public class SeasonalTileSwapper : MonoBehaviour // put on some gameobject
    2. {
    3.   [System.Serializable] // so this simple class shows up in the Inspector
    4.   public class SeasonGrouping
    5.   {
    6.      public TileBase tileSpring;
    7.      public TileBase tileSummer;
    8.      public TileBase tileFall;
    9.      public TileBase tileWinter;
    10.  
    11.      public TileBase GetSeasonTile(Season season)
    12.      {
    13.         // switch statement to get correct tile
    14.      }
    15.   }
    16.  
    17.  public Tilemap tilemap; // assign in Inspector
    18.  public SeasonGrouping[] groupings; // populate this in the Inspector to say what tiles are associated
    19.  
    20.  public void SwapToSeason(Season oldSeason, Season newSeason) // call from your season control system
    21.  {
    22.      foreach (var g in groupings)
    23.      {
    24.            var currentTile = g.GetSeasonTile(oldSeason);
    25.            var newTile = g.GetSeasonTile(newSeason);
    26.            tilemap.SwapTile(currentTile, newTile);
    27.      }
    28.  }
    29. }
    Untested, could have typo etc, hope that helps.
     
    quakemisterius likes this.
  4. quakemisterius

    quakemisterius

    Joined:
    Oct 16, 2022
    Posts:
    2
    Thank everyone for your responses! :)
    I finally developed this system in different way but works like a charm.
    For anybody who struggled like me:
    In inspector, I assigned an every tile from summer set to a list. I sliced each sprite sheet and made sure each of the sprite have same name on another season sprite sheets. Example in summer sheet and winter both first sliced sprites have name "rock".
    Then I just load a sprite sheet into a list of sprites with loadAll function and I loop through each sprite and change sprite assigned in the inspector. At the end, I just refresh tileMap to make sure everything render.
    In this way I didn't need to assign every tile and its corresponding season tiles. I just assigned it once and then swap sprites from resources loadAll function.

    Code might be not working as I just made it as example. Hope it gives idea how I did accomplish it. Right know I have better and more complex solution.

    Code (CSharp):
    1. public Tile[] allTiles;
    2. public Sprite[] newSeasonSprite;
    3.  
    4. public void swapSeason()
    5. {
    6. newSeasonSprite = Resources.LoadAll<Sprite>(“path to Sprite sheet”);
    7. int i=0;
    8. foreach (Sprite sprite in newSeasonSprite)
    9. {
    10.     if(sprite.name ==allTiles[i].name)
    11.      {
    12.  
    13.         allTiles[i].sprite = sprite;
    14.       }
    15. }
    16.  
    17. refreshTileMap();
    18. }
    19.  
     
    Last edited: Dec 1, 2022
    Kurt-Dekker likes this.