在很多游戏中我们需要让角色用最快的速度到达目标地点,于是我们需要找到一条最短的路径或是比较短的路径,于是出现很多寻路算法,在此介绍较为高效的A* 算法。
原理:把一个2D或者3D的场景解析成由很多方块或者方体组成,给每个方块赋予坐标值,则可以出现起始点的坐标值。
依托公式F=H+G; 靠谱指数=预计耗费+移动到当前已经耗费
关键集合:openList<point> 用来储存将要判断F值的点,closeList<point>用来储蓄已经判断过F值得点;
实现思路:
- 自定义起始点和终点(point start = map[2, 3]; point end = map[6, 3];)
- 新建openList<point>往里添加起始点。
- 用while判断如果openlist>0; 寻找openlist里最小F的点,把这点移除openlist,添加closelist.
- 新建sorroundList用来储存最小点周围的点;
- 过滤是否在closeList
- 遍历这些点是否在openList里面,如果是则判断周围的点和openlist里的点的G值,因为H知同一点一样(新建判断G值函数);如果当前点G值比opnelist的这一点G值小,则把这一点的父点改为最小F的点;
- 如果不在openlist则添加
- 再一次判断openlist里最小F的点,重复4.5.6.7
- 知道结束点在openlist里面打破循环;
point类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class point {
public point Parent { get; set; }
public float F { get; set; }
public float G { get; set; }
public float H { get; set; }
public int X { get; set; }
public int Y { get; set; }
public bool IsWall { get; set; } //
public point(int x,int y,point parent=null)
{
this.X = x;
this.Y = y;
this.Parent = parent;
IsWall = false;
}
public void UpDateParent(point parent,float g)
{
this.Parent = parent;
this.G = g;
this.F = G + H;
}
}
逻辑类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AStart : MonoBehaviour { //在开集合中找最小值,创建这一点的周围集合
//这个周围集合是否有点在开集合,有就比较,替换
public float width = 8;
public float height = 6;
private point[,] map = new point[8, 6];
// Use this for initialization
void Start () {
InitMap();
point start = map[2, 3];
point end = map[6, 3];
FindPath(start,end);
ShowPath(start,end);
//List<point> li = GetSurroundPoint(map[3, 3]);
//foreach (point p in li)
//{
// Debug.Log(p.X+"-"+p.Y);
//}
}
public void InitMap() //初始化
{
for (int i=0;i<8;i++)
{
for (int j=0;j<6;j++)
{
map[i, j] = new point(i,j);
}
}
map[4, 2].IsWall = true;
map[4, 3].IsWall = true;
map[4, 4].IsWall = true;
}
public void ShowPath(point start,point end)
{
point temp = end;
while (true)
{
Debug.Log(temp.X+","+temp.Y);
temp = temp.Parent;
if (temp.Parent==null)
{
Debug.Log(temp.X + "," + temp.Y);
break;
}
}
}
public void FindPath(point start,point end) //寻找路径(起点,终点)
{
List<point> openList = new List<point>();
List<point> closeList = new List<point>();
openList.Add(start); //
while (openList.Count>0)
{
point point1 = FindMinF(openList);
openList.Remove(point1);
closeList.Add(point1);
List<point> surroundPointd = GetSurroundPoint(point1); //获得附近的点
PointsFilter(surroundPointd,closeList); //过滤这些点是否在闭集合
foreach (point surroundPoint in surroundPointd)
{
if (openList.IndexOf(surroundPoint)>-1) //周围的点是否在开集合中出现过
{
float nowG = CalcG(surroundPoint,point1); //当前G值==当前到父类距离+父类G
if (nowG < surroundPoint.G) //原本在开集合这点的G值
{
surroundPoint.UpDateParent(point1,nowG);
}
}
else //如果开集合没有则添加,并赋值
{
surroundPoint.Parent = point1;
CalcF(surroundPoint,end); //
openList.Add(surroundPoint);
}
}
if (openList.IndexOf(end)>-1)
{
break;
}
}
}
public void PointsFilter(List<point> surroundPointd,List<point> closeList) //过滤周围在闭集合的点
{
foreach (point p in closeList)
{
if (surroundPointd.IndexOf(p)>-1) //闭集合存在则移除
{
surroundPointd.Remove(p);
}
}
}
public List<point> GetSurroundPoint(point point) //获得周围的点
{
point up = null, down = null, left = null, right = null;
point upLeft = null, upRight = null, downLeft = null, downRight = null;
List<point> list = new List<point>();
//先判断上下左右是否为边界,在判断是否为为空,不为墙
if (point.Y<(6-1))
{
up = map[point.X, point.Y + 1];
if (up!=null&&up.IsWall==false)
{
list.Add(up);
}
}
if (point.Y > (0))
{
down = map[point.X, point.Y - 1];
if (down != null && down.IsWall == false)
{
list.Add(down);
}
}
if (point.X < (8 - 1))
{
right = map[point.X+1, point.Y];
if (right != null && right.IsWall == false)
{
list.Add(right);
}
}
if (point.X > (0))
{
left = map[point.X-1, point.Y];
if (left != null && left.IsWall == false)
{
list.Add(left);
}
}
/左上存在时,左和上必须不为空,不为墙,自己也不能为空不能为墙
if (up!=null&&left!=null)
{
upLeft = map[point.X - 1, point.Y + 1];
if (up.IsWall==false&&left.IsWall==false&&upLeft!=null&&upLeft.IsWall==false)
{
list.Add(upLeft);
}
}
if (up != null && right != null)
{
upRight = map[point.X + 1, point.Y + 1];
if (up.IsWall == false && right.IsWall == false && upRight != null && upRight.IsWall == false)
{
list.Add(upRight);
}
}
if (down != null && left != null)
{
downLeft = map[point.X - 1, point.Y - 1];
if (down.IsWall == false && left.IsWall == false && downLeft != null && downLeft.IsWall == false)
{
list.Add(downLeft);
}
}
if (down != null && right != null)
{
downRight = map[point.X + 1, point.Y - 1];
if (down.IsWall == false && right.IsWall == false && downRight != null && downRight.IsWall == false)
{
list.Add(downRight);
}
}
return list;
}
private point FindMinF(List<point> openList) //寻找最小的的F
{
float f = float.MaxValue;
point temp = null;
foreach (point p in openList)
{
if (p.F<f)
{
temp = p;
f = p.F;
}
}
return temp;
}
private void CalcF(point now,point end) //当前点和终点,给F,G,H赋值
{
//F=G+H
float h = Mathf.Abs(end.X - now.X) + Mathf.Abs(end.Y-now.Y);
float g = 0;
if (now.Parent==null)
{
g = 0;
}
else
{
g = Vector2.Distance(new Vector2(now.X, now.Y), new Vector2(now.Parent.X, now.Parent.Y))+now.Parent.G;
}
float f = g + h;
now.F = f;
now.G = g;
now.H = h;
}
private float CalcG(point now,point parent)
{
return Vector2.Distance(new Vector2(now.X, now.Y), new Vector2(parent.X, parent.Y))+parent.G;
}
// Update is called once per frame
void Update () {
}
}