Author: llinzzi Date: 2007-05-24
Last year did a little thing, an online webgame, currently only realize a lot of people chat, move, drag screen movement, scene system, etc., can be used as a scene chat room. But it's been thrown away for a year.
As shown in figure
Art by Electrostatic design
Background will be older than the development
Want to pick up this year to do a good job, because it is based on the coordinate point, so the mobile path is very cost resources. Found a A * path algorithm, very intelligent.
Reprint some Introduction "Turn from http://data.gameres.com/message.asp?TopicID=25439"
Translator preface: Long ago knew A * algorithm, but never read the relevant articles, also did not read the code, but the brain has a vague concept. This decision starts from the beginning and studies the simple method of being highly regarded as the beginning of learning artificial intelligence.
This article is very well-known, there should be a lot of people in China have translated it, I did not find, I think the translation itself is also the level of their own English exercise. After efforts, finally completed the document, but also understand the principle of a * algorithm. There is no doubt that the author of the image of the description, concise and humorous language to tell the magic of this magical algorithm, I believe that every person who has read this will have knowledge (if not, that is my translation is too bad--b).
Original link: http://www.gamedev.net/reference/articles/article2003.asp
The following is the text of the translation. (As I use UltraEdit editor, I do not have to deal with various links in the original text (except the chart), but also in order to avoid the suspicion of unauthorized links, interested readers can refer to the original.
is not difficult, a * (read as A Star) algorithm for beginners is indeed a bit difficult.
This article does not attempt to make an authoritative statement on the subject. Instead, it simply describes the principle of the algorithm, allowing you to understand other relevant information in a further reading.
Finally, this article has no procedural details. You can do it in any computer program language. As you wish, I included a link to the example program at the end of the article. Compression packs include C + + and blitz basic two languages, and if you just want to see how it works, it also contains executable files.
We are improving ourselves. Let's start from the beginning ...
Order: Search area
Suppose someone wants to move from point A to separated B, as shown below, the green is the start a, the red is the end B, the blue box is the middle wall.
You first notice that the search area is divided into square grids. Like this, simplifying the search area is the first step in finding a way. This method simplifies the search area into a two-dimensional array. Each element of an array is a square of the grid, and the squares are marked as both accessible and not passed. The path is described as a collection of squares we pass from A to B. Once the path is found, our people move from the center of one square to the other until they reach their destination.
These midpoint are called "nodes". When you read other search information, you will often see people talking about nodes. Why don't you just describe them as squares? Because it is possible that your path is divided into other structures that are not squares. They can be rectangular, hexagonal, or any other shape. Nodes can be placed anywhere in the shape-either in the center, or along the border, or somewhere else. We use this system anyway, because it is the simplest.
Start Search
As we deal with the above graph grid, once the search area is transformed into a manageable node, the next step is to lead a search that finds the shortest path. In a * seek algorithm, we check the adjacent squares by starting from point A, extending outward until we find the target.
We do the following to start the search:
1, starting with point A and depositing it as an "open list" as a pending point. Opening a list is like a shopping list. Although there is only one element in the list, there will be more later. Your path may pass through the squares it contains, or it may not. Basically, this is a list of checked squares.
2, look for all reachable or accessible squares around the starting point, skipping walls, water, or other squares that cannot pass through the terrain. Also add them to the open list. Save point A as "parent square" for all these squares. When we want to describe the path, the data of the parent square is very important. It will explain its specific uses later on.
3, remove point A from the open list and add it to a "close list" that holds all squares that do not need to be checked again.
At this point, you should form a structure like the diagram. In the diagram, the dark green squares are the center of your starting squares. It is stroked in light blue to indicate that it has been added to the closed list. All adjacent squares are now on the open list, and they are stroked with light green. Each square has a gray pointer that refers to their parent square, which is the starting square.
Next, we choose to open the adjacent square in the list, roughly repeat the previous process, as follows. But which squares are we going to choose? That's the lowest F value.
Path score
The key to choosing which squares to go through in a path is the following equation:
F = G + H
Over here:
* G = from start a, along the resulting path, move to the grid to specify the movement cost of the squares.
* H = estimated movement cost of moving from that square on the grid to end B. This is often called heuristic, and may be a bit confusing to you. The reason for this is that it's just a guess. We have no way of knowing the length of the path beforehand, as there may be obstacles (walls, water, etc.) on the road. Although this article only provides a way to compute H, you can find many other ways on the web.
Our path is generated by iterating through the open list and selecting a square with the lowest F value. The article will describe the process in more detail. First, let's take a closer look at how to calculate this equation.
As mentioned above, G represents the cost of moving along the path from the start point to the current point. In this case, we make the horizontal or vertical move 10, and the diagonal direction cost 14. We take these values because the distance along the diagonal is the square root 2 (don't be afraid), or about 1.414 times times as long as the horizontal or vertical movement is spent. To simplify, we use 10 and 14 approximations. The proportions are basically correct, and we avoid the root operation and decimals. It's not just because we're afraid of trouble or we don't like math. Using such integers is also faster for computers. You will find that if you do not use these simplified methods, it will become very slow to find your way.
Since we are calculating G values leading to a square along a particular path, the method of evaluation is to take the G value of its parent node, and then increase by 14 and 10, respectively, according to its relative parent, diagonally or at right angles (not diagonally). The demand for this method in the example becomes more, because we get more than one square out of the start grid.
The H value can be estimated in different ways. The method we use here is called the Manhattan method, which calculates the sum of both horizontal and vertical squares from the current grid to the target, ignoring the diagonal direction. Then multiply the result by 10. This is the Manhattan approach because it looks like counting the number of blocks from one place to another in the city, where you can't cross the block diagonally. It is important that we overlook all the obstacles. This is an estimate of the remaining distance, not the actual value, which is why this method is called heuristic. Want to know more? You can find the equation and the extra annotations here.
The value of f is the and of G and H. The results of the first step search can be seen in the following chart. The scores of F,g and H are written in each square. As indicated by the square on the right side of the starting lattice, F is printed in the upper-left corner, G in the lower-left corner, and h in the lower-right corner.
Now let's take a look at these squares. In the square of the letter, G = 10. This is because it deviates only in a horizontal direction from the starting lattice. Immediately above the starting lattice, the G value of the squares below and to the left is equal to 10. The G-value in diagonal direction is 14.
The H value is obtained by solving the Manhattan distance to the red target, which moves only in horizontal and vertical directions and ignores the middle wall. In this way, the square right next to the starting point is 3 squares away from the Red Square, and the H value is 30. The square above the square has a distance of 4 squares (remember, you can only move in the horizontal and vertical direction), and the H value is 40. You should know roughly how to calculate the h value of the other squares.
The f value of each lattice, or the simple addition of G and H
Continue searching
To continue with the search, we simply select the lowest f-value squares from the open list. The selected squares are then treated as follows:
4, remove it from the open list and add it to the close list.
5, check all adjacent lattices. Skip those that are already in the closed list or cannot be passed (there are walls, water terrain, or other inaccessible terrain) and add them to the open list if they are not there yet. Take the checked squares as the parent of the new squares.
6, if a neighboring grid is already on the table, check to see if the current path is better. In other words, check if the G value will be lower if we reach it with a new path. If not, then do nothing.
On the other hand, if the new G value is low, change the parent of the adjacent square to the currently selected box (in the chart above, change the direction of the arrow to the square). Finally, the values of f and g are recalculated. If this doesn't look clear enough, you can look at the diagram below.
Well, let's see how it works. In our first 9 squares, after the starting point is switched to the closed list, there are 8 squares left in the open list. In this, the lowest f value is the lattice on the right side of the starting lattice, and its F value is 40. So we chose this as the next square to be processed. In the diagram that follows, it is highlighted in blue.
First, we take it out of the open list and put it in the closed list (which is why he was highlighted in blue). Then we check the adjacent lattices. Well, the lattice on the right is a wall, so let's skip it. The left lattice is the starting lattice. It's in the close list, so we'll skip it too.
The other 4 are already in the open list, so we check the G value to determine if the path is better if we get there through this grid. Let's take a look at the squares below the grid. Its G-value is 14. If we move from the current lattice to there, the G value equals 20 (the G value to the current lattice is 10, and moving to the above lattice will increase the G value by 10). Because the G value is 20 greater than 14, this is not a better path. If you look at the picture, you can understand it. Instead of moving one grid horizontally and then vertically, it's easier to move a grid directly diagonally.
When we repeat this process for the 4 adjacent squares that already exist in the open list, we find that no path can be improved by using the current grid, so we don't make any changes. Now that we've checked all the neighboring lattices, we can move to the next grid.
So we retrieved the open list, and now there are only 7 grids, and we still choose the lowest F value. Interestingly, this time, there are two squares with a value of 54. How do we choose? It's no trouble. In terms of speed, it is quicker to select the last grid to add to the list. This leads to a preference to use the newly found lattice when approaching a target in the search process. But it doesn't matter. (The different treatment of the same value, resulting in different versions of a * algorithm to find the same length of different paths.) )
So let's select the grid at the bottom right of the starting lattice, as shown in.
This time, when we checked the adjacent lattice, we found the wall on the right and skipped over. The top of the grid is skipped too. We also skipped over the lattice below the wall. Why, then? Because you can't get to that grid without crossing the corner. You really need to go down first and get to that corner and walk through the corners in a step-by-step way. (Note: The rules for crossing corners are optional.) It depends on how your node is placed. )
As a result, there are 5 other cells left. The other two squares below the current grid are not currently in the open list, so we add them and designate the current grid as their parent. The remaining 3 squares, two are already in the open list (the start lattice, and the grid above the current lattice, highlighted in blue in the table), so we skipped them. The last lattice, on the left side of the current grid, will be checked to see if the G value is lower through this path. Don't worry, we're ready to check the next grid on the open list.
We repeat the process, knowing that the target is added to the open list, as seen in the following figure.
Note that the parent node of the lattice below the starting lattice is already different from the previous. Its G-value is 28 and points to the upper-right lattice. Now its G-value is 20, pointing to the lattice above it. This occurs somewhere in the search process, and when the new path is applied, the G value is checked to be low-so the parent node is redefined and the G and F values are recalculated. Although this change is not important in this example, in many cases, this change can lead to a great change in the way results are sought.
So, how do we determine this path? It's very simple, starting with the red target, and moving in the direction of the arrow toward the parent node. This will eventually lead you back to the start lattice, which is your path! It should look like the picture. Moving from start to Grid B is simply moving from the midpoint of each grid (node) to the next, until you reach the target point. It's as simple as that.
A * Method summary
OK, now that you've read through the instructions, let's write each step together:
1, add the starting grid to the open list.
2, repeat the following work:
A find the lowest F-value grid in the Open list. We call it the current lattice.
b) switch it to the close list.
c) to each of the adjacent 8 squares?
* If it is not available or is already in the closed list, skip it. The reverse is as follows.
* If it's not in the open list, add it. Take the current grid as the parent node of the grid. Record the f,g of this lattice, and the H value.
* If it is already in the open list, check to see if the new path is better with the G value as the reference. A lower g value means a better path. If so, change the parent node of the lattice to the current grid and recalculate the G and F values of the lattice. If you keep your open list sorted by F value, you may need to sort the open list again after the change.
d) Stop, when you
* Add the target to the open list, where the path is found, or
* No target grid found, the open list is empty. At this point, the path does not exist.
3. Save the path. Start with the target grid and move along the parent node of each cell until you return to the starting grid. This is your path.
Digression
To digress, forgive me, it is worth mentioning that when you see the different discussions about a * on the Web or related forums, you sometimes see some code that is used as a * algorithm and they are not. To use a *, you must include all the elements discussed above--specific open and close lists, using f,g and H as path evaluations. There are many other search algorithms, but they are not a*,a* considered to be the best among them. Bryan Stout is part of the reference document that follows this article, including some of their strengths and weaknesses. Sometimes other algorithms are better in certain situations, but you have to be very clear about what you are doing. Well, that's plenty. Back to the article.
The annotations implemented
Now that you understand the basics, there are some additional things to consider when writing your program. Some of the following materials refer to the programs I write with C + + and blitz Basic, but are also valid for code written in other languages.
1, maintain the Open list: This is the most important part of a * search algorithm. Every time you visit the open list, you need to look for the square with the lowest F value. There are several different ways to achieve this. You can save the path element at will, and when you need to find the element with the lowest f value, traverse the open list. It's simple, but it's too slow, especially for long paths. This can be improved by maintaining a grid of sorted lists, each looking for the lowest F-Value box to select only the first element of the list. This method is my first choice when I realize it myself.
On the small map. This approach works fine, but it's not the quickest solution. More demanding speed * Programmers use a method called "binary heap", which is also the method I use in code. In my experience, this method is 2~3 times faster on most occasions and has a geometric progression (10 times times faster) on a long road. If you want to learn more about Binary heap, check out my article Using Binary heaps in * pathfinding.
2, other units: If you happen to see my example code, you will find that it completely ignores the other units. My seekers can actually cross each other. Depending on the specific game, this may or may not be possible. If you plan to consider other units and hope they can bypass each other, I suggest that you ignore other units in the Seek algorithm and write some new code for collision detection. When a collision occurs, you can generate a new path or use some standard movement rules (such as always right, etc.) until there is no obstacle on the road and then generate a new path. Why not consider other units in the initial path calculation? That's because the other units will move, and when you get to their original position, they may have left. This could lead to strange results when a unit suddenly turns to avoid a unit that is no longer there and crashes into its path after the computed path is completed.
However, ignoring other objects in the Seek algorithm means that you must write a separate collision detection code. This is because of the game, so I leave this decision to you. In the list of references, Bryan Stout's article is worth studying, with some possible solutions (like robust tracking, and so on).
3, some speed tips: When you develop your own a * program, or rewrite my, you will find that the path takes up a lot of CPU time, especially on the large map of a large number of objects on the road. If you have read other materials on the Web, you will understand that even if you have developed StarCraft or an empire-era expert, there is no alternative. If you find the path too slow, here are some suggestions that might work:
* Use smaller maps or fewer road-seekers.
* Do not search for multiple objects simultaneously. Instead, add them to a queue and spread the path-finding process in several game cycles. If your game runs at a speed of 40 cycles per second, no one will notice. But they will find that the speed of the game is suddenly slow, when a lot of seekers calculate their own path.
* Try to use a larger map grid. This reduces the total number of meshes searched in the seek path. If you have the ambition, you can design two or more path-finding systems to use on different occasions depending on the length of the path. It is also the practice of professionals to compute a long path with a large area and then switch to a fine search using a small lattice/area when approaching a target. If you are interested in this view, check out my article two-tiered a * pathfinding.
* Compute the long path using the path point system, or compute the path in advance and add it to the game.
* Preprocess your map to indicate which areas of the map are inaccessible. I call these areas "islands". In fact, they can be an island or any other area surrounded by walls that cannot be reached. The lower limit of a * is that when you tell it to find a path to those areas, it searches the entire map until all reachable squares/nodes are calculated by opening the list and closing the list. This can waste a lot of CPU time. You can avoid this by proactively identifying these areas (for example, by Flood-fill or similar methods), recording this information with certain kinds of arrays, and checking it before you start to seek a path. In my blitz version of the code, I built a map preprocessor to do this work. It also indicates the dead end which can be neglected by the algorithm, which further improves the speed of the path finding.
4, different terrain loss: In this tutorial and the program I came with, there are only two types of terrain--both accessible and not available. But you may need some terrain to pass through, but the move is more expensive-swamp, hill, dungeon Staircase, and so on. These are all accessible, but more expensive terrain than the flat open movement. Similarly, roads should be less expensive than natural terrain movements.
This problem is easy to solve, as long as the calculation of the G-value of any terrain to increase the loss of the terrain can be. Simply add some extra wear and tear to it. Because A * algorithm has been designed to look for the lowest-cost path, it is easy to handle this situation. In the simple example I have provided, the terrain can only be passed and cannot be passed, and a * will find the shortest, most direct path. But in a different terrain, the lowest-cost path may include a long moving distance-just like walking along a path around a swamp rather than going straight through it.
An additional consideration is what experts call "influence mapping" (tentatively translated as an impact map). Like the different terrain costs described above, you can create a grid of additional fractional systems and apply them to the search AI. Let's say you have a map with a large number of road-seekers, all of them going through a mountain area. Each time the computer generates a path through that pass, it becomes more crowded. If you want, you can create an impact map to adversely affect the number of squares that have mass killings. This gives the computer a more secure path and helps it avoid consistently sending teams and seekers to a specific path simply because the path is short (but potentially more dangerous).
5, dealing with unknown areas: Have you ever played a PC game like this, and the computer always knows which way is right, even if it hasn't scouted the map yet? For the game, finding the road is too good to appear unreal. Fortunately, this is a problem that can be easily solved.
The answer is to create a separate "knownwalkability" array for each individual player and computer (each player, rather than each unit, which consumes a lot of memory), each containing the area that the player has explored, and other areas deemed to be accessible through the region until proven. In this way, units wander at the dead end of the road and cause the wrong choice until they find their way around. Once the map has been explored, finding a way is as usual.
6, Smooth path: Although A * provides the shortest, lowest-cost path, it cannot automatically provide a seemingly smooth path. Take a look at the path that our example eventually forms (in Figure 7). The first step is to the lower right of the starting lattice, if the step is straight down, isn't the path smoother?
There are several ways to solve this problem. When calculating a path, you can exert a negative effect on the lattice that changes direction and add extra value to the G value. Instead, you can run through it after the path has been calculated, looking for places where the adjacent squares will make the path look smoother. Want to know the full results, view toward more realistic pathfinding, one (free, but need to register) Marco Pinter published in Gamasutra.com article
7, non-square search area: In our example, we use a simple 2D square chart. You can not use this way. You can use irregularly shaped areas. Think about the game of Adventure Chess, and the game of those countries. You can design a road-finding checkpoint like that. To do this, you may need to create a table of neighboring relations between countries, and a G value moving from one country to another. You also need to estimate the H-value method. Other things are exactly the same as in the example. When you need to add new elements to the open list, you don't need to use adjacent squares, and instead look for neighboring countries from the table.
Similarly, you can create a path point system for a certain topographic map, which is usually a turning point in the path or Dungeon Channel. As game designers, you can preset these path points. Two path points are considered adjacent if there is no barrier between them on the line. In the case of adventure Chess, you can save the adjacent information in a table and use it when you need to add elements to the open list. You can then record the associated G value (possibly using a straight distance between two points), the H value (you can use the straight distance to the target point), and the others will do as you originally did.
Another example of searching RPG maps in a non square area, view my article two-tiered a * pathfinding.
Further reading
OK, now you have a preliminary understanding of some of the further points. At this point, I suggest you study my source code. The package contains two versions, one written in C + + and the other with Blitz Basic. By the way, all two versions are commented in detail, easy to read, and here is the link.
* Example code: * Pathfinder (2D) Version 1.71
If you don't use C + + or blitz Basic, there are two small executables in the C + + version. Blitz Basic can be run on the Litz basic 3D (not Blitz Plus) demo version, which is free to download from the Blitz Basic website. Ben O ' Neill provides an online demo that can be found here.
You should also look at the following pages. After reading this tutorial, they should become much easier to understand.
* Amit A * page: This is made by Amit Patel, widely referenced page, if you do not read this article in advance, it may be a little difficult to understand. It's worth a visit. Especially to see Amit's own views on the subject.
* Smart moves: Smart Search: Bryan Stout published in gamasutra.com This article needs to register to read. Registration is free and is worth the money for this article and other resources on the site. Bryan's program with Delphi helps me learn A * and is the source of inspiration for my * code. It also describes several variations of a *.
* Terrain Analysis: This is an advanced, but interesting topic that Dave Pottinge wrote, Ensemble Studios expert. This guy was involved in the Empire era and the development of the Monarch era. Don't expect to understand everything here, but this is an interesting article that may lead you to your own ideas. It contains some ideas for mip-mapping,influence mapping and some other advanced ai/. The discussion of "flood filling" gives me the inspiration for my own "dead End" and "island" code, which is included in my blitz version of the code.
Some other sites that are worth seeing:
* aiguru:pathfinding
* Game AI resource:pathfinding
* GameDev.net:Pathfinding
Well, that's all. If you're just writing a program that uses these ideas, I'd like to see a glimpse. You can contact me like this:
Now, good luck!
Current 1/2 page
12 Next read the full text