Document directory
- The Search Area)
- Starting the Search)
- Path Sorting)
- Continue the Search)
- A * algorithm Summary (Summary of the * Method)
- Small Rant)
- Notes on Implemetation)
- Further Reading (Further Reading)
Address: http://www.gamedev.net/reference/articles/article2003.asp
Http://www.cppblog.com/christanxw/archive/2006/04/07/5126.html.
Overview
Although those who master the * algorithm think it is easy, the * algorithm is still very complicated for beginners.
The Search Area)
Let's assume that someone is moving from point A to point B, but these two points are separated by A wall. 1. Green indicates A, red indicates B, and blue indicates A wall.
Figure 1
You should note that we divide the area to be searched into square grids. This is the first step in searching for a region, just as we did here. This special method simplifies the search area to a 2-dimensional array. Each entry of an array represents a grid. Its State is walkalbe and unrecoverable ). By calculating the squares that need to be taken from A to B, the path is found. Once the path is found, the character moves from the center of a square to the center of another square until it reaches the destination.
The center of the square is "Node (nodes )". If you have read other articles about A * pathfinding algorithms, you will find that people often discuss nodes. Why not simply describe it as a square? Because we may divide the search area into other multi-deformation rather than square, such as hexagonal, rectangular, or even any multi-deformation. Nodes can be placed in any polygon, in the center of Multi-deformation, or on the side of the polygon. We use this system because it is the simplest.
Starting the Search)
Once we simplify the search area into a group of quantifiable nodes, just as we did above, what we need to do next is to find the shortest path. In A *, we start from the starting point, check the adjacent square, and then expand around until we find the target.
Let's start our journey:
1. Start from start A and add it to an open list consisting of squares. This open list is a shopping order. Of course, there is only one item in the open list, which is the starting point A. More items will be added later. The grid in the Open list may or may not pass through the path along the way. Basically, open list is a list of squares to be checked.
2. view the square adjacent to start A (ignore the square occupied by the walls, the square occupied by the river and other illegal terrain), and divide the square) or the reachable (reachable) Square is added to the open list. Set Start A as the father of these squares (parent node or parent square ). When we track paths, the contents of these parent nodes are very important. I will explain it later.
3. Remove A from the open list and add it to the close list. Every square in the close list does not need to be concerned now.
As shown in, the dark green square is the starting point, and its outer box is bright blue, indicating that the square is added to the close list. The black square adjacent to it needs to be checked, and their outer boxes are bright green. Each black square has A gray pointer pointing to their parent node. Here is the starting point.
Figure 2.
Next, we need to select A square adjacent to start A from the open list and repeat the previous steps more or less as described below. But which one should I choose? The one with the minimum F value.
Path Sorting)
The key to calculating the square of the Composition path is the following equation:
F = G + H
Here,
G = the cost of moving from start A to the specified square, along the path generated when the square is reached.
H = the estimated cost of moving from the specified square to end B. This is often called a testing method, which is a bit confusing. Why is this? It's a guess. We don't know the real distance until we find the path, because there are various things (such as walls and water) on the way ). This tutorial will teach you how to calculate H. You can also find other methods on the Internet.
Our path is generated as follows: traverse the open list repeatedly and select the square with the smallest F value. This process will be described in detail later. Let's take a look at how to calculate the equation above.
As mentioned above, G is the cost of moving from start A to the specified square. In this example, the cost of horizontal and vertical movement is 10, and the cost of diagonal movement is 14. The reason for using this data is that the actual diagonal movement distance is the square root of 2, or the approximate 1.414 times the cost of horizontal or vertical movement. 10 and 14 are used for simplicity. The ratio is correct, so we can avoid the calculation of opening and decimal places. This is not because we do not have this ability or do not like mathematics. Using these numbers can also make the computer faster. Later, you will find that if you do not use these techniques, the path searching algorithm will be slow.
Since we calculate the G value along the path to the specified Square, the method to calculate the G value of the square is to find out the G value of its father, then add 10 or 14 to the parent line or diagonal line. This method becomes clearer as we get more squares from the starting point.
There are many ways to estimate the H value. Here we use the Manhattan method to calculate the number of squares that move horizontally or vertically from the current square to the target, ignore diagonal movement, and multiply the total number by 10. The Manhattan method is called because it is similar to counting the number of blocks that pass through from one location to another, and you cannot cross the blocks diagonally. It is important to ignore obstacles in the path when calculating H. This is an estimate of the remaining distance, not the actual value. Therefore, it is called a test method.
Add G and H to obtain F. The result of step 1 is shown in. Each square is marked with the values of F, G, and H, just like the square on the right of the start point. The upper left corner is F, the lower left is G, and the lower right is H.
Figure 3
Okay. Now let's look at some squares. In the square marked with letters, G = 10. This is because there is only one square distance from the starting point to the horizontal direction. The G value of the left square is 10 and the G value of the diagonal line is 14.
The H value is obtained by estimating the Manhattan distance from the start point to the end point (Red Square), moving only horizontally and vertically, and ignoring the walls along the way. In this way, the square on the right of the start point has a distance of 3 squares from the end point, so H = 30. The distance between the square above the square and the end is 4 square meters (note that only the horizontal and vertical distance is calculated), so H = 40. For other squares, you can use the same method to know how H is obtained.
The F value of each square. Once again, simply add the G value and H value.
Continue the Search)
To continue searching, we select the least F value (square) node from the open list, and then perform the following operations on the selected square:
4. extract it from the open list and put it in the close list.
5. check all squares adjacent to it, and ignore the squares (such as walls, water, or other illegal terrain) in the close list or unmovable ), if the squares are not in open lsit, add them to open list.
Set the selected square to the new square father.
6. If an adjacent square is already in the open list, check whether the path is better. That is to say, whether the square arrives through the current square (the selected square) has a smaller G value. If not, do not perform any operations.
On the contrary, if the G value is smaller, set the father of the square to the current square (the square we selected), and re-calculate the F value and G value of the square. If you are still confused, refer.
Figure 4
OK. Let's see how it works. In our first nine squares, there are eight in the open list, And the start point is placed in the close list. Among these squares, the F value on the right of the start point is the minimum 40, so we choose this square as the next square to be processed. Its outer frame is highlighted by a blue line.
First, we move it from the open list to the close list (that is why the blue line is highlighted ). Then we check the square adjacent to it. The square on the right is a wall, which we ignore. The square on the left is the starting point. We ignore it in the close list. The other four adjacent squares are in the open list. We need to check whether the path to the square is better, and use the G value to determine. Let's look at the Square above. Its current G value is 14. If we get there through the current Square, the G value will be 20 (10 of which is the G value to reach the current square, plus the vertical move from the current square to the above Square
G value 10 ). Obviously 20 is bigger than 14, so this is not the optimal path. If you look at the picture, you will understand. Moving directly from the starting point along the diagonal line to that square is better than moving horizontally before moving vertically.
After checking the four adjacent squares in the open list, we did not find a better path through the current square. Therefore, we will not make any changes. Now we have checked all adjacent squares of the current square and processed them. It is time to select the next square to be processed.
So we traverse our open list again, and now it has only seven squares. We need to select the one with the smallest F value. Interestingly, this time there are two squares with the F value 54. Which one should I choose? It does not matter. In terms of speed, it is faster to add the square to the open list. This leads to the preference of the newly found square when approaching the target in the search process. But this is not important. (Different treatments for the same data lead to A * of the two versions to find different paths with the same length ).
Select the square at the bottom right of the start point, as shown in.
Figure 5
This time, when we checked the adjacent square, we found that the square on the right of it was a wall, ignoring it. The same is true for the above.
We also ignore the grid below the wall. Why? Because you cannot directly move from the current square to the square without crossing the corner. You need to move down first and then move to the square to bypass the corner. (Note: the rule for crossing corners is optional and depends on how your nodes are placed)
In this way, there are five adjacent squares left. The two squares under the current square have not been added to the open list, so they are added, and the current square is set as their father. In the remaining three squares, two are already in the close list (one is the starting point, the other is the square above the current square, and the outer box is highlighted). We ignore them. The last square, that is, the square on the left of the current square, is used to check whether there is a smaller G value through the current square. No. Therefore, we are going to select the next square to be processed from the open list.
Repeat this process until the end point is added to the open list, as shown in.
Figure 6
Note that the father of the square in the lower part of the starting point is different from that in the front. Previously, its G value was 28 and pointed to the square in the upper right of it. Now its G value is 20 and points to the square above it. This occurs somewhere in the routing process. When the new path is used, the G value is checked and reduced. Therefore, the parent node is reset, and the G and F values are recalculated. Although this change is not important in this example, in many cases, this change will lead to a huge change in the search results.
How can we determine the actual path? It's easy to move from the end point to the parent node by pressing the arrow, so that you are taken back to the start point, which is your path. As shown in. Moving from start A to end B is simply moving from the center of A square in the path to the center of another square until the target. That's easy!
Figure 7
A * algorithm Summary (Summary of the * Method)
OK. Now you have read the entire introduction. Now let's put all the steps together:
1. Add the start point to the open list.
2. Repeat the following process:
A. traverse the open list to find the node with the smallest F value and use it as the node to be processed.
B. Move the node to the close list.
C. For each of the eight adjacent squares of the current square?
◆ If it is not reachable or it is in the close list, ignore it. Otherwise, perform the following operations.
◆ If it is not in the open list, add it to the open list, set the current square as its father, and record the F, G, and H values of the square.
◆ If it is already in the open list, check whether this path (that is, it is located through the current square) is better. Use the G value for reference. A smaller G value indicates a better path. If so, set its father to the current square and recalculate its G and F values. If your open list is sorted by F value, you may need to sort it again after the change.
D. Stop when you
◆ Add the end point to the open list. The path is found, or
◆ An error occurred while searching for the endpoint. The open list is empty and there is no path.
3. Save the path. From the end point, each square is moved along the parent node until the start point. This is your path.
Small Rant)
Please forgive me for my digress. When you see various discussions about the * algorithm on the Internet or on A forum, you will occasionally find some A * Code. In fact, they are not. To use A *, you must include all the elements discussed above-especially open list, close list, and path cost G, H, and F. There are also many other pathfinding algorithms. These algorithms are not A * algorithms, and A * is considered to be the best. Bryan Stout discussed part of the articles cited at the end of this article, including their advantages and disadvantages. In some cases, you can choose one from the second, but you must understand what you are doing. OK, no nonsense. Return to the article.
Notes on Implemetation)
Now you understand the basic method. Here are some additional things you need to consider when writing your own program. The following materials reference some programs I have written in C ++ and Basic, but they are equally valid for other languages.
1. Maintain the Open List: This is the most important part of. Every time you access the Open list, you need to find the square with the minimum F value. There are several ways to do this. You can save the path element at will. When you need to find a square with the minimum F value, traverse the entire open list. This is very simple, but it will be very slow for a long path. This method can be improved by maintaining a sorted table. Every time you need to find a square with a minimum F value, you only need to retrieve the first item of the table. This is the first method I use when I write a program.
This works well for small maps, but it is not the fastest solution. A * programmer pursuing speed uses something called A binary heap, which is also used in my program. In my experience, this method is usually 2-3 times faster, and increases the path speed to a geometric level (10 times or even faster ). If you want to learn more about Binary Heaps, read Using Binary Heaps in A * Pathfinding.
2. Other units: if you happen to have carefully read my program, you will notice that I have completely ignored other units. My Pathfinder can actually cross each other. It depends on the game, Maybe yes, maybe no. If you want to consider other units and want them to bypass each other when moving, I suggest your pathfinder ignore them and write some new programs to determine whether the two units will collide. If a collision occurs, you can create a new path, or use some standard motion rules (such as moving to the right forever, etc.) until the obstacle is not on the way, and then generate a new path. Why is the initial path excluded from other units? Because other units are movable, they may not be in their own positions when you arrive. This can generate some weird results. A unit suddenly turns to avoid a collision with an existing unit. After its path is calculated, it will collide with those units traversing its path.
Ignoring other units in the path finding code means you must write another code to handle the collision. This is the details of the game, so I will leave the solution to you. Several solutions in the Bryan Stout's article referenced at the end of this article are worth understanding.
3. some speed tips: If you are developing your own A * program or adapting the program I wrote, you will find that pathsearching takes A lot of CPU time, especially when you have many Pathfinder and a large map. If you have read the online materials, you will find that, even if you develop Starcraft, experts in the Age of Empire are also like this. If you find things slow due to path searching, you have some good ideas:
◆ Use a small map or less Pathfinder.
◆ Do not seek the path of multiple Pathfinder at the same time. Instead, they are put into the queue and distributed to several game cycles. If your game runs at a speed of 40 cycles per second, no one will notice it. However, if there are a large number of Pathfinder at the same time, they will immediately find that the game is slowing down.
◆ Use a larger square in a map. This reduces the number of squares to be searched during path searching. If you are ambitious, you can design multiple route-finding solutions and use them in different scenarios based on the path length. This is also the practice of professionals. Use a large square for long paths and use a small square when you approach the target. If you are interested in this, see Two-Tiered A * Pathfinding.
◆ For a long path, consider using the path point system, or you can pre-calculate the path and add it to the game.
◆ Pre-process your map to identify which areas are inaccessible. These regions are called "isolated islands ". In fact, they can be islands or any area that is surrounded by walls and cannot be reached. The lower limit of A * is that when you tell him to search for the path to which area, he will search for the whole map until all squares that can arrive are processed through open list or close list. This wastes a lot of CPU time. This can be solved by pre-setting areas that cannot be reached. Record this information in an array and check it before seeking. In my Blitz program, I wrote a map Preprocessing Program to complete this. It can identify dead paths ignored by pathfinding algorithms in advance, which further improves the speed.
4. Different terrain loss: In this tutorial and my program, there are only two kinds of terrain: reachable and inaccessible. But if you have some terrain that you can reach, it will take a higher price to move, the swamp, the hill, and the dungeon stairs.
And so on are accessible terrain, but the cost of moving is higher than that of flat land. Similarly, road movement costs less than the terrain around it.
When you calculate the G value of a given square, you can easily solve this problem by adding the terrain cost. Simply add some extra costs to these squares. A * the algorithm is used to find the path with the lowest cost, which should be easily processed. In my simple example, there are only two types of terrain: reachable and inaccessible. A * searches for the shortest path and the most direct path. However, the path with the lowest cost may be long in a terrain-costly environment.
It's like bypassing the swamp along the road rather than directly crossing it.
Another thing to consider is the so-called "influence Mapping" by experts. Just like the variable-cost terrain described above, you can create an additional scoring system, apply it to AI. Suppose you have a map where a channel passes through a hill and a large number of Pathfinder will use this channel. Every time a computer generates a path through that channel, it will become very crowded. If needed, you can generate an influence map, which punishes squares that will result in a massacre. This allows the computer to select a safer path, and can also help it avoid the continuous delivery of the team or Pathfinder to a specific path due to short paths (of course more dangerous.
5. Maintain areas not tested: When you play PC games, do you find that the computer is always able to accurately select paths or even maps. For games, finding the path is too accurate but not true. Fortunately, this issue is easily fixed. The answer is to create an independent array of knowncyclability for each player and computer (not every unit, which wastes a lot of memory. Each array contains information about the areas that the player has detected, and other areas that are assumed to be reachable until they are confirmed. When this method is used, the Unit lingers at the dead end of the path and makes an incorrect choice until the path is found around it. Once a map is detected, the path searching works as usual.
6. Smooth path: A * automatically gives you the smallest and shortest path, but it does not automatically give you the smoothest path. Take a look at the path found in our example (figure 7 ). In this path, the first step is at the bottom right of the starting point. If the first step is at the bottom right of the starting point, will the path be smoother?
There are several ways to solve this problem. When you calculate a path, you can punish those squares that change the direction and add an additional overhead to its G value. Another option is that you can traverse the generated path and find the places that will make the path smoother by replacing them with adjacent squares. For More information, see Toward More Realistic Pathfinding.
7. Non-square search area: In our example, we use a Square area of 2D. You can use irregular areas. Think about the countries in the adventure game, and you can design a path-finding level like that. You need to create a table to store the neighboring relations between countries and the G value for moving from one country to another. You also need a method to estimate the H value. Others can be processed in the same way as in the preceding example. When you add a new item to the open list, instead of using adjacent squares, you can view neighboring countries in the table.
Similarly, you can create a path point system for the path of a fixed terrain map. Path points are usually turning points in road or dungeon channels. As a game designer, you can pre-set path points. If there are no obstacles to the line between the two path points, they are considered adjacent. In the adventure game example, you can save the adjacent information in a certain table and use it when new items are added to the open list. Then record the G value (the linear distance between two nodes may be used) and the H value (the linear distance from the node to the target may be used ). The rest will be handled as usual.
Further Reading (Further Reading)
OK. Now you have A basic understanding of A * and some advanced topics. I strongly recommend that you check my code. The compressed package contains two versions of implementation: C ++ and Blitz Basic. There are comments for both versions, which you can easily understand. Below is the link:
Sample Code: A * Pathfinder (2D) Version 1.71.
If you do not use C ++ or BlitzBasic, you can find two exe files in C ++. The BlitzBasic version must go to the website
Blitz Basic downloads the free Demo of BlitzBasic 3D to run. Here you can see an online verification instance of Ben O 'Neill.
You should read the articles on the following sites. After reading this tutorial, you can understand them more easily.
Amit's A * Pages: This article about Amit Patel has been widely cited. However, if you haven't read this tutorial, you may be confused. In particular, you can see some of Amit Patel's own ideas.
Smart Moves: Intelligent Path Finding: Bryan Stout. Bryan used A program written in Delphi to help me learn A * and gave me some inspiration in my program. He also elaborated on A *'s other options.
Terrain Analysis: Dave Pottinger is a very advanced and attractive article. He is an expert at Ensemble Studios. This guy adjusted the game empire and kings. Don't expect to be able to read everything here, but this is an attractive article that can give you some good ideas. It discusses the packet mip-mapping,
Influence mapping, and other high-level AI pathfinding themes. His flood filling inspired me when dealing with dead paths "dead ends" and "island. This is included in my Blitz version program.
The following sites are worth looking:
·
AiGuru: Pathfinding
·
Game AI Resource: Pathfinding
·
GameDev.net: Pathfinding