| 
 | 
 
 
  GameRes游资网授权发布 文 / 肥宝传说之路 
 
  游戏技能攻击区域的计算,关乎服务端的效率。需要确保正确,简洁地计算攻击区域,才能快速寻找攻击对象。 
 
  今天只讨论地图上距离的问题。 
 
  一般情况下攻击区域分为以下几种: 
 
  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喷火。同时也要检测周围的玩家是否中招 
 
 
  判断一个点是否在矩形内是很简单的,如下: 
 
- //判断点是否在矩阵内  
 
 - 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的余弦,从而可以求出相对坐标。然后在判断是否在矩阵内。 
 
 
  3.扇形区域 
 
  攻击者对前方角度α,长度为L的区域进行攻击。如下图,攻击目标为B,要计算旁边的C是否也受到攻击 
 
- //计算两点之间的距离  
 
 - 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)  
 
 -     {  
 
 -         //受到攻击,攻击处理  
 
 -     }  
 
 - }         
 
  复制代码 
  扇形,当然是用极坐标最方便,判断一下距离,在半径范围内,然后判断一下角度是否适合。 
 
  策划配置:扇形半径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°之间  
 
 -     {  
 
 -         //受到攻击  
 
 -     }   
 
 - }  
 
  复制代码 
  分开算就行了,用个for循环,每个扇形分别计算。 
 
  4.圆形区域,圆形其实是最简单的了。 
 
 
  判断是否在攻击者半径范围内就行了。 
 
 
  对于多个圆形区域的计算 
 
- 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)//超过技能半径就无法攻击到了  
 
 -     {  
 
 -         //受到攻击  
 
 -     }   
 
 - }     
 
  复制代码 
  本质上还是一样,用一个for循环,计算出圆心的位置,然后计算点到圆心的距离就完成了。 
 
  相关阅读:MMO游戏数值建模-明澈流风-V1.0 
 
 |   
 
 
 
 |