Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

2D Tilemap, Move AI to Selected Tile

Discussion in 'Scripting' started by rapidcatch4464, Jan 20, 2020.

  1. rapidcatch4464

    rapidcatch4464

    Joined:
    Jan 20, 2020
    Posts:
    9
    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:

    Code (CSharp):
    1.     public Vector2 GetSquareClicked() //Finds mouse click position
    2.     {
    3.         Vector2 clickPosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
    4.         Vector2 worldPosition = Camera.main.ScreenToWorldPoint(clickPosition);
    5.         Vector2 gridPosition = SnapToGrid(worldPosition);
    6.  
    7.         return gridPosition;
    8.     }
    9.  
    10.     public Vector2 SnapToGrid(Vector2 worldPosition) //Rounds mouse position to nearest int
    11.     {
    12.         float newX = Mathf.RoundToInt(worldPosition.x);
    13.         float newY = Mathf.RoundToInt(worldPosition.y);
    14.  
    15.         return new Vector2(newX, newY);
    16.     }
    I want to return the position of the tile to the AI script which is this:
    Code (CSharp):
    1.     public void Movement()
    2.     {
    3.         Vector2 tilePosition = GetComponent<controlScript>().GetSquareClicked();
    4.         transform.position = Vector2.MoveTowards(transform.position, tilePosition, speed * Time.deltaTime);
    5.      
    6.     }
    This returns as "object not set to reference", so I'm assuming I'm not defining something correctly.
     
  2. Karrzun

    Karrzun

    Joined:
    Oct 26, 2017
    Posts:
    123
    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.
     
  3. rapidcatch4464

    rapidcatch4464

    Joined:
    Jan 20, 2020
    Posts:
    9
    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)
     
  4. Karrzun

    Karrzun

    Joined:
    Oct 26, 2017
    Posts:
    123
    Alright. So I'm assuming line 25 in your playerScript is
    Vector2 tilePosition = GetComponent<controlScript>().GetSquareClicked();

    which would mean that your GetComponent<controlScript> 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.:)
     
  5. rapidcatch4464

    rapidcatch4464

    Joined:
    Jan 20, 2020
    Posts:
    9
    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:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class controlScript : MonoBehaviour
    6. {
    7.  
    8.     public GameObject selectedCharacter;
    9.     public bool characterSelected;
    10.     public playerScript playerScript;
    11.  
    12.     // Start is called before the first frame update
    13.     void Start()
    14.     {
    15.        
    16.     }
    17.  
    18.     // Update is called once per frame
    19.     void Update()
    20.     {
    21.         PlayerInput();
    22.  
    23.         if (Input.GetMouseButtonDown(0))
    24.         {
    25.             ClickSelect();
    26.         }
    27.     }
    28.  
    29.  
    30.     public GameObject ClickSelect()
    31.     {
    32.         int raycastLayers = LayerMask.GetMask("Player", "Tool");
    33.  
    34.         //Converting Mouse Pos to 2D (vector2) World Pos
    35.         Vector2 rayPos = new Vector2(Camera.main.ScreenToWorldPoint(Input.mousePosition).x, Camera.main.ScreenToWorldPoint(Input.mousePosition).y);
    36.         RaycastHit2D hit = Physics2D.Raycast(rayPos, Vector2.zero, 0f, raycastLayers);
    37.  
    38.         if (hit)
    39.         {
    40.             Debug.Log(hit.transform.name + " selected");
    41.             selectedCharacter = hit.transform.gameObject;
    42.             characterSelected = true;
    43.             return hit.transform.gameObject;
    44.         }
    45.        
    46.         else return null;
    47.     }
    48.  
    49.     public void PlayerInput()
    50.     {
    51.         if (Input.GetMouseButtonDown(1))
    52.         {
    53.             playerScript.Movement();
    54.         }
    55.     }
    56.  
    57.     public Vector2 GetSquareClicked() //Finds mouse click position
    58.     {
    59.         Vector2 clickPosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
    60.         Vector2 worldPosition = Camera.main.ScreenToWorldPoint(clickPosition);
    61.         Vector2 gridPosition = SnapToGrid(worldPosition);
    62.  
    63.         return gridPosition;
    64.     }
    65.  
    66.     public Vector2 SnapToGrid(Vector2 worldPosition) //Rounds mouse position to nearest int
    67.     {
    68.         float newX = Mathf.RoundToInt(worldPosition.x);
    69.         float newY = Mathf.RoundToInt(worldPosition.y);
    70.  
    71.         return new Vector2(newX, newY);
    72.     }
    73.  
    74.  
    75. }
    76.  
    Then here's the AI code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class playerScript : MonoBehaviour
    6. {
    7.     [SerializeField] float speed;
    8.  
    9.     Rigidbody2D rb;
    10.  
    11.     // Start is called before the first frame update
    12.     void Start()
    13.     {
    14.         rb = GetComponent<Rigidbody2D>();
    15.     }
    16.  
    17.     // Update is called once per frame
    18.     void Update()
    19.     {
    20.        
    21.     }
    22.  
    23.     public void Movement()
    24.     {
    25.         Vector2 tilePosition = GetComponent<controlScript>().GetSquareClicked();
    26.         GameObject selected = GetComponent<controlScript>().selectedCharacter;
    27.  
    28.         transform.position = Vector2.MoveTowards(transform.position, tilePosition, speed * Time.deltaTime);
    29.      
    30.     }
    31. }
    32.  
    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)
     
  6. Karrzun

    Karrzun

    Joined:
    Oct 26, 2017
    Posts:
    123
    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.