RTS游戏开发:基于Gird的Theta*寻路算法,以及利用视线算法优化A*路径的思路【附视线算法的代码、A*、Theta*的伪代码】

为什么要有Theta*寻路,Theta*寻路相对A*寻路的优缺点。

RTS游戏开发:基于Gird的Theta*寻路算法,以及利用视线算法优化A*路径的思路【附视线算法的代码、A*、Theta*的伪代码】_第1张图片

这是一个A*的寻路找出的路径,可以看出,A*的路径是附着于网格的。这导致了A*的路径不是最短路,而且不自然,在RTS游戏中这是不被允许的,所以我们需要一种优化来解决这一问题,这就导致了Theta算法的出现,在上面的地图中Theta算法中的路径,我们找到的是一条直接通往终点的路。

Thata*算法与A*算法的比较

Theta是基于A*的优化,优化的地方仅仅只有选择父节点和计算代价这一步骤.

RTS游戏开发:基于Gird的Theta*寻路算法,以及利用视线算法优化A*路径的思路【附视线算法的代码、A*、Theta*的伪代码】_第2张图片

 其中LineOfSight函数是确定两个点是否有可见性的,其中可见性的定义为两点的连线不与任意一个障碍物相交【不包括障碍物边缘】。

视线算法的代码:

static bool LineOfSight(Pair S, Pair E, bool map[][Size]) {
	//换相对位置,使其只有S->E方向为右上角,S->E方向为右下角
	if (S.X > E.X) {
		Swap(S, E);
	}

	Pair P1, P2;
	P1.SetXY(S.X, S.Y);
	P2.SetXY(1e6, 1e6);

	float dx = S.X - E.X;
	float dy = S.Y - E.Y;
	float s;
	float Delta;
	//往X轴方向移动 else 往Y轴方向移动

	if (Abs(dx) > Abs(dy)) {
		Delta = Abs(dy / dx)*(dy > 0 ? -1 : 1);
		s = (dx < 0 ? 1 : -1);
	}
	else {
		Delta = Abs(dx / dy)*(dx > 0 ? -1 : 1);
		s = (dy < 0 ? 1 : -1);
	}
	//水平线和竖直线单独处理
	if (S.X == E.X) {
		while ((int)(P1.Y) != E.Y) {
			if (map[(int)P1.X][int(P1.Y)] == 1) {
				return false;
			}
			P1.Y += s;
		}
		return true;
	}
	if (S.Y == E.Y) {
		while ((int)(P1.X) != E.X) {
			if (map[(int)P1.X][int(P1.Y)] == 1) {
				return false;
			}
			P1.X+=s;
		}
		return true;
	}
	static long long  m;
	//X轴
	if (abs(dx) - abs(dy) >= 1e-3) {
		//CSystem::SetWindowTitle("X");

		while (int(P2.X) != E.X) {
			P2.SetXY(P1.X + s, P1.Y + Delta);
			//看个位有没有变
			if ((int)(P2.Y+1e-3) != (int)P1.Y && (int)P2.Y - P2.Y > 1e-3) {
				if (map[(int)P1.X][(int)P1.Y] == 1 || map[(int)P1.X][(int)P2.Y] == 1) {
					return false;
				}
			}
			else {
				if (map[(int)P1.X][(int)P1.Y] == 1) {
					return false;
				}
			}
			P1 = P2;
		}
	}
	else {//Y轴
		//CSystem::SetWindowTitle("Y");
		while ((int)P2.Y != E.Y) {
			P2.SetXY(P1.X + Delta, P1.Y + s);
			if ((int)(P2.X + 1e-3) != (int)P1.X && ((int)P2.X) - P2.X > 1e-3) {
				if (map[(int)P1.X][(int)P1.Y] == 1 || map[(int)P2.X][(int)P1.Y] == 1) {
					return false;
				}
			}
			else {
				if (map[(int)P1.X][(int)P1.Y] == 1) {
					return false;
				}
			}
			P1 = P2;
		}
	}
	return true;
}

其中Gird是一个bool数组,1代表有障碍物,0代表没有障碍物体。

利用的算法是Bresenham算法,这是一个效率很高的画线算法;

要注意,Theta算法计算h时要用欧拉距离,相对于对角距离和曼哈顿距离,欧拉距离搜索的结点数是最少的;

Theta算法的核心要义就是:

P点的邻居节点为Q点,此刻我们更新Q点

1:我们看Q点与P点的父节点是否有视线

    • g( Parent( P ) ) + EulerDistance( Q , Parent ( P ) ) < g(Q)时,我们将Q的父节点指向Parent( P );
    • 没有:推出
  •  没有:->经典A*路径

RTS游戏开发:基于Gird的Theta*寻路算法,以及利用视线算法优化A*路径的思路【附视线算法的代码、A*、Theta*的伪代码】_第3张图片

RTS游戏开发:基于Gird的Theta*寻路算法,以及利用视线算法优化A*路径的思路【附视线算法的代码、A*、Theta*的伪代码】_第4张图片

要注意,当我们确认Q与Parent(P)有视线时,我们就不用计算A*路径了,这个很容易证明,用三角形两边之和大于第三边就行了,直观理解就是上面两幅图。

用视线算法优化A*路径,以达到任意角的效果

除了用Theta*来达到任意角的效果,我们也可以直接用视线算法来优化计算出来的路径,将A*路径进行一个“拉直”。

思路如下:

我们将路径存储到一个list容器中;

两个点,PointBefore,PointLast,

起初PointBefore=PointLast=StartPoint【起点】

List NewPath;

NewPath.push_back(PointBefore);

while(PointBefore!=EndPoint){

    while(LineOfSight(PointBefore,PointLast)==1){
        PointLast=PointLast->NextPoint;//PointLast->NextPoint为容器中PointLast的下一个点
    }
    NewPath.push_back(PointLast.before);
    PointBefore=PointLast;
}

可以看出来,这是一个双指针算法;

循环完毕后NewPath就是一个新的路径了;

这个算法的时间复杂度继承了双指针算法的传统o(n);

其中n是路径长度;

你可能感兴趣的:(大数据,AStar,ThetaStar,Bresenham)