Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question How to Highlight a Tile on Click (Troubleshooting SetTile)

Discussion in '2D' started by pjlouis90, Oct 10, 2022.

  1. pjlouis90

    pjlouis90

    Joined:
    Aug 14, 2022
    Posts:
    2
    Hi,

    I followed a tutorial to try to get one of my tiles to highlight on click (or mouseover), but it doesn't seem to be working. This is my first time seeking help with Unity, so please bear with me.

    In my scene I have a grid with two tilemaps as children: Interactive and Terrain. My intent is for Interactive to show the highlight using a HighlightTile sprite, while Terrain shows the default sprite.

    The grid has the following script attached to it:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Tilemaps;
    5. using UnityEngine.UIElements;
    6.  
    7.  
    8. public class GridController : MonoBehaviour {
    9.  
    10.     private Grid grid;
    11.  
    12.     [SerializeField] private Tilemap interactiveMap = null;
    13.     [SerializeField] private TileBase hoverTile = null;
    14.  
    15.     private Vector3Int previousMousePos = new Vector3Int();
    16.  
    17.     void Start() {
    18.         grid = gameObject.GetComponent<Grid>();
    19.     }
    20.  
    21.     void Update()
    22.     {
    23.  
    24.         Vector3Int mousePos = GetMousePosition();
    25.  
    26.         if (!mousePos.Equals(previousMousePos)) {
    27.             interactiveMap.SetTile(previousMousePos, null); // Remove old hoverTile
    28.             interactiveMap.SetTile(mousePos, hoverTile);
    29.             previousMousePos = mousePos;
    30.         }
    31.  
    32.         if (Input.GetMouseButton(0))  {
    33.             interactiveMap.SetTile(mousePos, hoverTile);
    34.         }
    35.  
    36.         if (Input.GetMouseButton(1)) {
    37.             interactiveMap.SetTile(mousePos, null);
    38.         }
    39.  
    40.     }
    41.  
    42.     Vector3Int GetMousePosition() {
    43.         Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    44.         return grid.WorldToCell(mouseWorldPos);
    45.     }
    46.  
    47. }
    The code is currently redundant (using both mouse position and click) as I'm still just trying to get something to work. Also, I had to change the hoverTile type from Tile to TileBase, as I got a type conversion error when using SetTile.

    And for reference, here's what my setup looks like:

    Screenshot (2114).png

    Right now, if I run the game, no matter whether I hover over the tiles or click on them, it does nothing. Any help or suggestions is appreciated. Also, let me know what other information might be needed to solve this problem.

    Thanks in advance.
     
  2. vonchor

    vonchor

    Joined:
    Jun 30, 2009
    Posts:
    238
    the mouse position isn't the same as the tilemap 'grid' position. the mouse position is in screen coordinates.
    Here's an example of how to do it from my own code

    Code (CSharp):
    1. using TilePlus;
    2. using UnityEngine;
    3. using UnityEngine.Tilemaps;
    4.  
    5. namespace TilePlusDemo
    6. {
    7.     /// <summary>
    8.     /// An example of how to control a tile from a mouse-click.
    9.     /// </summary>
    10.     public class TpPickTile : MonoBehaviour
    11.     {
    12.         /// <summary>
    13.         ///The tilemap
    14.         /// </summary>
    15.         [Tooltip("The tilemap")]
    16.         public Tilemap m_Tilemap;
    17.  
    18.         /// <summary>
    19.         /// The camera
    20.         /// </summary>
    21.         [Tooltip("The camera")]
    22.         public Camera m_Camera;
    23.  
    24.         /// <summary>
    25.         /// Delay between moves used for debouncing
    26.         /// </summary>
    27.         [Tooltip("Delay between moves for debouncing, min=0.2")]
    28.         public float m_Delay = 0.2f;
    29.  
    30.         //used for debouncing
    31.         private float lastTime;
    32.  
    33.         //used for debouncing
    34.         private float timeAccum;
    35.  
    36.         private void Update()
    37.         {
    38.             // +debounce
    39.             timeAccum += Time.deltaTime;
    40.  
    41.             if (!Input.GetMouseButton(0))
    42.                 return;
    43.  
    44.             if (m_Delay < 0.2f)
    45.                 m_Delay = 0.2f;
    46.             if (timeAccum < (lastTime + m_Delay))
    47.                 return;
    48.             lastTime = timeAccum;
    49.  
    50.             // -debounce
    51.            
    52.            
    53.             //get the mouse position
    54.             var screenPos = Input.mousePosition;
    55.             //test to ensure that it's within the visible area
    56.             if (screenPos.x < 0 ||
    57.                 screenPos.y < 0 ||
    58.                 screenPos.x > Screen.width ||
    59.                 screenPos.y > Screen.height)
    60.                 return;
    61.            
    62.             //get the tilemap grid position
    63.             var worldPos = m_Camera.ScreenToWorldPoint(screenPos);
    64.             var gridPos = m_Tilemap.WorldToCell(worldPos);
    65.            
    66.             /*
    67.             Your code goes here - gridpos is the tile coordinate
    68.  
    69.             */
    70.  
    71.         }
    72.     }
    73. }
    74.  
    Hope this helps.
     
    pjlouis90 likes this.
  3. pjlouis90

    pjlouis90

    Joined:
    Aug 14, 2022
    Posts:
    2
    Thanks. This worked, although for some reason I had to add +10 to the Z coordinate of each grid position. Any ideas as to why this was needed? Not really necessary to know, but it'd be helpful for future reference.

    Here's my (now working) code:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Tilemaps;
    3.  
    4. public class GridController : MonoBehaviour {
    5.  
    6.     [SerializeField] private Tilemap m_Tilemap = null;
    7.     [SerializeField] private Camera m_Camera = null;
    8.     [SerializeField] private TileBase selectedTile = null;
    9.  
    10.     private Vector3Int previousGridPos = new Vector3Int();
    11.  
    12.     /// <summary>
    13.     /// Delay between moves used for debouncing
    14.     /// </summary>
    15.     public float m_Delay = 0.2f;
    16.  
    17.     //used for debouncing
    18.     private float lastTime;
    19.  
    20.     //used for debouncing
    21.     private float timeAccum;
    22.  
    23.     void Update()
    24.     {
    25.  
    26.         // +debounce
    27.         timeAccum += Time.deltaTime;
    28.  
    29.         if (!Input.GetMouseButton(0))
    30.             return;
    31.  
    32.         if (m_Delay < 0.2f)
    33.             m_Delay = 0.2f;
    34.         if (timeAccum < (lastTime + m_Delay))
    35.             return;
    36.         lastTime = timeAccum;
    37.  
    38.         // -debounce
    39.  
    40.         //get the mouse position
    41.         var screenPos = Input.mousePosition;
    42.         //test to ensure that it's within the visible area
    43.         if (screenPos.x < 0 ||
    44.             screenPos.y < 0 ||
    45.             screenPos.x > Screen.width ||
    46.             screenPos.y > Screen.height) {
    47.             return;
    48.         }
    49.  
    50.         var worldPos = m_Camera.ScreenToWorldPoint(screenPos);
    51.         var gridPos = m_Tilemap.WorldToCell(worldPos);
    52.  
    53.         //test to ensure that it's within the visible area
    54.         if (screenPos.x < 0 ||
    55.             screenPos.y < 0 ||
    56.             screenPos.x > Screen.width ||
    57.             screenPos.y > Screen.height)
    58.             return;
    59.  
    60.         if (!gridPos.Equals(previousGridPos))
    61.         {
    62.             var adjPos = gridPos + new Vector3Int(0, 0, 10);
    63.             var adjPrev = previousGridPos + new Vector3Int(0, 0, 10);
    64.             Debug.Log("gridPos: " + adjPos);
    65.             Debug.Log("previousGridPos: " + adjPrev);
    66.             m_Tilemap.SetTile(adjPrev, null); // Remove old tile
    67.             m_Tilemap.SetTile(adjPos, selectedTile);
    68.             previousGridPos = gridPos;
    69.         }
    70.     }
    71. }
    72.  
     
  4. vonchor

    vonchor

    Joined:
    Jun 30, 2009
    Posts:
    238
    One thing - you don't need this twice, although it shouldn't hurt anything


    //test to ensure that it's within the visible area
    if (screenPos.x < 0 ||
    screenPos.y < 0 ||
    screenPos.x > Screen.width ||
    screenPos.y > Screen.height)
    return;



    the only reason I can think of for requiring the offset is that you're using a custom orientation or Tile Anchor. If a normal XY tilemap's Z position is 0 and there's no custom orientation or tile anchor then the Z parameter should be zero.
     
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,622
    If the cells are in zero Z then reset the Z i.e. "worldPos.z = 0" before converting to tile-space.
     
    pjlouis90 likes this.
  6. vonchor

    vonchor

    Joined:
    Jun 30, 2009
    Posts:
    238
    I should have thought of that one... the way I'm using it the Z doesn't matter (not using Tilemap.GetTile). My bad :-(
     
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,622
    It's easily overlooked just as are things like finding the distance between two Vector3 then normalising and putting in a Vector2 and wondering why it's wrong.

    It's probably worth always using a Vector2Int if all the cells exist in the logical position Z=0. There's an implicit conversion operator from Vector3Int I believe.