A * game path algorithm implemented by js + ajax: Page 1/2

Source: Internet
Author: User

Author: llinzzi Date:
Last year, we made a little thing, an online WebGame. Currently, we only implement multi-person chat, mobile, drag screen movement, and scenario system, which can be used in chat rooms. But it's been a year.

Design by static electricity
The background will be developed by old users

This year, I want to pick it up and do it well. Because it is based on coordinate points, the mobile path is very resource-intensive. It is intelligent to find A * path algorithm.

Reprint some of the Introduction "from the http://data.gameres.com/message.asp? TopicID = 25439]
Preface: I have known the * algorithm A long time ago, but I have never carefully read the relevant articles or read the code. I just have A vague concept in my mind. I decided to start from the beginning and study this simple method, which has been put on top of others, as a start to learning artificial intelligence.
This article is well-known and should have been translated by many people in China. I did not find it. I think the translation itself is also an exercise for my own English skills. After hard work, I finally completed the document and understood the principles of the * algorithm. Without a doubt, the author uses the image description and concise and humorous language to explain this magic algorithm from a simple perspective. I believe that everyone who has read this algorithm will understand it. (if not, that is, even the translation is too bad-B ).
Link: http://www.gamedev.net/reference/articles/article2003.asp
The following is the text of the translation. (Because I used ultraedit to edit the file, I did not deal with the various links in the original text (except charts). I also tried to avoid the suspicion of unauthorized links. Interested readers can refer to the original text.

The meeting is not difficult. The A * algorithm is indeed difficult for beginners.

This article does not attempt to make an authoritative statement on this topic. Instead, it only describes the principle of the algorithm so that you can understand other relevant materials in further reading.

Finally, this article has no program details. You can implement it in any computer programming language. As you wish, I will include a link to the example program at the end of the article. The compressed package consists of the C ++ and Blitz Basic versions. If you just want to see its running effect, it also contains executable files.

We are improving ourselves. Let's start from scratch...

Sequence: search for a region

Suppose someone wants to move from point A to point B separated by A wall. For example, the green is the start point A, the red is the end point B, and the blue square is the middle wall.

First, you notice that the search area is divided into square grids. In this way, simplifying the search for a region is the first step in path searching. This method simplifies the search area into a two-dimensional array. Each element of the array is a square in the grid. The square is marked as passable and inaccessible. The path is A set of squares from A to B. Once the path is found, our people move from the center of a square to another until they reach their destination.

These vertices are called "nodes ". When you read other tracing materials, you will often see people discussing nodes. Why not describe them as squares? Because it is possible that your path is divided into other non-square structures. They can be rectangular, hexagonal, or any other shape. Nodes can be placed anywhere in the shape-in the center, along the boundary, or somewhere else. We use this system, in any case, because it is the simplest.

Start search

As we use the grid processing method, once the search area is converted to a node that is easy to process, the next step is to guide a search to find the shortest path. In the * pathfinding algorithm, we start from vertex A and check the method of adjacent squares to expand outward until we find the target.

Perform the following operations to start searching:

1. Start from vertex A and store it as A pending vertex to an "enable list ". Enabling a list is like a shopping list. Although there is only one element in the list, it will be added later. Your path may pass through the square it contains, or it may not. Basically, this is a list of squares to be checked.
2. Find all the squares that can be reached or passed around the starting point, and skip the squares with walls, water, or other aspects that cannot pass through the terrain. They are also added to the Enable list. Save vertex A as the "parent square" for all these squares ". When we want to describe the path, the parent square information is very important. It will be explained later.
3. Delete Vertex A from the Enable list and add it to A "Close List". The list stores all squares that do not need to be checked again.

At this point, you should form a structure. In the figure, the dark green square is the center of your initial square. It is marked with a light blue stroke to indicate that it is added to the closed list. All adjacent cells are now in the open list, and they are drawn in a light green. Each square has a gray pointer pointing to its parent square, that is, the square of the beginning.

Next, we choose to enable the near square in the list to roughly repeat the previous process, as shown below. But which square should we choose? Which has the lowest f value.

Route score

The key to selecting the square passing through the path is the following equation:

F = G + H

Here:
* G = it takes time to move from start A to the generated path to the specified grid.
* H = estimated moving cost of moving the square from the grid to end B. This is often called heuristic and may confuse you. This is because it is just a guess. We cannot know the path length in advance, because there may be various obstacles (walls, water, etc.) on the road ). Although this article only provides one method for calculating H, you can find many other methods on the Internet.

Our path is generated by repeatedly traversing the Enable list and selecting the square with the lowest f value. The article will describe this process in more detail. First, let's take a deeper look at how to calculate this equation.

As mentioned above, G indicates the cost of moving along the path from the starting point to the current point. In this example, we consume 10 for horizontal or vertical movement and 14 for diagonal lines. We take these values because the distance along the diagonal line is the root number 2 (not to be afraid) consumed by moving horizontally or vertically, or about 1.414 times. We use 10 and 14 for simplicity. The ratio is basically correct, and root calculation and decimal number are avoided. This is not just because we are afraid of trouble or do not like mathematics. Using such integers is also faster for computers. You will not find that if you do not use these simplified methods, the path will become slow.

Since we are calculating the G value leading along a specific path to a certain Square, the evaluate method is to take the G value of its parent node, then, increase by 14 and 10 respectively based on the diagonal or right direction (non-diagonal) of the parent node. In this example, the requirement for this method will become more, because we get more than one square from the start square.

The H value can be estimated using different methods. The method we use here is called the Manhattan method, which calculates the total number of squares in the horizontal and vertical direction from the current grid to the target grid, ignoring the diagonal line. Then multiply the result by 10. This becomes the way of Manhattan because it looks like the number of blocks in a computing city from one place to another, where you cannot cross the blocks along the diagonal line. It is very important that we ignore all obstacles. This is an estimation of the residual distance rather than the actual value, which is also known as a heuristic method. Want to know more? Here you can find equations and additional annotations.

F is the sum of G and H. The result of the first search can be seen in the chart below. The scores of F, G, and H are written in each square. As shown in the square next to the right of the starting lattice, F is printed in the upper left corner, G is in the lower left corner, and H is in the lower right corner.

Now let's look at these squares. In the text box, G = 10. This is because it only has a grid distance from the start point in the horizontal direction. Next to the top of the Start lattice, the G value of the lower Square and the left square is equal to 10. The G value in the diagonal line is 14.

The H value is obtained by solving the Manhattan distance to the red target cell. The H value moves only horizontally and vertically, and the middle wall is ignored. In this way, the square next to the right of the start point is 3 grids away from the Red Square, and the H value is 30. The square above this square has a distance of 4 cells (Remember, it can only be moved in the horizontal and vertical directions), and the H value is 40. You should probably know how to calculate the H value of other squares ~.

The F value of each grid is simply obtained by adding G and H.

Continue search

To continue searching, we simply select the square with the lowest f value from the enabled list. Then, process the selected square as follows:

4. Delete it from the enabled list and add it to the disabled list.
5. Check all adjacent grids. Skip the terrain that is already in the disabled list or that cannot pass through (terrain with walls, water, or other terrain that cannot pass through) and add them to the enabled list if they are not in the list. Use the selected square as the parent node of the New Square.
6. If an adjacent grid is already in the open list, check whether the current path is better. In other words, check whether the G value is lower if we use a new path to reach it. If not, do nothing.
On the other hand, if the new G value is lower, change the parent node of the adjacent square to the currently selected square (in the above chart, change the direction of the arrow to this square ). Finally, recalculate the values of F and G. If this does not seem clear enough, you can see the figure below.

Okay. Let's see how it works. In the first nine cells, after the start point is switched to the disabled list, 8 cells are left in the enabled list. Here, the lowest value of F is the grid next to the right of the starting lattice, and its F value is 40. Therefore, we choose this grid as the next square to be processed. In the following figure, it is highlighted in blue.

First, we extract it from the Enable list and put it into the disabled list (this is why it is highlighted in blue ). Then we check the adjacent grids. Oh, the grid on the right is a wall, so we skipped it. The lattice on the left is the start lattice. It is in the close list, so we skip it.

The other four cells are already in the Enable list, so we can check the G value to determine whether the path is better if we get there through this grid. Let's look at the square below the selected grid. Its G value is 14. If we move from the current lattice to it, the G value will be equal to 20 (the G value reaching the current lattice is 10, and moving to the above lattice will increase the G value by 10 ). Because the G value is greater than 14, this is not a better path. If you look at the image, you can understand it. Instead of moving one grid horizontally and vertically, it is easier to move one grid directly along the diagonal line.

When we repeat the process of four neighboring cells already in the enabled list, we find that no path can be improved by using the current grid, so we will not change it. Now that we have checked all neighboring cells, we can move them to the next grid.

So we can retrieve the enabled list. Now there are only seven cells in it, and we still choose the lowest f value. Interestingly, this time, two grids have 54 values. How do we choose? This is not troublesome. In terms of speed, it is faster to select the grid that is last added to the List. This leads to the preference of the newly found lattice when approaching the target during the path searching process. But this does not matter. (Different treatments for the same value lead to different versions of the * algorithm to find different paths with the same length .)

Then we will select the grid at the bottom right of the starting grid ,.

This time, when we checked the adjacent cells, we found the right side was a wall and skipped. The above grid is also skipped. We also skipped the grid below the wall. Why? Because you cannot directly reach the grid without crossing the corner. You really need to go down first and then arrive at that grid, step by step through that corner. (Note: The Corner crossing rule is optional. It depends on how your nodes are placed .)

In this way, the other five cells are left. The other two grids under the current grid are not in the enabled list currently, So we add them and specify the current grid as their parent node. The other three cells are already in the enabled list (the start lattice and the current lattice are highlighted in blue in the table), so we skipped them. The last grid is checked on the left side of the current grid through this path, and the G value is lower. Don't worry, we are ready to check the next entry in the enabled list.

Repeat this process to know that the target cell is added to the open list, as shown in the following figure.

Note that the parent node of the lower lattice is different from the previous one. Previously, its G value is 28 and points to the grid in the upper right corner. Now its G value is 20, pointing to the lattice above it. This occurs somewhere in the routing process. When a new path is applied, the G value becomes lower after the check-so the parent node is re-specified, and the G and F values are re-calculated. Although this change is not important in this example, in many cases, this change will lead to a huge change in the search results.

So how can we determine this path? It is easy to move toward the parent node from the red target cell. This will eventually guide you back to the starting lattice. This is your path! It should look like the figure above. Moving from the starting grid A to the target grid B simply moves from the midpoint of each grid (node) along the path to the next until you reach the target point. That's simple.

A * method summary

Now that you have read the entire instruction, let's write the operations in each step together:

1. Add the Start entry to the Enable list.
2. Repeat the following steps:
A) Search for the lattice with the lowest f value in the enabled list. We call it the current grid.
B) switch it to the disabled list.
C) For each of the adjacent 8 cells?
* If it fails or is already in the closed list, skip it. Otherwise.
* If it is not in the enabled list, add it. Use the current grid as the parent node of this grid. Record the F, G, and H values of this grid.
* If it is already in the enabled list, use the G value as the reference to check whether the new path is better. A lower G value means a better path. In this case, the parent node of the grid is changed to the current grid, and the G and F values of the grid are recalculated. If you keep your enabled list sorted by the F value, you may need to sort the enabled list again after the change.

D) stop when you
* Add the target cell to the enabled list. The path is found at this time, or
* The target cell is not found. The enable list is empty. At this time, the path does not exist.
3. Save the path. Starting from the target lattice, move along the parent node of each grid until the start lattice is returned. This is your path.

Digress

Sorry, it is worth mentioning that when you see different discussions about A * on the Internet or in related forums, sometimes you will see some code used as A * algorithm, but they are not actually. To use A *, you must include all the elements discussed above-A specific list of enable and disable, and use F, G, and H for path evaluation. There are many other pathfinding algorithms, but they are not A *, and A * is considered to be the best among them. Bryan Stout describes some of their advantages and disadvantages in a reference document later in this article. Sometimes other algorithms will be better in a particular scenario, but you must be very clear about what you are doing. Okay, enough. Return to the article.

Implementation Annotation

Now that you understand the basic principles, you have to consider some additional things When writing your program. Some of the following materials reference programs I wrote in C ++ and Blitz Basic, but they are equally effective for code written in other languages.

1. Maintain the open list: This is the most important part of A * pathfinding algorithm. Each time you access the Enable list, you need to find the square with the lowest f value. There are several different methods to achieve this. You can save the path element as needed. When you need to find the element with the lowest f value, traverse the enabled list. This is very simple, but it is too slow, especially for long paths. This can be improved by maintaining a sorted list. You only need to select the first element of the List to find the square with the lowest f value each time. This method is my first choice when I implement it myself.

In the small map. This method works well, but it is not the fastest solution. A * programmer who is more demanding on speed uses A method called "binary heap", which is also the method I use in code. Based on my experience, this method will be faster than 2 in most cases ~ 3 times, and the speed on the long path is increased in a geometric series (10 times or more ). If you want to learn more about binary heap, refer to my article, Using Binary Heaps in A * Pathfinding.

2. Other units: If you read the code in my example, you will find that it completely ignores other units. My Pathfinder can actually cross each other. Depending on the specific game, this may be okay, maybe not. If you want to consider other units and want them to bypass each other, I suggest you ignore other units in the pathfinding algorithm and write some new code for collision detection. When a collision occurs, you can generate a new path or use some standard moving rules (such as always to the right) until there is no obstacle on the road, and then regenerate the new path. Why not consider other units in the initial path calculation? That's because other units will move. When you reach their original location, they may have left. This may lead to a strange result: a unit suddenly turns to avoid a unit that is no longer there, and will hit the calculation path and rush into the unit in its path.

However, ignoring other objects in the pathfinding algorithm means you must write a separate collision detection code. This is different from the game, so I leave this decision to you. In the reference list, Bryan Stout's article is worth studying. There are some possible solutions (such as robust tracing and so on ).

3. Some speed tips: when you develop your own A * program or rewrite my program, you will find that path searching takes A lot of CPU time, this is especially true when a large number of objects are found on a map. If you have read other materials on the Internet, you will understand that, even if you have developed Starcraft or the Empire era experts, this is helpless. If you think the path is too slow, some suggestions here may be effective:

* Use a smaller map or fewer Pathfinder.
* Do not route multiple objects at the same time. Instead, they are added to a queue to distribute the pathfinding process across several game cycles. If your game runs at a speed of 40 cycles per second, no one can notice it. However, they will find that the speed of the game suddenly slows down when a large number of Pathfinder calculate their path.
* Try to use a larger map grid. This reduces the total number of grids to be searched in the search path. If you are interested, you can design two or more tracing systems for use in different scenarios, depending on the path length. This is exactly what professionals do. They use a large area to calculate a long path, and then switch to a small GRID/area to find the path when approaching the target. If you are interested in this point of view, read my article Two-Tiered A * Pathfinding.
* Use the path point system to calculate the long path, or pre-calculate the path and add it to the game.
* Preprocessing your map indicates which areas of the map are not reachable. I call these regions "isolated islands ". In fact, they can be islands or other areas that cannot be reached by walls. The lower limit of A * is that when you tell it to find the path to those areas, it searches for the whole map, until all reachable squares/nodes are computed by enabling or disabling the list. This wastes a lot of CPU time. You can pre-determine these regions (such as through flood-fill or similar methods) to avoid this situation, record this information with some types of arrays, and check it before you start searching. In my Blitz code, I created a map Preprocessor to do this. It also indicates the dead end that can be ignored by the pathtracing algorithm, which further improves the pathtracing speed.

4. Different terrain loss: In this tutorial and my program, there are only two kinds of terrain-accessible and inaccessible. But you may need some terrain that can pass through, but it takes longer to move-swamp, Hill, dungeon stair, and so on. These are all terrain that can be passed but are more expensive than flat land movement. Similarly, roads are less costly than natural terrain movement.

This problem can be easily solved by increasing the terrain loss when calculating the G value of any terrain. Simply add some additional losses to it. Because the * algorithm has been designed to find the lowest-cost path, it is easy to handle this situation. In the simple example provided by me, only the terrain can pass and cannot pass. A * will find the shortest and most direct path. However, in different terrain scenarios, the path with the lowest cost may contain a long moving distance-like bypassing the swamp along the road rather than directly traversing it.

An additional consideration is what experts call "influence mapping ). As described above, you can create an additional score system and apply it to AI. Suppose you have a map with a large number of Pathfinder users, all of which are going through a certain mountainous area. Every time a computer generates a path that passes the mark, it becomes more crowded. If you want to, you can create an influence ing map to adversely affect the grids with large numbers of slaughter events. This will make computers more inclined to secure paths and help them avoid continuing to send teams and Pathfinder to a specific path just because the path is short (but may be more dangerous.

5. Dealing with unknown areas: Have you ever played such a PC game? The computer always knows which path is correct, even if it hasn't detected a map? For games, finding the path too good will seem unrealistic. Fortunately, this is an easy solution.

The answer is to create an independent "knownconfigurability" array for each different player and computer (each player, rather than each unit-that would consume a large amount of memory, each array contains the areas that the player has explored, and other areas that are considered accessible through the area until they are confirmed. In this way, the Unit lingers at the dead end of the road and leads to wrong choices until they find the path around them. Once a map is explored, the path is searched as usual.

6. Smooth path: although A * provides the shortest and lowest cost path, it cannot automatically provide the seemingly smooth path. Let's take a look at the final path of our example (in Figure 7 ). The first step is the bottom right of the starting grid. If this step is directly down, will the path be smoother?

There are several ways to solve this problem. When calculating the path, it can exert adverse effects on the grid that changes the direction and add additional values to the G value. You can also use another method. You can run the path again after calculation, and find the places that will make the path look smoother by replacing the adjacent cells. For the complete results, see Toward More Realistic Pathfinding, an article published by Marco Pinter at Gamasutra.com (free but need to be registered ).

7. Non-square search area: In our example, we use a simple 2D square chart. You may not use this method. You can use irregular areas. Think about adventure games and those countries in the game. You can design a path-finding level like that. To do this, you may need to create a table of neighboring countries and move the G value from one country to another. You also need to estimate the H value. Other things are exactly the same as in the example. When you need to add new elements to the open list, you do not need to use adjacent grids. Instead, you need to find neighboring countries from the table.

Similarly, you can create a path point system for a definite topographic map. The path points are generally the turning points of the road or the dungeon channel. As game designers, You can preset these path points. Two path points are considered adjacent if there is no obstacle in the straight line between them. In the adventure game example, you can save the adjacent information in a table and use it when you need to add an element to the list. Then you can record the associated G values (the linear distance between two points may be used), H values (the linear distance to the target point can be used ), you can simply follow the previous steps.

Another example of searching for an RPG map in A non-Square area is to view my article Two-Tiered A * Pathfinding.

Further reading

Well, now you have a preliminary understanding of some further points. At this time, I suggest you study my source code. The package contains two versions: C ++ and Blitz Basic. By the way, both versions are detailed and easy to read. Here is the link.

* Sample Code: A * Pathfinder (2D) Version 1.71

If you neither use C ++ nor Blitz Basic, there are two small executable files in C ++. Blitz Basic can be run on the free litz Basic 3D (not Blitz Plus) demo version downloaded from the Blitz Basic website. Ben o'neill provides an online demonstration available here.

You should also look at the following webpage. After reading this tutorial, they should become much easier to understand.

* Amit A * Page: This is A widely used page created by Amit Patel. It may be hard to understand if you have not read this article beforehand. It is worth looking. In particular, we should look at Amit's own views on this issue.
* Smart Moves: Smart path searching: This article published by Bryan Stout at Gamasutra.com must be registered before you can read it. Registration is free and is worth the money compared to this article and other resources on the website. Bryan uses A program written in Delphi to help me learn A *, which is also the source of inspiration for my A * code. It also describes several changes of.
* Terrain analysis: This is an advanced but interesting topic, written by Dave Pottinge and expert at Ensemble Studios. This guy is involved in the development of the empire and kings. Don't expect to understand everything here, but this is an interesting article that may make you come up with your own ideas. It includes some advanced AI/pathfinding ideas about mip-mapping, influence mapping, and others. The discussion of "flood filling" inspired me with my own "Dead End" and "island" code, which is included in my Blitz version of code.

Other Websites worth reading:

* AiGuru: Pathfinding
* Game AI Resource: Pathfinding
* GameDev.net: Pathfinding

Okay, that's all. If you just write a program that uses these ideas, I 'd like to know. You can contact me like this:
Now, good luck!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.