寻路算法的平滑处理

转载注明:http://blog.youkuaiyun.com/lqk1985/article/details/6679754

A*算法是根据可行走区域和不可行走区域wall得到的。一般用正方块模拟,当然也可以其他形状。

以前老的游戏或小游戏用A*,如果游戏行走的概念本来就是走格子的,格子会用很大,比如战棋类游戏,那直接用A*得到的路径没关系。

现有游戏分地图很大分辨率很高,wall的精度一般是10*10,还是比较小的,可以保证角色可以站在很靠近障碍物的地方。

微观:走格子 抖动

MMO游戏中角色行走的概念中其实是没有格子的。游戏中行走其实是根据给出的路径中的两点,判断朝向,播放相应的行走动画,移动(可以是地图动人不动,或人动地图不动)。

为了保证精度,我们使用的格子很小,得到的路径上点也就很多了。实际行走中将会发现角色行走时会不停抖动,其实是因为格子太小,路径点太近,人物不停在改变朝向。

只要保证路径上两点间的距离足够远就行了。我们需要在A*给出的由格子序列组成的路径上进行过滤。

取其中两个格子,判断两个点之间有无不可行走区域,如果没有,这两个格子之间的格子都可以从路径中移除。


例子:格子12345678910。

我们采用分治的方法,先取中间点5,看1-5和5-10直线连接上有没有不可行走区域。

如可以则把两个端点加入路径,中间的格子去掉,不可以就继续对两个端点之间进行分治。

private function getRoute(startX : int,startY : int,endX : int,endY : int,aStarPath : Array,wall:Wall):void {
			var xDistance : int = endX - startX;
			var yDistance : int = endY - startY;
			var distance : int = Math.round(Math.pow(xDistance * xDistance + yDistance * yDistance,1 / 2));
			var i : int;
			var tempX : int,tempY : int;
			var flag : Boolean = false;
			var isExistStart : Boolean = false;
			var isExistEnd : Boolean = false;
			var note : Array;		
		 	
		 	// 这一步作为递归的出口
			for (i = 1;i < distance;i += KEY_POINT_LENGTH) {
				tempX = Math.round(startX + i * xDistance / distance);
				tempY = Math.round(startY + i * yDistance / distance);			    
				if (wall.isWall(tempX,tempY)) {
					flag = true;	
					break;
				}		
			}
			// 如果没有障碍物,则将起始点加入到新路径并返回		  
			if (! flag) {	
				for (i = 0;i < route.length;i ++) {
					note = route[i];
					if (note[0] == startX && note[1] == startY)		isExistStart = true;
					if (note[0] == endX && note[1] == endY)			isExistEnd = true;
				}
				if (! isExistStart)		route.push([startX,startY]);
				if (! isExistEnd)		route.push([endX,endY]);
				return;
			}
		  	
			var tmpDistance : Number;
			var a : int,b : int,c : int,tmpX : int = startX,tmpY : int = startY,tmpPos : int;
			a = endY - startY;
			b = startX - endX;
			c = endX * startY - startX * endY;	
			tmpPos = 0;	  		 	  
			tmpDistance = 0;	
		    distance = 0; 
			
			var length:int = aStarPath.length;
			//有碰撞点则选择高度最大的点为关键点
			for (i = 0;i < length;i ++) { 
				note = aStarPath[i];
				tmpDistance = Math.abs((a * note[0] + b * note[1] + c) / Math.sqrt(a * a + b * b));
				if (distance < tmpDistance) {
					distance = tmpDistance;
					tmpX = note[0];
					tmpY = note[1];
					tmpPos = i + 1;	  		
				}		  		  	   
			}
			//防止栈溢出
			if (distance == 0){
				return;
			}
			//如果起始点不在路径中将起始点加入新路径		 
			for (i = 0;i < route.length;i ++) {
				note = route[i];
				if (note[0] == startX && note[1] == startY)  isExistStart = true;
			}
			if (! isExistStart)	route.push([startX,startY]);	
				//递归计算关键坐标并加入到路径数组 
			var tmpFirstArray : Array = new Array();
			for (i = 0;i < tmpPos;i ++) {
				tmpFirstArray[i] = aStarPath[i];
			}  
			var tmpLastArray : Array = new Array();
			for (i = tmpPos;i < length;i ++) {
				tmpLastArray[i - tmpPos] = aStarPath[i];	 
		
			}
			if (tmpFirstArray.length > 0)		getRoute(startX,startY,tmpX,tmpY,tmpFirstArray,wall);	
			if (tmpLastArray.length > 0)		getRoute(tmpX,tmpY,endX,endY,tmpLastArray,wall);
			//如果目标点不在路径中,将目标点坐标加入到新的路径		 
			for (i = 0;i < route.length;i ++) {
				note = route[i];
				if (note[0] == endX && note[1] == endY)		isExistEnd = true;	 
			}
			if (! isExistEnd)	route.push([endX,endY]);
			
			return;
		}

宏观:沿着不可行走区域走

现在角色行走时已经不抖动了,但是我们会发现角色有时会沿着不可行走区域走,而事实上某两点之间是有直线可以走的。

这其实是因为我们在分治过程中,这两个可直线行走的点被分到两段中,就不会去检测他们是否可以直线行走了。

这时经过上面针对微观抖动的过滤后,路径上的点已经比较少了。所以我们可以采用遍历路径上所有路点,两两之间是否可以直线行走,每次过滤掉的点接着的判断就不纳入计算,复杂度为O(N^2)。

当然可能还是有点沿着不可行走区域,因为整个过滤过程还是基于A*的,A*给出的路径本身就是有点沿着不可行走区域。

这样我们就可以获得比较不错的角色寻路体验了。

private function filter(route:Array, wall:Wall):void{
			GameStage.itemLayer.graphics.clear();
			GameStage.itemLayer.graphics.beginFill(0xffff00);
			for(var i:int=0; i<route.length; i++){
				//GameStage.itemLayer.graphics.drawRect(route[i][0], route[i][1], 5, 5);
			}
			
			var i:int=0;
			var j:int=route.length-1;
			
			while(j-i>1){
				
				while(j>i+1){
					var xDistance : int = route[j][0] - route[i][0];
					var yDistance : int = route[j][1] - route[i][1];
					var distance : int = Math.round(Math.pow(xDistance * xDistance + yDistance * yDistance,1 / 2));
					var tempX:int, tempY:int;
					var flag : Boolean = false;
					for(var k:int=1; k<distance; k+=KEY_POINT_LENGTH){
						tempX = route[i][0] + k * xDistance / distance;
						tempY = route[i][1] + k * yDistance / distance;	
						
						if (wall.isWall(Math.floor(tempX),Math.floor(tempY))
							&& wall.isWall(Math.floor(tempX),Math.ceil(tempY))
							&& wall.isWall(Math.ceil(tempX),Math.ceil(tempY))
							&& wall.isWall(Math.ceil(tempX),Math.floor(tempY))) {
							GameStage.itemLayer.graphics.beginFill(0xff00ff);
							GameStage.itemLayer.graphics.drawRect(tempX, tempY, 5, 5);
							flag = true;	
							break;
						}
					}
					if(!flag){
						GameStage.itemLayer.graphics.beginFill(0x00ffff);
						for(var d:int=i+1; d<j; d++){
							//GameStage.itemLayer.graphics.drawRect(route[d][0], route[d][1], 5, 5);
						}
						route.splice(i+1, j-i-1);
						break;
					}else{
						j--;
					}
				}
				i++;
				j=route.length-1;
			}
			//GameStage.itemLayer.graphics.endFill();
		}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值