Raycasthit2d and tilemap collider 2D

Hello,

Apologies for having to ask this question but I am horrifically stuck with this.

I am doing a course and it is using a version of unity without the tile palette, I am assuming this is what is causing the problem but am too new to figure out what is happening. So I am trying to place a tower and the raycasthit2d transform position is returning 0.0,0.0,0.0 as opposed to the mouse location. The worldPoint is returning correct values but the hit.transform.position is returning rubbish. Please see my code below. Any help with this would be greatly appreciated. Thanks.

void Update () 
{
        if (Input.GetMouseButtonDown(0))
        {
            Vector2 worldPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            Debug.Log(worldPoint);
            RaycastHit2D hit = Physics2D.Raycast(worldPoint, Vector2.zero);
            Debug.Log(hit.transform.position);
            if (hit.collider.tag == "BuildSite")
            {
                PlaceTower(hit);
            }
        }
}

public void PlaceTower(RaycastHit2D hit)
{
    if (!EventSystem.current.IsPointerOverGameObject() && towerBtnPressed != null)
    {
    GameObject newTower = Instantiate (towerBtnPressed.TowerObject);
    newTower.transform.position = hit.transform.position;
    }

}

Hey OP. You probably have this figured out by now, but for anyone searching here are some suggestions.

Firstly, use hit.point to get the Vector2 result of the hit. You can save this into a variable and then pass that to the PlaceTower method instead of the RayCastHit2D.

For example. Make a variable called:

Vector2 buildPoint;

Then inside the mouse input statement:

//after the raycast
buildPoint = hit.point;

if (hit.collider.tag.Equals("BuildSite"))
{
      PlaceTower(buildPoint);
}

public void PlaceTower(Vector2 hit)

Another suggestion would be to make a new layer called BuildSite or something and place all your BuildSites into that layer.

Then at the start of this class place a reference to that layer.
You will need to get the integer of the BuildSite layermask, or alternatively you could just make it public and use the inspector to select the correct layermask. Both ways are below, ive used 8 as an example of the layermask integer.

LayerMask buildSite = 8;

public LayerMask buildSite;

Then in your update method:

if (Input.GetMouseButtonDown(0))
{
      Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
      RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction);
    
      //this hit.point should give you the same Vector2 results as worldPoint variable
      Debug.Log(hit.point);
      buildPoint = hit.point;
    
      //now you can check if the ray hit a collider on a certain layer 
      if (hit.collider.gameObject.layer == buildSite)
      {
           PlaceTower(buildPoint);
      }
}

It returns 0.0,0.0,0.0 since that is the position of the entire tilemap. You need to get the reference of the tilemap in order to calculate the position of the tile clicked. To do that we just need to get the tilemap component of the clicked/collided map then use WorldToCell and GetCellCenterWorld function.

void Update() {
    if (Input.GetMouseButtonDown(0)) {
        raycastTilemap();
    }
}

private void raycastTilemap() {
    Vector2 worldPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    RaycastHit2D hit = Physics2D.Raycast(worldPoint, Vector2.zero);

    if (hit.collider != null && hit.collider.TryGetComponent(out TilemapCollider2D tilemapCollider)) {
        Tilemap tilemap = tilemapCollider.GetComponent<Tilemap>();

        Vector3Int cellPosition = tilemap.WorldToCell(hit.point);
        Vector2 cellCenter = tilemap.GetCellCenterWorld(cellPosition);

        placeTower(cellCenter);
    }
}

public void placeTower(Vector2 position) {
    if (EventSystem.current.IsPointerOverGameObject() || towerBtnPressed == null) {
        return;
    }

    GameObject newTower = Instantiate(towerBtnPressed.TowerObject);
    newTower.transform.position = position;
}

PS: As you progress through the course, you’ll eventually need to implement individual box colliders for build tiles. I suggest using the tilemap for design purposes only and adding separate box colliders for collision detection.