- Home /
Board-game pathfinding system (Similar to Mario Party style)
Hey all. I’m currently working on a board-game style 3D Unity game (a very similar concept to Mario Party) and struggling to find an efficient way of setting up waypoints for pathfinding. I already have a method set up, however, there are some issues and I feel it’s not too efficient. Here is my current board (or more test room I would call it)…
Each of the red circles are spaces and connect to each other depending on what’s either side. Players will start at space 0, and move along the board. Unlike traditional Mario Party though, the player can go backwards as long as they don’t make a full 180 degree turn. For example, after exiting space 0 and on to space 1, players can go to space 22 instead of space 2. I have a Waypoint script attached to each one of these spaces. The script contains a list variable with every space number that attaches to that specific space. In this image is space 7, with the attached waypoints being 6, 8, and 23. Here is the current script used to move each player around the board based on their dice roll.
public GameMaster _GameMaster;
public PlayerStats _Stats;
public Transform _WaypointsGO;
public List<Waypoint> _Waypoints;
public Waypoint _PreviousWaypoint;
public Waypoint _CurrentWaypoint;
public int _MoveSpaces;
public string _CurrentDirection;
public NavMeshAgent _PlayerAI;
public bool _ChooseDirection;
private List<DirectionalArrow> _CurrentArrowSet = new List<DirectionalArrow>();
private DirectionalArrow _CurrentArrow;
private void Start()
{
_Stats = GetComponent<PlayerStats>();
_CurrentDirection = "";
_PreviousWaypoint = null;
_ChooseDirection = false;
foreach(Transform waypoint in _WaypointsGO)
{
_Waypoints.Add(waypoint.GetComponent<Waypoint>());
if(waypoint.name == "0")
{
_CurrentWaypoint = waypoint.GetComponent<Waypoint>();
}
}
_PlayerAI.Warp(new Vector3(_GameMaster._PlayerSpawns[_Stats._PlayerNo - 1].transform.position.x, _GameMaster._PlayerSpawns[_Stats._PlayerNo - 1].transform.position.y + 0.8f, _GameMaster._PlayerSpawns[_Stats._PlayerNo - 1].transform.position.z));
}
public void GoToFirstSpace()
{
_PlayerAI.SetDestination(new Vector3(_CurrentWaypoint.transform.position.x, _CurrentWaypoint.transform.position.y + 0.8f, _CurrentWaypoint.transform.position.z));
}
private void Update()
{
if (GameMaster._CurrentPlayerNo != _Stats._PlayerNo)
return;
if(Input.GetKeyDown(KeyCode.Space))
{
RollDice();
}
}
private void RollDice()
{
_MoveSpaces = Random.Range(1, 10 + 1);
Debug.Log("You rolled a " + _MoveSpaces);
StartCoroutine(MovePlayer());
}
IEnumerator MovePlayer()
{
int spacesMoved = 0;
while(spacesMoved < _MoveSpaces)
{
_CurrentArrow = null;
int currentWaypoint = int.Parse(_CurrentWaypoint.name);
int nextWaypoint = 0;
int directionChosen = 1;
if (_CurrentWaypoint._AmountOfPaths > 2 || (_CurrentWaypoint._AmountOfPaths > 1 && !_CurrentWaypoint._Waypoints.Contains(int.Parse(_PreviousWaypoint.gameObject.name)))) //|| (_PreviousWaypoint == null && _CurrentWaypoint._AmountOfPaths > 1))
{
foreach(Transform set in _GameMaster._DirectionalArrowSets)
{
if(set.name == _CurrentWaypoint._ArrowSetID.ToString())
{
set.gameObject.SetActive(true);
_CurrentArrowSet.Clear();
int activeArrows = 0;
foreach (Transform arrow in set)
{
if (arrow.name != _PreviousWaypoint.name)
{
_CurrentArrowSet.Add(arrow.GetComponent<DirectionalArrow>());
_CurrentArrowSet[_CurrentArrowSet.Count - 1]._Priority = activeArrows;
activeArrows++;
arrow.gameObject.SetActive(true);
}
else
{
arrow.gameObject.SetActive(false);
}
}
}
}
directionChosen = 0;
_ChooseDirection = true;
int priority = 0;
foreach (DirectionalArrow arrow in _CurrentArrowSet)
{
if (arrow._Priority == priority)
{
arrow._Anim.SetBool("IsSelected", true);
_CurrentArrow = arrow;
}
else
{
arrow._Anim.SetBool("IsSelected", false);
}
}
bool hasChosenDirection = false;
while(hasChosenDirection == false)
{
if (Input.GetKeyDown(KeyCode.S))
{
priority = _CurrentArrow._Priority;
priority++;
if (priority > _CurrentArrowSet.Count - 1)
{
priority = 0;
}
foreach (DirectionalArrow arrow in _CurrentArrowSet)
{
if (arrow._Priority == priority)
{
arrow._Anim.SetBool("IsSelected", true);
_CurrentArrow = arrow;
}
else
{
arrow._Anim.SetBool("IsSelected", false);
}
}
}
if(Input.GetKeyDown(KeyCode.Alpha1))
{
hasChosenDirection = true;
}
yield return null;
}
foreach (Transform set in _GameMaster._DirectionalArrowSets)
{
set.gameObject.SetActive(false);
}
_ChooseDirection = false;
}
if(_CurrentArrow == null)
{
List<int> temp2 = new List<int>(_CurrentWaypoint._Waypoints);
if (_PreviousWaypoint != null)
{
temp2.Remove(int.Parse(_PreviousWaypoint.gameObject.name));
}
nextWaypoint = temp2[directionChosen - 1];
} else
{
nextWaypoint = int.Parse(_CurrentArrow.name);
}
_PreviousWaypoint = _CurrentWaypoint;
foreach (Waypoint w in _Waypoints)
{
if(w.gameObject.name == nextWaypoint.ToString())
{
_CurrentWaypoint = w;
}
}
_PlayerAI.SetDestination(new Vector3(_CurrentWaypoint.transform.position.x, _CurrentWaypoint.transform.position.y + 0.8f, _CurrentWaypoint.transform.position.z));
spacesMoved++;
yield return new WaitForSeconds(0.2f);
while(_PlayerAI.remainingDistance > 1.1f)
{
yield return null;
}
}
if(_CurrentWaypoint._SpaceType == "GainCoins")
{
_Stats._Coins = _Stats._Coins + 3;
} else if (_CurrentWaypoint._SpaceType == "LoseCoins")
{
_Stats._Coins = _Stats._Coins - 3;
if(_Stats._Coins < 0)
{
_Stats._Coins = 0;
}
}
GameMaster._CurrentPlayer++;
if(GameMaster._CurrentPlayer > GameMaster._AmountOfPlayers)
{
GameMaster._CurrentPlayer = 1;
}
GameMaster._CurrentPlayerNo = _GameMaster._PlayerOrder[GameMaster._CurrentPlayer - 1];
}
If the player gets to a space with two or more separate ways, arrows will appear allowing the user to choose a direction. As I say, everything works great at the moment, but I feel having to go manually input the neighbor waypoints for each waypoint is a bit on the inefficient side, and was trying to find a better solution. Most importantly, I need a method that will tell me exactly how many spaces away a particular point is from a player, and I have no idea how to go about that for this current method. For example, if a star is on space 11 and a player on space 27, how will the game know how many spaces the star is away with all those different routes? If anyone has any ideas please let me know! Thanks all.
Answer by Feldspatt · May 15, 2021 at 01:59 PM
Concerning the finding of available routes: If the disposition of waypoints is -and from the picture it seems so- along a grid, you can add a location property to your waypoints. You make a list with all your waypoints.
Then, depending of the current location and direction of the player you find in the list the three available locations, for example if he is facing top, find in list waypoint where location = (current.x, current.y +1).
For the shortest path to a location, i dont know if the pathfinding unity solution can return you that, but you can implement the djikstra algorythm or the bit more sophisticated a* algorythm quite easily. Plus it willworks easily with a grid system.
Hey thanks for the answer! Really sorry for getting back so late, I hate when I take the time to answer people's questions and they never get back to me, and I just did just that lol.
I've actually found a solution myself using a similar approch to what I was already doing, but instead using gameobjects instead of an array. So there would be a gameobject called '1' for waypoint 1, and two children of that object '2' and '22' representing the neighbours.
I was thinking about your idea before making this post but the problem with that is there may be waypoints much further away, such as there could be a route leading the player from one side of the map to the other, and obviously that destination waypoint wouldn't be close to the player's current position at all. At least I think that's what you mean by your solution.
Thanks for the help anyway!