对于a星寻路算法最直白的理解:
从a点走到b点,首先把地图画成网格,让障碍物在网格内
如图,从s点要走到e点,把障碍物设成黑色,还要创建2个队列,一个是寻找新的节点队列(开启队列),一个是保存已走过的节点队列(关闭队列)。在寻找新的节点时,要判断该节点距离,公式为总距离=当前节点距离起点步数+当前节点距离终点步数。这里注意的是即使有障碍物在计算当前节点距离起终点步数也要按照没有障碍物来算。
拿该图来说,从起点s走到终点e(假设可以斜着走且斜走距离为1)。图上所有open,close的标识是走完后的,所以先不看,从s点出发,把s点加入开启队列,进入第一次循环,条件是如果开启队列不为空,找到开启队列中总距离最小点(只有一个点s),则找到s点旁边可走的点(相邻s点且不在open队列,不在close队列),可以找到3,5,8,9,10点,将这些点移入open队列,计算每点总距离,将s点移除open队列,s点移入close队列,进入第二次循环,当open队列不为空,找到open队列总距离最小点,可以看到总距离最小点有3个,8,9,10并且都是4(4=1+3这个1指的是该点距离起点距离3点是该点距离终点距离),假设第一个点是8,那么找到8点的旁边的可走点,由于5,9点在开启队列,4点在关闭队列,所以找到6,7点移入开启队列并计算总距离,把8移入关闭队列
此时,开启队列{3,10,9,5,6,7} 关闭队列{4,8}
进入第三次循环,开启队列不为空时,找到开启队列中总距离最小的点,这里有两个9,10总距离为4,假设是9,找邻点,所有邻点要么在开启队列要么在关闭队列,所以9移入关闭队列,
进入第四次循环,找到10点,找邻点2,11,14,此时开启队列{2,3,5,6,7,11,14} 关闭队列{4,8,9,10}
第五次循环,找到5点,移入关闭,开启队列{2,3,6,7,11,14} 关闭队列{4,5,8,9,10}
第六次开启队列{2,6,7,11,14} 关闭队列{3,4,5,8,9,10}
第七次开启队列{2,6,11,14} 关闭队列{3,4,5,7,8,9,10}
第八次开启队列{2,11,14} 关闭队列{3,4,5,6,7,8,9,10}
不说啦,可能有点错误,你们明白意思就行
最后如何找到最佳路径呢,每次在开启队列找到的点选取邻点时候,都要将邻点的父节点选为该点,图中用箭头表示,这样当第n次循环,当从开启队列找到的第一个点为终点时,回溯父节点就找到了最短路径。
贴出来一张c#代码供参考:
- using UnityEngine;
- using System.Collections;
-
- public class AStar {
-
- public static PriorityQueue closedList, openList;
-
- private static float NodeCost(Node a, Node b)
- {
- Vector3 vecCost = a.position - b.position;
- return vecCost.magnitude;
- }
-
- public static ArrayList FindPath(Node start, Node goal)
- {
- openList = new PriorityQueue();
- openList.Push(start);
- start.nodeTotalCost = 0.0f;
- start.estimatedCost = NodeCost(start, goal);
-
- closedList = new PriorityQueue();
-
- Node node = null;
-
- while (openList.Length != 0)
- {
- node = openList.First();
- if (node.position == goal.position)
- {
- return CalculatePath(node);
- }
-
- ArrayList neighbours = new ArrayList();
- GridManager.instance.GetNeighbours(node, neighbours);
- for (int i = 0; i < neighbours.Count; i++)
- {
- Node neighbourNode = (Node)neighbours[i];
- if(!closedList.Contains(neighbourNode))
- {
- float cost = NodeCost(node, neighbourNode);
- float totalCost = node.nodeTotalCost + cost;
-
- float neighbourNodeEstCost = NodeCost(neighbourNode, goal);
-
- neighbourNode.nodeTotalCost = totalCost;
- neighbourNode.estimatedCost = totalCost + neighbourNodeEstCost;
- neighbourNode.parent = node;
-
- if (!openList.Contains(neighbourNode))
- {
- openList.Push(neighbourNode);
- }
- }
- }
- closedList.Push(node);
- openList.Remove(node);
- }
-
- if (node.position != goal.position)
- {
- Debug.LogError("Goal Not Found");
- return null;
- }
- return CalculatePath(node);
- }
-
- private static ArrayList CalculatePath(Node node)
- {
- ArrayList list = new ArrayList();
- while (node != null)
- {
- list.Add(node);
- node = node.parent;
- }
- list.Reverse();
- return list;
- }
- }
避障算法:
对于一个物体要想通过障碍物需要避障算法,算法思想,从坦克前方做射线和障碍物的焦点处做法线,让坦克的新方向为(前方+法线方向*力的大小),这样坦克向左走,当射线碰不到障碍物的时候就没有法线,就不会转向了
代码中将障碍物的layer层射到第8层为Obstacles层
参考代码
- public class AvoidingObstacles : MonoBehaviour {
-
- public float speed = 20.0f;
- public float mass = 5.0f;
- public float force = 50.0f;
- public float minimunDistToAvoid = 20.0f;
-
- private float curSpeed;
- private Vector3 targetPoint;
-
-
- void Start () {
- targetPoint = Vector3.zero;
- }
-
- void OnGUI()
- {
- GUILayout.Label("Click anywhere to move the vehicle.");
- }
-
-
- void Update () {
- RaycastHit hit;
-
- var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
-
- if (Input.GetMouseButton(0) && Physics.Raycast(ray, out hit, 100.0f))
- {
- targetPoint = hit.point;
- }
-
- Vector3 dir = targetPoint - transform.position;
- dir.Normalize();
-
- AvoidObstacles(ref dir);
-
- if (Vector3.Distance(targetPoint, transform.position) < 3.0f)
- return;
-
- curSpeed = speed * Time.deltaTime;
- var rot = Quaternion.LookRotation(dir);
-
- transform.rotation = Quaternion.Slerp(transform.rotation, rot, 5.0f * Time.deltaTime);
- transform.position += transform.forward * curSpeed;
- }
-
- private void AvoidObstacles(ref Vector3 dir)
- {
- RaycastHit hit;
-
-
-
- int layerMask = 1 << 8;
-
- if (Physics.Raycast(transform.position, transform.forward,
- out hit, minimunDistToAvoid, layerMask))
- {
- Vector3 hitNormal = hit.normal;
- hitNormal.y = 0.0f;
- dir = transform.forward + hitNormal * force;
- }
- }
- }
Flocking算法:
用于群体物体追踪一个领头物体,比如gta5警车追人,红警派出一群小兵去哪里,如图绿色的物体一直在追踪红色物体移动,把红色物体的移动代码写好,在hierarchy中把绿色物体拖动到红色物体下面,给绿色物体添加flock代码即可
红色物体代码:比较简单就不注释了:
- public class FlockControl : MonoBehaviour {
-
- public float speed = 100.0f;
- public Vector3 bound;
-
- private Vector3 initialPosition;
- private Vector3 nextMovementPoint;
-
-
- void Start () {
- initialPosition = transform.position;
- CalculateNextMovementPoint();
- }
-
- private void CalculateNextMovementPoint()
- {
- float posX = Random.Range(-bound.x, bound.x);
- float posY = Random.Range(-bound.y, bound.y);
- float posZ = Random.Range(-bound.z, bound.z);
-
- nextMovementPoint = initialPosition + new Vector3(posX, posY, posZ);
- }
-
-
- void Update () {
- transform.Translate(Vector3.forward * speed * Time.deltaTime);
- transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(nextMovementPoint - transform.position), 2.0f * Time.deltaTime);
- if (Vector3.Distance(nextMovementPoint,transform.position) <= 20.0f)
- CalculateNextMovementPoint();
- }
- }
绿色物体代码,比较复杂,只管用就行,不用跟我客气: