Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
  • Help Room /
avatar image
1
Question by SerialXym · Sep 09, 2015 at 02:06 PM · pathfindingalgorithmhighlightpathsdijkstra

How do I highlight all available paths with Dijkstra's algorithm on a tile based map?

When I click on a character I want to see how far it can move on a square tile based map, with x amount of movement points that it has got (like Fire Emblem games). 1 movement point allows you to move one tile to the right, left, top or bottom.

I can highlight any tile I want, it's simple, but the problem is that I can't manage to create a list/array of all tile positions that the character can move on for me to highlight them all. I could do it if all characters had a constant amount movement points, but I feel that my game would benefit from having different units with different amounts of movement points.

My map has a graph and every tile has a list of other tile positions it is connected to. I tried making a function which finds those neighboring tiles, but it gets a bit crazy when you try to find the neighboring tiles of the neighboring tiles that I've found before and it saves the same tile positions more than once and it felt like there had to be a much more efficient way to do this.

On top of that, I would like it to take into account the cost to enter the tile ( e.g. swamp terrain costs 2 movement points to enter), but I feel like I'll have to drop this.

Any help would be appreciated.

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

2 Replies

· Add your reply
  • Sort: 
avatar image
3
Best Answer

Answer by cjdev · Sep 09, 2015 at 08:07 PM

You're on the right track with the tile objects and the graph of connecting tiles but I have a feeling you're making the function harder than it needs to be. In a case like this where you need to chain through a relationship of child to parent in succession recursive functions are often the way to go. Assuming you have a Tile class that looks like this:

 public class Tile {
     public int moveCost;
     public List<Tile> connected;
 }

Then you can implement a recursive function to get the connected Tiles like this:

 private List<Tile> validMoves;
 
 void Start()
 {
     validMoves.Clear();
     //Tiles initialized and startTile is the current Tile
     GetValidMoves(startTile, 42);
 }
 
 private void GetValidMoves(Tile startTile, int movePoints)
 {
     validMoves.Add(startTile);
     for (int i = 0; i < startTile.connected.Count; i++)
     {
         int nextMoveCost = movePoints - startTile.connected[i].moveCost
         if (nextMoveCost >= 0 && !validMoves.Contains(startTile.connected[i]))
             GetValidMoves(startTile.connected[i], nextMoveCost);
     }
 }
Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image AceVanCleef · Jul 17, 2018 at 12:31 AM 0
Share

While working on my own tile map based project, I noticed an issue in this suggested solution. "!valid$$anonymous$$oves.Contains(startTile.connected[i]" prevents the code from aquiring all valid$$anonymous$$oves. For example, the root tile R has its neighbours A (top), B (right), C (left) and D (bottom/below) around it, the algorithm will first go to A. A's neighbours will be now added. I call them A_1 to the right, A_2 to the top and A_3 to the left (A_4 is R and already included in validTiles). Sooner or later, A_3's neighbour will be processed. It's neighbour below is actually Tile C. Now because you check "!valid$$anonymous$$oves.Contains(startTile.connected[i])"), C can no longer be used to check for its own neighbours. The path from R to all its left neighbour is blocked.

I suggest to draw a 5x5 array on paper and emulate my steps I explained. This will help visualizing the issue.

Technically, you can easily fix this by removing "&& !valid$$anonymous$$oves.Contains(startTile.connected[i]" from the code, but this comes with the downside of adding duplicates to "List"valid$$anonymous$$oves. I suggest using the Breath first search algorithm.

avatar image
0

Answer by AceVanCleef · Jul 17, 2018 at 02:08 AM

I implemented a solution using Breadth First Search (BFS) in order to avoid the following two issues of the previous suggested solution:

  • !validMoves.Contains(startTile.connected[i] prevented from visiting all valid tiles a unit should be able to move onto.

  • After removing !validMoves.Contains(startTile.connected[i], tiles get visitied multiple times and duplicates will be added to List<Tile> validMoves.

    BFS Concept

Before showing my solution, I recommend watching this video explaining the concept of BFS in a short and simple manner: Breadth First Search Algorithm

Get all tiles in a unit's walking range

Units have a certain amount of movePoints they can use to walk around. Tiles in the otherhand have topographical features such as grassland, forests and mountains. A forest is more expensive to walk on ( MovementCost ) than grassland. A mountain even more thus influencing how far a unit can actually walk.

 public List<Node> GetValidMoves(int startPosX, int startPosY, int movePoints)
     {
         List<Node> validTiles = new List<Node>();
         int[,] distance = new int[BoardSizeX, BoardSizeY];
         for(int x = 0; x < BoardSizeX; ++x) {
             for (int y = 0; y < BoardSizeY; ++y) {
                 distance[x, y] = Int32.MaxValue;
             }
         }
         distance[startPosX, startPosY] = 0;
         Queue<Node> queue = new Queue<Node>();
         queue.Enqueue( graph[startPosX, startPosY] );
         while (queue.Count > 0) {
             Node current = queue.Dequeue();
             foreach(Node neighbour in current.Neighbours) {
                 if( distance[neighbour.x , neighbour.y] > movePoints) {
                     int movementCost = GetTileTypeAt(neighbour.x , neighbour.y).MovementCost;
                     distance[neighbour.x , neighbour.y] = movementCost + distance[current.x, current.y];
                     if (distance[neighbour.x , neighbour.y] <= movePoints) {
                         queue.Enqueue(neighbour);
                     }
                 }
             }
             if (distance[current.x, current.y] > 0 && distance[current.x, current.y] <= movePoints) {
                 validTiles.Add(current);
             }
         }
 
         return validTiles;
     }


Attack Range

Here is a variation for getting all tiles that are attackable within a units maxAttackRange

 public List<Node> GetTilesInAttackRange(int startPosX, int startPosY, int maxAttackRange, int minAttackRange)
     {
         if (minAttackRange < 0) {
             minAttackRange = 0;
             Debug.LogError("minAttackRange was below 0 and got corrected to 0");
         }
         if (minAttackRange >= maxAttackRange) {
             throw new System.ArgumentException("MinAttackRange can't be larger than or equal to MaxAttackRange.");
         }
 
         List<Node> validTiles = new List<Node>();
         int[,] distance = new int[BoardSizeX, BoardSizeY];
         for(int x = 0; x < BoardSizeX; ++x) {
             for (int y = 0; y < BoardSizeY; ++y) {
                 distance[x, y] = Int32.MaxValue;
             }
         }
         distance[startPosX, startPosY] = 0;
         Queue<Node> queue = new Queue<Node>();
         queue.Enqueue( graph[startPosX, startPosY] );
         while (queue.Count > 0) {
             Node current = queue.Dequeue();
             foreach(Node neighbour in current.Neighbours) {
                 if( distance[neighbour.x , neighbour.y] == Int32.MaxValue) {
                     distance[neighbour.x , neighbour.y] = 1 + distance[current.x, current.y];
                     if (distance[neighbour.x, neighbour.y] <= maxAttackRange) {
                         queue.Enqueue(neighbour);
                     }
                 }
             }
             if (distance[current.x, current.y] > minAttackRange && distance[current.x, current.y] <= maxAttackRange) {
                 validTiles.Add(current);
             }
         }
 
         return validTiles;
     }    

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image LeGingerGamer · Jun 14, 2019 at 12:01 PM 0
Share

I have a question. If I wanted to limit this code so that moving diagonally would be 2 move points. How would I go about doing that?

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

5 People are following this question.

avatar image avatar image avatar image avatar image avatar image

Related Questions

Making the AI to reach the destination without a Navmesh? 0 Answers

Movement following the A* algorithm 0 Answers

How to steer character by path using Root Motion? 0 Answers

Enemy can't move follow the path, A* algorithm for my game 0 Answers

Is there a way to define rodas in unity for AI movement? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges