Search Unity

Tile Map How to remove a tile at runtime?

Discussion in '2D Experimental Preview' started by der_r, Jun 6, 2016.

  1. der_r

    der_r

    Joined:
    Mar 30, 2014
    Posts:
    259
    I'm having problems deleting tiles from a
    layer at runtime. Adding new tiles seems to work fine. Maybe I am missing something.

    Here is a code snippet:

    Code (CSharp):
    1.     public void Update()
    2.     {
    3.         var grid = map.GetComponentInParent<Grid>();
    4.         var tilePos = grid.WorldToCell(transform.position);
    5.         map.SetTile(tilePos, tile);
    6.     }
    I grab the GO in the editor and move it around to paint new tiles.

    if "tile" is any tile with a sprite attached to it, the renderer uses it.

    Setting to "null" does nothing.

    If the tile has a sprite that is completely transparent attached to it, nothing happens either.

    I'm not sure if this is a bug or expected behavior and I have to do it differently.

    [Edit] When I have a tile that is transparent with some opaque elements, like a slope, the new tile gets painted ontop of the layer.

    Made a GIF to demonstrate better what I am talking about. First I paint some tiles at runtime. Then I deactivate the layer "Ground" to show that I am painting on the intended layer.

     
    Last edited: Jun 6, 2016
    TheKingOfTheRoad likes this.
  2. Sir-Spunky

    Sir-Spunky

    Joined:
    Apr 7, 2013
    Posts:
    132
    I seem to be able to remove individual tiles at runtime by setting them to null, but I think you need to target the TileMap component rather than the Grid component, e.g.

    Code (csharp):
    1. TileMap tilemap = GetComponent<TileMap> ();
    2. tilemap.SetTile(new Vector3Int(0,0,0), null); // Remove tile at 0,0,0
    However, I've not found a way to remove all tiles at once. There is a ClearAllVirtualTiles(), but that seems to be editor-related and does nothing to the real tiles. I could destroy and recreate the whole tilemap component, but that feels a bit hacky.
     
    TheKingOfTheRoad and der_r like this.
  3. der_r

    der_r

    Joined:
    Mar 30, 2014
    Posts:
    259
    The "map" variable I access in my code is indeed a Tilemap layer. I'm using the grid component to calculate the tilemap cell from the world position of my gameobject. Setting to null also didn't work for me.
     
  4. keely

    keely

    Joined:
    Sep 9, 2010
    Posts:
    966
    Call
    SetTile to null should work. I'm guessing the only thing explaining why it doesn't work for you is that you have moved your Tilemap to different world position than your grid. Your calls to grid.WorldToCell() don't return correct values anymore if that is the case. You need to call tilemap.WorldToCell() directly.
     
  5. der_r

    der_r

    Joined:
    Mar 30, 2014
    Posts:
    259
    The Tilemap GO with Grid and all layers are still at (0/0/0). I tried calling WordToCell from tilemap directly but it didn't make a difference.

    Here is my complete setup:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Eraser : MonoBehaviour
    6. {
    7.     public TileMap map;
    8.     // Use this for initialization
    9.     void Start()
    10.     {
    11.  
    12.     }
    13.  
    14.     // Update is called once per frame
    15.     void Update()
    16.     {
    17.         var tilePos = map.WorldToCell(transform.position);
    18.         map.SetTile(tilePos, null);
    19.     }
    20. }








    I run the game and move the object around in the editor. Nothing happens. But when I modify the script to set an opague tile instead it works as expected.

    When I paint a tile that has transparent parts to it, the old tile is underneath the new tile as shown in my previous GIF.

    I tried all tilemap layers and they all behave the same.
     
  6. der_r

    der_r

    Joined:
    Mar 30, 2014
    Posts:
    259
    I modified the Eraser script to take in a Tile (starting off with null). I made another GIF to better demonstrate what I'm getting:

     
  7. keely

    keely

    Joined:
    Sep 9, 2010
    Posts:
    966
    The reason it is not working is that your Eraser is in Z = -88 and the default brush always paints on Z = 0.

    Some context so you understand what is going on:

    Tilemap is not 2D, it is 3D. All the data and APIs are in 3D coordinates. However, the current painting design is limited to 2D and we don't really advertise that fact for UX simplicity reasons. There is no way for you to paint with default brush outside Z = 0.

    In the default Rectangle layout, the rendering of any Z value is always flattened to world Z=0. Think of it as infinitely thin stack of infinite layers. You can use this for "sub-layering" if you make your levels procedurally. The sub-layers are rendered from back to front (-infinite to +infinite).

    What is happening in your case is that you painted your level with default brush so the data ended up in Z=0. Then your Eraser happens to be in transform.z = -88, so the WorldToCell also returns z = -88 and you SetTile ends up in Z = -88. So when you paint with opaque, you still see it getting rendered in world Z=0, because we flatten everything.

    I _think_ this is what happening. Can't say for 100% without having the actual project.
     
  8. der_r

    der_r

    Joined:
    Mar 30, 2014
    Posts:
    259
    That did it! Doh. :) Thanks!
     
  9. Xepherys

    Xepherys

    Joined:
    Sep 9, 2012
    Posts:
    178
    Oh, good catch. Yeah, @der_r, I don't know that I would've noticed that either until I'd pulled most of my hair out.
     
  10. SilverFox13

    SilverFox13

    Joined:
    Mar 24, 2018
    Posts:
    1
    I know I'm 4 years late, but there is one problem with this, if you scale up the Eraser gameobject and try to "erase" a tile, you have to touch the tile with the center of the eraser, but I don't want that. How would you do this but that it "erases" tiles on collision?