天天看點

MMO遊戲技能攻擊區域的計算

本文來自肥寶傳說之路,引用必須注明出處!

遊戲技能攻擊區域的計算,關乎服務端的效率。需要確定正确,簡潔地計算攻擊區域,才能快速尋找攻擊對象。

今天隻讨論地圖上距離的問題。

一般情況下攻擊區域分為以下幾種:

1.點對點,對個人進行攻擊

2.射線攻擊,其實就是矩形區域

3.扇形攻擊

4.圓形攻擊

當然,還有其他情況,例如多區域和其他奇奇怪怪的形狀。不過考慮的實際觀賞價值,和精度的問題,多區域,隻考慮圓形和扇形,其他形狀也不考慮了。

釋放技能需要幾個事物,攻擊者,主要被攻擊者(也可能是攻擊地點),其他圍觀的群衆

class CPoint//點的定義
{
    double x;
    double y;
}
typedef std::vector<CPoint> SeqCPoint;
double skillDistance = 123;//技能釋放距離
CPoint attackerPoint;//攻擊者位置
CPoint defenserPoint;//被攻擊者位置或技能釋放點
SeqCPoint otherRoles;//其他需要檢測的角色
           

下面再細細講解:

====================================================================================================================================

1.點對點的攻擊

這個是最簡單的,隻要達到技能釋放的距離,就可以釋放。隻要攻擊者和被攻擊者的位置小于配置的skillDistance即可。

bool isFarThanDistance(CPoint a, CPoint b, double distance)
{
	double x = a.x - b.x;
	double y = a.y - b.y;
	if(x*x + y * y > distance *distance) return true;//超過距離
	return false;//未超過
}

if (!isFarThanDistance( attackerPoint, defenserPoint, skillDistance) )
{
//在技能範圍内,攻擊處理
}
           

====================================================================================================================================

2.射線攻擊,矩形區域

怪物向目标噴出一條長長的火線,在火線上的玩家受到攻擊,如下圖。A向B噴火。同時也要檢測周圍的玩家是否中招

MMO遊戲技能攻擊區域的計算

判斷一個點是否在矩形内是很簡單的,如下:

//判斷點是否在矩陣内
bool inRect( double minx, double miny, double maxx, double maxy, CPoint p)
{
    if(p.x >= minx && p.x <= maxx && p.y >= miny && p.y <= maxy) return true;
    return false;
}
           

但這個是在矩形的邊跟坐标軸平行的情況下的。如果攻擊者的攻擊方向跟坐标軸不平行,如上圖,就無法計算了

怎麼辦呢,如果能轉換成相對坐标就簡單很多了。 相對坐标的知識

要從絕對坐标轉換成相對坐标,需要确定相對坐标的原點和x軸方向。

原點是A點,也就是attackerPoint,X軸方向從A點指向B點。現在是求圖中C點的相對坐标。

ABC三點确定位置。根據餘弦定理可以求出角CAB的餘弦,進而可以求出相對坐标。然後在判斷是否在矩陣内。

//計算兩點之間的距離
double computeDistance(CPoint& from, CPoint& to)
{
	return sqrt(pow(to.x - from.x, 2) + pow(to.y - from.y, 2));
}
/**
* 直角坐标--絕對坐标轉相對坐标
* originPoint 相對坐标系的原點
* directionPoint 指向x軸方向的點
* changePoint 需要轉換的坐标
*/ 
CPoint changeAbsolute2Relative(CPoint originPoint, CPoint directionPoint, CPoint changePoint)
{
       //originPoint為圖中A點,directionPoint為圖中B點,changePoint為圖中C點
	CPoint rePoint;
	if (originPoint == directionPoint)//方向點跟原點重合,就用平行于原坐标的x軸來算就行了
	{//AB點重合,方向指向哪裡都沒所謂,肯定按原來的做友善
		rePoint.x = changePoint.x - originPoint.x;
		rePoint.y = changePoint.y - originPoint.y;
	}
	else
	{ 
		//計算三條邊
		//計算三條邊
		double a = computeDistance(directionPoint, changePoint);
		double b = computeDistance(changePoint, originPoint);
		double c = computeDistance(directionPoint, originPoint);
		
		double cosA = (b*b + c*c - a*a) / 2*b*c;//餘弦
		rePoint.x = a * cosA ;//相對坐标x
		rePoint.y = sqrt(a*a - rePoint.x*rePoint.x);//相對坐标y
	}
	return rePoint;
}	
for(SeqCPoint::iterator iter = otherRoles.begin();
	iter != otherRoles.end();
	iter ++)
{
	//檢測每一個角色是否在矩形内。	
	CPoint rePoint = changeAbsolute2Relative(attackerPoint, defenserPoint, *iter);//相對坐标
	//skillWidth為圖中寬度,skillLong為圖中長度
	//寬度是被AB平分的,從A點開始延伸長度
	bool beAttack = inRect(0, - skillWidth/2, skillLong, skillWidth/2, rePoint);//相對坐标下攻擊範圍不用算了,跟目标的相對坐标算一下
	if (beAttack)
	{
		//受到攻擊,攻擊處理
	}
}		
           

====================================================================================================================================

3.扇形區域

攻擊者對前方角度α,長度為L的區域進行攻擊。如下圖,攻擊目标為B,要計算旁邊的C是否也受到攻擊

MMO遊戲技能攻擊區域的計算

扇形,當然是用極坐标最友善,判斷一下距離,在半徑範圍内,然後判斷一下角度是否适合。

策劃配置:扇形半徑R和扇形總角度β

是以建構以A為原點的極坐标。根據中心線的角度α,求出扇形的角度範圍為[α-β/2,α+β]。再求出C點的極坐标進行比較

void changeXYToPolarCoordinate(Common::CPoint p, double& r, double& angle)
{ 	
	r = sqrt(p.x*p.x + p.y*p.y);//半徑
	angle = atan2(p.y , p.x) * 180/PI;//計算出來的是弧度,轉成角度,atan2的範圍是-π到π之間
	angle = (angle + 360)%360;
}
CPoint changeAbsolute2Relative(CPoint originPoint, CPoint changePoint)
{
	CPoint rePoint;
	rePoint.x = changePoint.x - originPoint.x;
	rePoint.y = changePoint.y - originPoint.y;
	return rePoint;
}
double baseR, baseAngle;
CPoint rePoint = changeAbsolute2Relative(attackerPoint, defenserPoint);//圖中B點的相對坐标
changeXYToPolarCoordinate(rePoint, baseR, baseAngle);//轉變成極坐标,baseAngle是角度
for(SeqCPoint::iterator iter = otherRoles.begin();
	iter != otherRoles.end();
	iter ++)
{
	CPoint rePointC = changeAbsolute2Relative(attackerPoint, iter2);//圖中C點相對坐标 
	double cr = 0;//極坐标半徑
	double cangle = 0;//極坐标角度
	changeXYToPolarCoordinate(rePointC, cr, cangle);
	if (cr > R)//超過技能半徑就無法攻擊到了
	{
		continue;
	}
	if ( abs(cangle - baseAngle) < β/2 )//相差的角度小于配置的角度,是以受到攻擊。要注意,這裡的角度都是在0°到360°之間
	{
		//受到攻擊
	} 
}
           

今天發現還有一種方法,就是利用向量的點積,可以百度一下。

CPoint rePoint = changeAbsolute2Relative(attackerPoint, defenserPoint);//圖中B點的相對坐标
double longB = sqrt(rePoint.x * rePoint.x + rePoint.y * rePoint.y);//長度
rePoint.x /= longB;
rePoint.y /= longB;//求機關向量
for(SeqCPoint::iterator iter = otherRoles.begin();
	iter != otherRoles.end();
	iter ++)
{
	CPoint rePointC = changeAbsolute2Relative(attackerPoint, iter2);//圖中C點相對坐标 
	double longC = sqrt(rePointC.x * rePointC.x + rePointC.y * rePointC.y);//長度
	rePointC.x /= longC;
	rePointC.y /= longC;//求機關向量
	double jiaodu = acos(rePoint.x * rePointC.x + rePoint.y * rePointC.y) * 180 /PI;//角CAB的大小
	if(jiaodu < β/2)
	//相差的角度小于配置的角度,是以受到攻擊。要注意,這裡的角度都是在0°到360°之間
	{
		//受到攻擊
	} 
}
           

對于扇形計算面積的優化,可以參考 這裡

再說一下多個扇形的情況

MMO遊戲技能攻擊區域的計算

分開算就行了,用個for循環,每個扇形分别計算。

====================================================================================================================================

4.圓形區域,圓形其實是最簡單的了。

MMO遊戲技能攻擊區域的計算

判斷是否在攻擊者半徑範圍内就行了。

for(SeqCPoint::iterator iter = otherRoles.begin();
	iter != otherRoles.end();
	iter ++)
{
	CPoint rePointC = changeAbsolute2Relative(attackerPoint, iter2);//圖中C點相對坐标 
	double cr = sqrt(rePointC.x*rePoint.x + rePointC.y*rePointC.y); //點到圓心的距離
	if (cr <= R)//超過技能半徑就無法攻擊到了
	{
		//受到攻擊
	} 
}	
           

對于多個圓形區域的計算

MMO遊戲技能攻擊區域的計算

本質上還是一樣,用一個for循環,計算出圓心的位置,然後計算點到圓心的距離就完成了。

MMO遊戲技能攻擊區域的計算

繼續閱讀