Objective:
Game Type: 2D Top down tile based
I’m trying to use ConstantPath in A* Pathfinding Project Pro to make it so I can preview how many tiles my player can move to. After movement of the player or if there was not a lastPath set it runs a ConstantPath to return a ConstantPath with a max range of 4 (just a small range for testing). When it gets the ConstantPath I have sprites spawned in the game at each of the nodes returned by the ConstantPath (e.g. looping through constPath.allNodes). What I am aiming for is that I can click the mouse and have it allow or disallow pathing to the selected point based on if the mouse click is on a node returned by the ConstantPath. Code for my class is at the end.
Problem:
The ConstantPath runs and I can spawn objects (preview “Tiles”) on each of the constant path’s nodes and they appear in game seemingly OK, but in the game window while using Gizmos, it displays the navmesh as having an extra layer of adjacent nodes (E.g. if I’m expecting 4 nodes to the right, i have 4 preview spawned tiles to the right and one more after that displayed by the gizmo showing it is a navmesh). Sometimes when I click in the ‘navmesh’ area displayed in the gizmo (that doest have a preview tile on it) the distance checking I am doing says it is a valid node. The distance checking is looping through the last path returned by the last ran ConstantPath and checking if the distance between each node in the constant path and the mouse click is < 1. This seems OK for everything that is a ‘preview tile’ that was spawned but sometimes it lets you click outside of it just by 1 node.
I feel like I’m probably missing something obvious. Thanks for the help/input.
What my scene looks like with the ‘preview tiles’ around the player and the navmesh gizmo:
Gif of the problem (External link, sorry, i dont know if gifs will upload and if this is against policy, i will redo it): https://imgur.com/a/fxNXr
Screenshot of my AStartPath Inspector:
Screenshot of my “player” inspector:
My class code, Sorry if it doesnt come out formatted but I didnt see a code option in the formatting:
using System.Collections;
using System.Collections.Generic;
using Pathfinding;
using UnityEditor.Graphs;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Networking;
public class PathSelector : NetworkBehaviour
{
[SerializeField]
private GameObject highlightTile;
[SerializeField]
private GameObject targetHighlightTile;
[SerializeField]
private GameObject highlightInvalidTile;
[SerializeField]
private LayerMask blockingLayer;
private PlayerMovement playerMovement;
private GridLayout gridLayout;
private Vector3 cellSize;
private Seeker seeker;
private EventSystem eventSystem;
private Vector3Int previousHighlightedPos;
private Vector3Int previousMousePos;
private List<GameObject> previewTiles = new List<GameObject>();
private ConstantPath lastPath;
void Start ()
{
eventSystem = GameObject.Find("EventSystem").GetComponent<EventSystem>();
playerMovement = gameObject.GetComponent<PlayerMovement>();
seeker = GetComponent<Seeker>();
gridLayout = GameObject.Find("Grid").GetComponent<Grid>();
cellSize = gridLayout.cellSize;
playerMovement.OnPlayerStopMovement += OnPlayerStopMovement;
playerMovement.OnPlayerStartMovement += OnPlayerStartMovement;
}
void Awake()
{
}
void OnDisable()
{
playerMovement.OnPlayerStopMovement -= OnPlayerStopMovement;
playerMovement.OnPlayerStartMovement -= OnPlayerStartMovement;
}
#region Event Handlers
private void OnPlayerStopMovement(Vector3 stopPosition)
{
ClearPreview();
StartCoroutine(PreviewPath(transform.position));
}
private void OnPlayerStartMovement(Vector3 startPosition, Vector3 endPosition)
{
ClearPreview();
}
#endregion
void FixedUpdate ()
{
if (!isLocalPlayer)
{
return;
}
Vector3 mouseTarget = Camera.main.ScreenToWorldPoint(Input.mousePosition);
// Get cell mouse clicked on
Vector3Int mouseGridPos = gridLayout.WorldToCell(mouseTarget);
Vector3 mouseGridPosWorld = gridLayout.CellToWorld(mouseGridPos);
mouseGridPosWorld += new Vector3(cellSize.x / 2, cellSize.y / 2);
if (lastPath == null)
{
StartCoroutine(PreviewPath(transform.position));
}
if (gridLayout
&& Input.GetMouseButtonDown(0)
&& !playerMovement.isMoving
&& !eventSystem.IsPointerOverGameObject()
&& Utils.IsOnScreen(Input.mousePosition))
{
Vector3Int playerGridPos = gridLayout.WorldToCell(transform.position);
Vector3Int targetGridPos = playerGridPos;
if (playerGridPos != mouseGridPos)
{
targetGridPos = mouseGridPos;
var target = gridLayout.CellToWorld(targetGridPos);
target += new Vector3(cellSize.x / 2, cellSize.y / 2);
if (IsPathValid(target))
{
playerMovement.StartMovement(target);
}
}
}
}
IEnumerator PreviewPath(Vector3 start)
{
int maxMoves = playerMovement.MaxMoves; // This is 4, since i want to be able to move 4 'tiles'
ConstantPath constPath = ConstantPath.Construct(start, 1000 * maxMoves, null);
AstarPath.StartPath(constPath);
lastPath = constPath;
// Wait for the path to be calculated
yield return StartCoroutine(constPath.WaitForPath());
ClearPreview();
List<GraphNode> nodes = constPath.allNodes;
for (int i = 0; i < nodes.Count; i++)
{
var vector = (Vector3)nodes[i].position;
SpawnTile(highlightTile, vector);
}
}
void ClearPreview()
{
for (int i = 0; i < previewTiles.Count; i++)
{
GameObject.Destroy(previewTiles[i]);
}
previewTiles.Clear();
}
private void SpawnTile(GameObject tile, Vector3 pos)
{
var obj = Instantiate(tile, pos, Quaternion.identity);
previewTiles.Add(obj);
}
private bool IsPathValid(Vector3 pos)
{
if (lastPath != null)
{
for (int i = 0; i < lastPath.allNodes.Count; i++)
{
var vector = (Vector3)lastPath.allNodes[i].position;
float distance = Vector3.Distance(pos, vector);
if (distance < 1.0f)
{
Debug.Log("** Click: " + pos + "; Path: " + vector + "; distance: " + distance);
return true;
}
else
{
Debug.Log("Click: " + pos + "; Path: " + vector + "; distance: " + distance);
}
}
}
return false;
}
}