Search Unity

  1. Unity 2019.1 beta is now available.
    Dismiss Notice
  2. We're excited to be bringing you the newest Unity features, news, demos, and we've even got some special guests from some of the industry’s most storied franchises. Set a reminder to tune in on YouTube here
    Dismiss Notice
  3. Unity 2018.3 is now released.
    Dismiss Notice

Manipulating tilemaps from outside the tilemap tools seems to be impossible

Discussion in '2D Experimental Preview' started by nemo10, Oct 18, 2017.

  1. nemo10


    Jul 25, 2017
    I've been working on my own version of a terrain tile that uses unique images for every tile direction instead of rotating them like the one in 2D extras does. (It's for a platformer, so I want visual variety in the sides/top/bottom)

    Rather than make every possible combination of tiles with an indented corner, I want to add the corner details as overlays when they're necessary.

    Unfortunately, I haven't yet found a way to do this due to weirdness in the Tilemap API.

    I ruled out the option of instantiating a game object for every tile that needed corner details and then stacking sprite renderers based on how many corners the tile had - I assume adding additional hundreds/thousands of GO's is a bad idea, though I could be wrong.

    This is where the API design gets confusing. In order to manipulate tile data, I need an instance of ITilemap. But... ITilemap is not an interface, it's a class. And Tilemap is not a subclass of ITilemap. No, ITilemap is a completely separate class that only seems to exist/be accessible when passed by the editor itself to GetTileData while painting tiles. And it's not even ITilemap - it's UnityEditor.EditorPreviewTilemap, which is not documented anywhere. Furthermore, Tilemap's API contains everything that ITilemap has, which makes the inclusion of ITilemap even more confusing. It also has a GetEditorPreviewTile() method, which seems to obviate the need for ITilemap.

    I tried to manipulate the contents of my corner overlay layers while creating the terrain tiles, but since I only have access to the ITilemap instance currently being used for the current tilemap, I seem to be able to set all the tilemap data I need to correctly while simultaneously completely screwing up the editor preview for the current tilemap layer.

    So my next idea was to handle all of this via menu item and see if I could get the correct tile info from outside the tile base API. Turns out, that's not really possible.

    Why? TileBase has no accessor to location on its own, so if I want to iterate through all tiles and then make changes based on what I find, I cannot simply call tilemap.GetTilesBlock (tilemap.cellBounds); and then iterate through the results - I have no access to the positions of those tiles AND I don't have access to an instance of ITilemap, so I can't call tile.GetTileData().

    Am I just not expected to ever make modifications to Tilemaps outside of the tilemap tools? Or is there some fundamental piece of information about the tilemap API that I missed?

    rakkarage and Sylmerria like this.
  2. snarlynarwhal


    Jan 16, 2014
    The current Tilemap API is terrible IMO. I have spent about 5 hours trying to write a replace tiles script.
    Trollypolly, KhenaB and rakkarage like this.
  3. tabrooksy


    Jan 21, 2018
    @PJ-Legendre i am a complete noob here so dont quote me be here is a script i wrote for replacing tilemap tiles

    Code (CSharp):
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    7. using UnityEngine.Tilemaps;
    8. public class ClickableTile : MonoBehaviour
    9. {
    10.     public bool PaintActive;
    11.     public Tilemap tileMap;
    12.     public Tile redTile;
    13.     public List<Vector3> listOfTilePositions = new List<Vector3>();
    14.     public Vector3Int cellPosition;
    15.     public Camera cameraP;
    16.     public GridLayout gridLayout;
    17.     public int leftCornerX;
    18.     public int leftCornerY;
    20.    void Start()
    21.     {
    22.         for (int x = tileMap.cellBounds.xMin; x < tileMap.cellBounds.xMax; x++)
    23.         {
    24.             for (int y = tileMap.cellBounds.yMin; y < tileMap.cellBounds.yMax; y++)
    25.             {
    26.                 Vector3Int localPlace = (new Vector3Int(x, y, (int)tileMap.transform.position.y));
    27.                 Vector3 place = tileMap.CellToWorld(localPlace);
    28.                 if (tileMap.HasTile(localPlace))
    29.                 {
    30.                   listOfTilePositions.Add(new Vector3(x - leftCornerX, y - leftCornerY, 0));
    32.                 }
    33.             }
    34.         }
    35.     }
    36.     void fireLaser() {
    38.         Vector2 CamPosGrid = cameraP.ScreenToWorldPoint(Input.mousePosition);
    39.         cellPosition = gridLayout.WorldToCell(CamPosGrid);
    40.         Debug.Log(cellPosition);
    41.         if (listOfTilePositions.Contains(cellPosition))
    42.         {
    43.            tileMap.SetTile((cellPosition), redTile);
    44.         }
    46.     }
    47.     void Update()
    48.     {
    50.         if (Input.GetButtonDown("Fire1"))
    51.         {
    52.             PaintActive = true;
    53.         }
    54.         if (Input.GetButtonUp("Fire1"))
    55.         {
    56.             PaintActive = false;
    57.         }
    58.         if (PaintActive == true)
    59.         {
    60.             fireLaser();
    61.         }
    62.     }
    63. }
    Last edited: Jan 21, 2018
  4. tabrooksy


    Jan 21, 2018
    if you test my code please be aware that you will need to adjust the leftCornerX and leftCornerY to the number displayed for the cell position ie

    i you click on the bottom left corner of the tile map in playmode and it says (-8.0,-9.0,0.0) in the debug adjust to following

    LeftcornerX = 8;
    LeftcornerY= 9;
    Last edited: Jan 21, 2018
  5. snarlynarwhal


    Jan 16, 2014
    @tabrooksy thanks for the reply - I eventually figured it out and did something similar:

    Code (CSharp):
    1. private void FindReplaceableTilesInTilemap(Tilemap tilemap) {
    2.         foreach (var position in tilemap.cellBounds.allPositionsWithin) {
    3.             TileBase tile = tilemap.GetTile(position);
    4.             if (tile != null) {
    5.                 HandleReplaceTile(tilemap, tile, position);
    6.             }
    7.         }
    8.     }
    10.     private void HandleReplaceTile(Tilemap tilemap, TileBase tile, Vector3Int position) {
    11.         for (int i = 0, len = findTiles.Count; i < len; i++) {
    12.             if (findTiles[i] == tile) {
    13.                 tilemap.SetTile(position, replaceTiles[i]);
    14.             }
    15.         }
    16.     }
  6. Connorses


    Jan 28, 2016
    May I recommend making your tiles half the size, and drawing them using a 2x2 brush? This way you could write ruletiles that make up the corners of a tile, and they might fit together better. Simply draw groups of 4 ruletiles instead of trying to work with singular tiles.
  7. Jinnk


    Sep 20, 2014
    Man, you are a life saver, thanks!
    snarlynarwhal likes this.
  8. meksikietis222


    Nov 21, 2018
    Wow thanks, what about placing tiles on empty grid?