2D Tilemap, Move AI to Selected Tile

I have a scene in which there’s a 12x7 grid of tiles. I want the character I have selected to move to the tile I right click. My obstacle right now is that I cannot seem to get the code right to return the tiles transform.position to the AI I want to move.

This is the code on the controllerScript:

    public Vector2 GetSquareClicked() //Finds mouse click position
    {
        Vector2 clickPosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
        Vector2 worldPosition = Camera.main.ScreenToWorldPoint(clickPosition);
        Vector2 gridPosition = SnapToGrid(worldPosition);

        return gridPosition;
    }

    public Vector2 SnapToGrid(Vector2 worldPosition) //Rounds mouse position to nearest int
    {
        float newX = Mathf.RoundToInt(worldPosition.x);
        float newY = Mathf.RoundToInt(worldPosition.y);

        return new Vector2(newX, newY);
    }

I want to return the position of the tile to the AI script which is this:

    public void Movement()
    {
        Vector2 tilePosition = GetComponent<controlScript>().GetSquareClicked();
        transform.position = Vector2.MoveTowards(transform.position, tilePosition, speed * Time.deltaTime);
      
    }

This returns as “object not set to reference”, so I’m assuming I’m not defining something correctly.

Please add the complete error message. That makes it easier to help you out and gives everyone a clue where the error occurs/is produced.

NullReferenceException: Object reference not set to an instance of an object
playerScript.Movement () (at Assets/Scripts/playerScript.cs:25)
controlScript.PlayerInput () (at Assets/Scripts/controlScript.cs:54)
controlScript.Update () (at Assets/Scripts/controlScript.cs:21)

Alright. So I’m assuming line 25 in your playerScript is
Vector2 tilePosition = GetComponent<controlScript>().GetSquareClicked();
which would mean that your GetComponent results in the exception.

Where do you call the Movement () method from? Remember that GetComponent() only finds components on the very same GameObject the script that’s making the call is attached to. I guess, however, that your controlScript is attached to the tile GameObject so you’d need a reference to that first. That’s just a wild guess, though. If that doesn’t solve the problem, post the complete scripts please. Makes it easier to get the bigger picture/context.:slight_smile:

That’s exactly what that line is!

I call movement() from controlScript.PlayerInput () (at Assets/Scripts/controlScript.cs:54). Basically I wrote “right click = movement()”.

I didn’t know that! Or to be more clear, I thought it could at least call other scripts in code without the scripts being attached.

Here’s the current control script, which is just attached to an empty object:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class controlScript : MonoBehaviour
{

    public GameObject selectedCharacter;
    public bool characterSelected;
    public playerScript playerScript;

    // Start is called before the first frame update
    void Start()
    {
       
    }

    // Update is called once per frame
    void Update()
    {
        PlayerInput();

        if (Input.GetMouseButtonDown(0))
        {
            ClickSelect();
        }
    }


    public GameObject ClickSelect()
    {
        int raycastLayers = LayerMask.GetMask("Player", "Tool");

        //Converting Mouse Pos to 2D (vector2) World Pos
        Vector2 rayPos = new Vector2(Camera.main.ScreenToWorldPoint(Input.mousePosition).x, Camera.main.ScreenToWorldPoint(Input.mousePosition).y);
        RaycastHit2D hit = Physics2D.Raycast(rayPos, Vector2.zero, 0f, raycastLayers);

        if (hit)
        {
            Debug.Log(hit.transform.name + " selected");
            selectedCharacter = hit.transform.gameObject;
            characterSelected = true;
            return hit.transform.gameObject;
        }
       
        else return null;
    }

    public void PlayerInput()
    {
        if (Input.GetMouseButtonDown(1))
        {
            playerScript.Movement();
        }
    }

    public Vector2 GetSquareClicked() //Finds mouse click position
    {
        Vector2 clickPosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
        Vector2 worldPosition = Camera.main.ScreenToWorldPoint(clickPosition);
        Vector2 gridPosition = SnapToGrid(worldPosition);

        return gridPosition;
    }

    public Vector2 SnapToGrid(Vector2 worldPosition) //Rounds mouse position to nearest int
    {
        float newX = Mathf.RoundToInt(worldPosition.x);
        float newY = Mathf.RoundToInt(worldPosition.y);

        return new Vector2(newX, newY);
    }


}

Then here’s the AI code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class playerScript : MonoBehaviour
{
    [SerializeField] float speed;

    Rigidbody2D rb;

    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    void Update()
    {
       
    }

    public void Movement()
    {
        Vector2 tilePosition = GetComponent<controlScript>().GetSquareClicked();
        GameObject selected = GetComponent<controlScript>().selectedCharacter;

        transform.position = Vector2.MoveTowards(transform.position, tilePosition, speed * Time.deltaTime);
      
    }
}

I don’t have any code attached to the tile for this function. I assumed if I could just grab it’s transform, I wouldn’t need any script on the tile.

(I know it’s bare and messy, it’s a new project I’ve started on lol)

That’s correct, you can absolutely do that. In that case, I’d suggest you take a quick look at Unity’s Raycast API. Call that and use the RaycastHit to get the transform.