100 likes | 114 Views
Modifying A* Pathfinding for Video Games. By: David Gelhardt. A* in Brief.
E N D
Modifying A* Pathfinding for Video Games By: David Gelhardt
A* in Brief • Start at the initial position (node) and place it on the Open list, along with its estimated cost to the destination, which is determined by a heuristic. The heuristic is often just the geometric distance between two nodes. • Pop the node off the Open list that has the lowest estimated cost to the destination. • If the node is the destination, we've successfully finished (quit). • Examine the node's eight neighboring nodes. • For each of the nodes which are not blocked, calculate the estimated cost to the goal of the path that goes through that node. (This is the actual cost to reach that node from the origin, plus the heuristic cost to the destination.) • Push all those nonblocked surrounding nodes onto the Open list, and repeat loop. • In the end, the nodes along the chosen path, including the starting and ending position, are called the waypoints. The A* algorithm is guaranteed to find the best path from the origin to the destination, if one exists.
Heuristical Pathfinding • To perform an efficient A* search, it is important that the origin and destination nodes of any particular search are not too far apart, or the search time will become enormous. For our example the distance between origin and destination will be constrained to 40 tiles, and that the total search space be no more than 60x60 tiles. • In the real world people do not formulate precise path plans which stretch on for miles. Instead they subdivide the path and choose landmarks to reach. • Ex. “First get to the highway on-ramp, then travel to the exit for the mall, then drive to the parking lot.” • There are two techniques to create a map heirarchy in games. • Subdivide the line to the destination into midpoints, each of which is then used as a subdestination. • Preprocess the map into a large number of regions, for example castles, clearings, hills, and so on. Then start by finding a path on the "region map" to get from the current position to the destination region, and then find a tile-based path on the detailed map to get to the next region. If a unit has no region knowledge and you want to be completely realistic with its behavior, it can just choose the next region which lies in the compass direction of its ultimate destination.
A Faster Implementation of the Standard A* • The standard A* algorithm uses a sorted linked list (or two linked lists) to track nodes that are checked. • Instead use a 60x60 fixed matrix. • Each point on the matrix should store: • The cost to get to the point • The total cost through that point to the goal • The [x,y] location of its "parent" tile (the tile before it on the path) • A Boolean stating whether or not it is on the "Open" list of actively pursued nodes. • The [x,y] locations of the Previous and Next nodes in the Open list. • Also keep a separate array of 1-bit Booleans, which store whether or not each node in our matrix has been touched yet during this search. • The original algorithm maintians an Open list, actually a Priority Que. However by using Previous and Next pointers within the fixed array we save memory. • Note additionally that the "list" can be implemented as a binary tree (by having two Next node pointers at each element), but it’s been found to be substantially faster to have a simple (non-priority) list. • Overall, by avoiding all memory allocations and list insertions, this method turns out to be dramatically faster. I have profiled it to be as much as 40 times faster than standard A* implementations.
Smoothing out A* • The first and most basic step in making an A* path more realistic is getting rid of the zigzag effect it produces. • We introduce a simple smoothing algorithm which takes place after the standard A* algorithm has completed its path. The algorithm makes use of a function Walkable(pointA, pointB), which samples points along a line from point A to point B at a certain granularity, checking at each point whether the unit overlaps any neighboring blocked tile. The function returns true if it encounters no blocked tiles and false otherwise.
Pseudocode for smoothing algorithm • checkPoint = starting point of pathcurrentPoint = next point in pathwhile (currentPoint->next != NULL) if Walkable(checkPoint, currentPoint->next) // Make a straight path between those points: temp = currentPoint currentPoint = currentPoint->next delete temp from the path else checkPoint = currentPoint currentPoint = currentPoint->next
Adding Realistic Turns • First we must decide/find out what the turning radius of our unit is. The bigger the unit the bigger the turning radius. Then we must create a realistic curve. • Psuedocode to create a curve: • angleToP = initial_direction - 90P.x = Origin.x + r * cos(angleToP)P.y = Origin.y + r * sin(angleToP) dx = Destination.x - P.xdy = Destination.y - P.yh = sqrt(dx*dx + dy*dy) if (h < r)return false d = sqrt(h*h - r*r)theta = arccos(r / h) phi = arctan(dy / dx) [offset to the correct quadrant]Q.x = P.x + r * cos(phi + theta)Q.y = P.y + r * sin(phi + theta)
Calculate position and orientation • Since we created a turn we can now calculate our position and orientation. • Psuedo code: • distance = unit_speed * elapsed_timeloop i = 0 to 3: if (distance < LineSegment[i].length) // Unit is somewhere on this line segment if LineSegment[i] is an arc determine current angle on arc (theta) by adding or subtracting (distance / r) to the starting angle depending on whether turning to the left or right position.x = LineSegment[i].center.x + r*cos(theta) position.y = LineSegment[i].center.y + r*sin(theta) determine current direction (direction) by adding or subtracting 90 to theta, depending on left/right else position.x = LineSegment[i].start.x + distance * cos(LineSegment[i].line_angle) position.y = LineSegment[i].start.y + distance * sin(LineSegment[i].line_angle) direction = theta break out of loop else distance = distance - LineSegment[i].length
Achieving Legal Turns • 5 Ways to do this: • Ignore blocking tiles • First use the A* algorithm to calculate the path. Then progress from point to point in the path as follows: At any waypoint, a unit has a position, an orientation, and a destination waypoint. • Path recalculations • After the A* has completed, we step through the path, making sure every move from one waypoint to the next is valid. If we find a collision, we mark the move as invalid and try the A* path search again. • Making tighter turns • Whenever we need to make a turn that would normally cause a collision, we allow our turning radius to decrease until the turn becomes legal. • Backing up • Just like with a car if you can’t make the turn you back up and try again. • Directional A* Algorithm • Basically the A* Algorithm adds a third dimension and checks the possible path the that the unit could be traveling for items that are in the way.
The End • Pathfinding is a complex problem which requires further study and refinement. • This just briefly covers converting A* for realistic movement in video games • For more information checkout this article:http://www.gamasutra.com/features/20010314/pinter_01.htm