[FreedomAI] Week 3 - A* Pathfinding

The last article introduced InfluenceMap. If you don't use this information, it is also a waste. In this article, let's look at an InfluenceMap application. A* Pathfinding - A problem we often run into is how to get the AI ​​to go from one point to another. Usually the shortest line segment before two points does not apply here. It is possible that we need to avoid those annoying obstacles. It is possible that we need to want to spread out to flank the enemy. We need to avoid the area where the friendly information is dense on the influence map... ...........At this time we need to use the A* pathfinding algorithm.

We can imagine that you are on one square of the chessboard, and you want to go to another square of the chessboard, what would you do? Since you don't have the ability to see the whole picture, most of the time you will choose, move to the adjacent point with the highest current profit, and so on recursively, and finally reach the destination.

So we get the first concept that A* pathfinding needs - cost. Every node has a cost, and in every move operation, we tend to move to the point with the least cost.

OK, the algorithm is roughly as described above. Let us describe it in more rigorous language.

At the beginning we maintain a start node, a target node, a start list, and a shutdown list.

1. Add a start node to enter the start list.

2. If there are nodes in the start list. Dequeue, check if the node in the list is closed, and if so, repeat the process.

3. Check whether the node is the target node, if so, exit.

4. Calculate the cost of this node's neighbors and add it to the start list.

5. Add the node to the target path.

6. Go back to step 2.

Let's take a look at the code:

public class AStarNodeItem
	{
		
		public Vector3 pos;
	
		public int x, y;

	
		public int gCost;

		public int hCost;


		public int fCost
		{
			get
			{
				return gCost + hCost;
			}
		}


		public AStarNodeItem parent;

		public AStarNodeItem(Vector3 pos, int x, int y)
		{
			this.pos = pos;
			this.x = x;
			this.y = y;
		}

		public AStarNodeItem()
		{
			
		}

	}

Note that here we define the cost into two parts, gcost, and the distance between adjacent nodes. Note that the distance between the center of the nine-square grid and other points has the difference between adjacent and diagonal lines, and the other part is the main body of our calculation.

Let's take a look at the code of the pathfinding part:

public Queue<Vector3> AStarFindPath(Vector3 src,Vector3 des,AStarComputer pAStarComputer)
		{
			PriorityQueue<AStarNodeItem> openList = new PriorityQueue<AStarNodeItem> (0,new AStarNodeItemCamparer());
			List<AStarNodeItem> closedList = new List<AStarNodeItem> ();
			Queue<Vector3> pathLast = new Queue<Vector3> ();
			Vector2 start = getTilefromPosition (new Vector2(src.x,src.z));
			Vector2 end = getTilefromPosition (new Vector2(des.x,des.z));

			if (!(start.x == end.x && start.y == end.y))
			{
				AStarNodeItem AStarforStart = new AStarNodeItem (src,(int)start.x,(int)start.y);
				AStarforStart.gCost = 0;
				AStarforStart.hCost = pAStarComputer (src,des);
				openList.Push (AStarforStart);
			}

			while(openList.Count!=0)
			{
				AStarNodeItem tAStarNodeItem = openList.Pop ();
				while (closedList.Contains(tAStarNodeItem)&&!isWall(new Vector2(tAStarNodeItem.x,tAStarNodeItem.y)))
					tAStarNodeItem = openList.Pop ();
				
				if (tAStarNodeItem.x == end.x && tAStarNodeItem.y == end.y)
				{
					pathLast.Enqueue (tAStarNodeItem.pos);
					return pathLast;
				}

				pathLast.Enqueue (tAStarNodeItem.pos);
				closedList.Add (tAStarNodeItem);
				openList.Clear ();
				for (int i = 0; i < NineGrid.Length; i++)
				{
					Vector2 tv = new Vector2 (tAStarNodeItem.x+NineGrid[i].x,tAStarNodeItem.y+NineGrid[i].y);
					Vector3 tp = GetPositionByTile (tv);
					AStarNodeItem ttAStarNodeItem = new AStarNodeItem (tp,(int)tv.x,(int)tv.y);
					ttAStarNodeItem.gCost = (int)(NineGrid [i].magnitude*10);
					ttAStarNodeItem.hCost = pAStarComputer (tp,des);
					openList.Push (ttAStarNodeItem);
				}

			}

			return null;

		}

In addition, we provide another parameter form:

public Queue<Vector3> AStarFindPath(Vector3 src,Vector3 des,AStarComputerformula pAStarComputerformula)
		{
			return AStarFindPath (src, des, pAStarComputerformula.EquationComputer);
		}

What is this doing? In fact, when calculating hcost, we often need to consider more than one factor, so this parameter form can be any, you specify an expression des= srcA*A+srcB*B...... ......................, the calculation of several factors is blended according to the importance, and the result is obtained.

Let's take a look at the definition of AStarComputerformula:

public class AStarComputerformula
	{
		List<AStarComputerformulaNode> mAStarComputerList = new List<AStarComputerformulaNode>() ;

		float blendfuncSum = 0.0f;

		public AStarComputer EquationComputer
		{
			get
			{
				return (Vector3 target, Vector3 des) =>
				{
					float rf = 0.0f;
					foreach(var v in mAStarComputerList)
					{
						float tf = v.mAStarComputer(target,des);
						rf+=tf*v.mBlendFunc/blendfuncSum;
					}
					return rf;
				};
			}
		}

		public void Add(AStarComputer pAStarComputer,float pBlendFunc)
		{
			AStarComputerformulaNode tAStarComputerformulaNode = new AStarComputerformulaNode ();
			tAStarComputerformulaNode.mAStarComputer = pAStarComputer;
			tAStarComputerformulaNode.mBlendFunc = pBlendFunc;
			blendfuncSum += pBlendFunc;
		}

	};
Here we use the usage of lambda expression, and calculate the equivalent calculation function very concisely.
 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325767344&siteId=291194637