本文来自肥宝游戏,引用必须注明出处!
对于攻击区域的计算,请看上两遍文章:
MMO游戏技能攻击区域的计算
MMO游戏技能攻击区域的计算2--给地图划分格子
这两篇文章已经对攻击区域进行详细讲解,分为没划分格子和划分格子的情况。这里就不在详述了。
在前面的文章,已经得出结论:由于服务端的承载问题,需要对地图划分格子。
但是划分格子后,通过格子配置,也可以实现对圆形,扇形,矩形等图形的计算。但是在获得简便的计算之后,却是用精度来做代价。
所以今天要对两种方式,做一次效率的分析。
先确定一些基础条件:
1.把地图格子设置为20*20,整个地图就是220*220,分成11*11个格子。
2.每个格子让一个角色站着。
代码篇幅太大,放到末尾了!!!
统计结果:
柱形图:
最后总结:
1测试了几次,发现相差都不大,就不算平均值了,只取某一次的数据。
2对于同一种区域计算,提升执行次数,基本是等比例提升。
3扇形可能会同时出现多个,因为在实际游戏中,可能玩家会站得比较近而打不到的情况,这个时候用一个半径比较小,角度比较大的扇形来补充比较合适。
4格子少的时候效率是很高的,但是格子多了,效率就降低了。
换算一下,一个矩形相当于18个格子的计算量,一个扇形相当于6到七个。
至于什么哪个方式比较时候,我建议是各种方式的代码都需要写,到时候在根据具体格子大大小,还有技能区域的大小,判断一下能用多少个格子来表示攻击区域。在进行选择
5必须提到一点,一开始函数参数没有使用引用,执行时间差不多是现在给出来的数据的3倍!尤其是格子算法函数调用多,造成很大开销,差距更大。所以没啥事要使用引用,否则老是创建新的对象,消耗很大的。
6想不到扇形这些的计算量并不是很大,不要以为算法复杂,计算消耗就大,还是需要实际测试一下,可能出乎你意料!!!!!!
=======================================================================
下面是具体的测试代码
MapManager.h
// // Header.h // HelloWorld // 关注微信公众号:传说之路,大家共同学习 // Created by feiyin001 on 16/4/3. // Copyright (c) 2016年 FableGame. All rights reserved. // #ifndef HelloWorld_Header_h #define HelloWorld_Header_h #include <stdio.h> #include <vector> #define PI 3.1412;//圆周率 #define CellBase 20;//格子的大小 namespace FableGame { struct SPoint { int x; int y; }; typedef std::vector<SPoint> SeqSPoint; static const int rectWidth = 500 ;//矩形区域的宽度,像素 static const int rectHeight = 200 ;//矩形区域的高度,像素 //地图上各种处理都放在这里 class CMapManager { public: //判断两点间是否超过一定距离 static bool isFarThanDistance(SPoint& a, SPoint& b, int distance); //判断一个点是否在矩形内,这个要求与坐标轴平行 static bool inRect( double minx, double miny, double maxx, double maxy, SPoint& p); //判断一个点是否在矩形内, static bool inRectRelat( SPoint& originPoint, SPoint& directionPoint, SPoint& checkPoint); //判断是否在扇形内 static bool checkInFan( SeqSPoint& angleConfigs, SPoint& originPoint, SPoint& directionPoint, SPoint& checkPoint ); //计算两点之间的距离 static double computeDistance(SPoint& from, SPoint& to); /** * 直角坐标--绝对坐标转相对坐标 * originPoint 相对坐标系的原点 * directionPoint 指向x轴方向的点 * changePoint 需要转换的坐标 */ static SPoint changeAbsolute2Relative(SPoint& originPoint, SPoint& directionPoint, SPoint& changePoint); //这个转换的坐标轴是跟原来的平行的 static SPoint changeAbsolute2Relative(SPoint& originPoint, SPoint& changePoint); //======检测是否在格子配置的图形里面====== static bool checkInCell(SeqSPoint& pointConfigs, SPoint& originPoint, SPoint& checkPoint); }; } #endif
MapManager.cpp
// // MapManager.cpp // HelloWorld // 关注微信公众号:传说之路,大家共同学习 // Created by feiyin001 on 16/4/3. // Copyright (c) 2016年 FableGame. All rights reserved. // #include "MapManager.h" #include <math.h> using namespace FableGame; //判断两点间是否超过一定距离 bool CMapManager::isFarThanDistance(SPoint& a, SPoint& b, int distance) { //求出相对距离xy int x = (a.x - b.x) * CellBase;//坐标点都是格子的坐标点,所以要乘以格子的长度 int y = (a.y - b.y) * CellBase; if(x * x + y * y > distance *distance) return true;//超过距离(勾股定理) return false;//未超过 } //判断一个点是否在矩形内,这个要求与坐标轴平行 bool CMapManager::inRect( double minx, double miny, double maxx, double maxy, SPoint& p) { //判断点p的xy是否在矩形上下左右之间 if(p.x >= minx && p.x <= maxx && p.y >= miny && p.y <= maxy) return true; return false; } //计算两点之间的距离 double CMapManager::computeDistance(SPoint& from, SPoint& to) { return (sqrt(pow(to.x - from.x, 2) + pow(to.y - from.y, 2)))* CellBase; } //直角坐标--绝对坐标转相对坐标 SPoint CMapManager::changeAbsolute2Relative( SPoint& originPoint,//相对坐标系的原点 SPoint& directionPoint,//指向x轴方向的点 SPoint& changePoint)//需要转换的坐标 { //originPoint为图中A点,directionPoint为图中B点,changePoint为图中C点 SPoint rePoint; if (originPoint.x == directionPoint.x && originPoint.y == directionPoint.y)//方向点跟原点重合,就用平行于原坐标的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 / CellBase;//相对坐标x rePoint.y = sqrt(a*a - a * cosA * a * cosA) / CellBase;//相对坐标y } return rePoint; } bool CMapManager::inRectRelat( SPoint& originPoint, SPoint& directionPoint, SPoint& checkPoint) { //检测每一个角色是否在矩形内。 SPoint rePoint = changeAbsolute2Relative(originPoint, directionPoint, checkPoint);//相对坐标 //skillWidth为图中宽度,skillLong为图中长度 int skillWidth = rectWidth/CellBase;//矩形攻击区域的宽度 int skillLong = rectHeight/CellBase;//矩形攻击区域的高度 //宽度是被AB平分的,从A点开始延伸长度 return inRect(0, - skillWidth/2, skillLong, skillWidth/2, rePoint);//相对坐标下攻击范围 } SPoint CMapManager::changeAbsolute2Relative(SPoint& originPoint, SPoint& changePoint) { SPoint rePoint; rePoint.x = changePoint.x - originPoint.x; rePoint.y = changePoint.y - originPoint.y; return rePoint; } bool CMapManager::checkInFan( SeqSPoint& angleConfigs, SPoint& originPoint, SPoint& directionPoint, SPoint& checkPoint ) { //先求主目标的单位向量 SPoint rePoint = CMapManager::changeAbsolute2Relative(originPoint, directionPoint);//攻击者与主目标的向量 double longB = sqrt(rePoint.x * rePoint.x + rePoint.y * rePoint.y) ;//长度 rePoint.x /= longB; rePoint.y /= longB;//求单位向量 for (SeqSPoint::iterator anIter = angleConfigs.begin(); anIter != angleConfigs.end(); anIter++) { if (CMapManager::isFarThanDistance( originPoint, checkPoint, anIter->y))//检测是否在扇形的半径范围外 { return false; } //然后求出检测点的向量 SPoint rePointC = CMapManager::changeAbsolute2Relative(originPoint, checkPoint);//图中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;//实际的角度大小 double angleBeta = anIter->x; if ( jiaodu < angleBeta) {//相差的角度小于配置的角度,所以受到攻击。要注意,这里的角度都是在0°到360°之间 return true;//在角度范围内 } } return false; } bool CMapManager::checkInCell(SeqSPoint& pointConfigs, SPoint& originPoint, SPoint& checkPoint) { SPoint rePoint = changeAbsolute2Relative(originPoint, checkPoint);//计算出相对位置 //判断是否跟配置某一点相同 for (SeqSPoint::iterator iter = pointConfigs.begin();//检测是否重合的点 iter != pointConfigs.end() ; iter++) { if (iter->x == rePoint.x && iter->y == rePoint.y) { return true; } } return false; }
main.cpp
// // main.cpp // HelloWorld // 关注微信公众号:传说之路,大家共同学习 // Created by feiyin001 on 16/4/3. // Copyright (c) 2016年 FableGame. All rights reserved. // #include <iostream> #include "stdio.h" #include "stdlib.h" #include "time.h" #include "MapManager.h" using namespace FableGame; int main(int argc, const char * argv[]) { //==========先确定一些基础的内容================ int skillDistance = 1000;//技能释放距离 SPoint attackerPoint;//攻击者位置 attackerPoint.x = 0;//攻击者位置 attackerPoint.y = 1;//攻击者位置 SPoint defenserPoint;//被攻击者位置或技能释放点 defenserPoint.x = 8;//被攻击者位置或技能释放点 defenserPoint.y = 8;//被攻击者位置或技能释放点 SeqSPoint otherRoles;//其他需要检测的角色 //其他角色位置,为了方便测试,在每个格子都放一个人吧。 //otherRoles其他需要检测的角色 for (int i = 0; i <= 10; i++) { for (int j = 0; j <= 10; j++) { SPoint p; p.x = i; p.y = j; otherRoles.push_back(p); } } int count = 1000000;//提高执行次数有助于减低一些公共开销的时间的比例 std::cout << "Fable Game! 执行次数次数"<<count<<std::endl; //=====啥都不做,看看时间========= { clock_t startTime = clock();//开始时间 int i = 0; while (i < count) { i++; } clock_t endTime = clock();//结束时间 std::cout << "什么都不做,就循环1000次的时间:"<< (endTime-startTime)/1000 <<"毫秒"<<std::endl; //除以1000,是因为MacBook中使用微秒的,这里用毫秒把 } //=====点对点的检测========= { clock_t startTime = clock();//开始时间 int i = 0; while (i < count) { i++; for (SeqSPoint::iterator iter = otherRoles.begin(); iter != otherRoles.end(); iter++) { if (CMapManager::isFarThanDistance(attackerPoint, *iter, skillDistance)) { //在攻击范围内的点,可以进行攻击 } } } clock_t endTime = clock();//结束时间 std::cout << "点对点的检测,使用时间:"<< (endTime-startTime)/1000 <<"毫秒"<<std::endl; //除以1000,是因为MacBook中使用微秒的,这里用毫秒把 } //=====扇形========= { SeqSPoint angleConfigs;//扇形的配置 for (int cellNum = 1; cellNum <= 3; cellNum++) { SPoint p;//p点的具体值在这里没意义的,但是这里想让它跑完整个循环 p.x = 60;//角度 p.y = 1000;//半径 angleConfigs.push_back(p); clock_t startTime = clock();//开始时间 int i = 0; while (i < count) { i++; for (SeqSPoint::iterator iter = otherRoles.begin(); iter != otherRoles.end(); iter++) { if (CMapManager::checkInFan(angleConfigs, attackerPoint, defenserPoint, *iter)) { //在扇形范围内的点,可以进行攻击 } } } clock_t endTime = clock();//结束时间 std::cout << "扇形算法的检测,扇形的个数"<<angleConfigs.size()<<"使用时间:"<< (endTime-startTime)/1000 <<"毫秒"<<std::endl; //除以1000,是因为MacBook中使用微秒的,这里用毫秒把 } } //=====矩形========= { clock_t startTime = clock();//开始时间 int i = 0; while (i < count) { i++; for (SeqSPoint::iterator iter = otherRoles.begin(); iter != otherRoles.end(); iter++) { if (CMapManager::inRectRelat(attackerPoint, defenserPoint, *iter)) { //在矩形范围内的点,可以进行攻击 } } } clock_t endTime = clock();//结束时间 std::cout << "矩形算法的检测,使用时间:"<< (endTime-startTime)/1000 <<"毫秒"<<std::endl; //除以1000,是因为MacBook中使用微秒的,这里用毫秒把 } //=====圆形========= { clock_t startTime = clock();//开始时间 int i = 0; while (i < count) { i++; for (SeqSPoint::iterator iter = otherRoles.begin(); iter != otherRoles.end(); iter++) { if (CMapManager::isFarThanDistance(attackerPoint, *iter, skillDistance)) { //在攻击范围内的点,可以进行攻击 } } } clock_t endTime = clock();//结束时间 std::cout << "圆形算法的检测,使用时间:"<< (endTime-startTime)/1000 <<"毫秒"<<std::endl; //除以1000,是因为MacBook中使用微秒的,这里用毫秒把 } //========================================================== //================通过格子的配置来实现各种形状=================== //========================================================== { SeqSPoint pointConfigs; for (int cellNum = 1; cellNum <= 20; cellNum++) { SPoint p;//p点的具体值在这里没意义的,但是这里想让它跑完整个循环 p.x = 10000; p.y = 10000; pointConfigs.push_back(p); clock_t startTime = clock();//开始时间 int i = 0; while (i < count) { i++; for ( SeqSPoint::iterator iter = otherRoles.begin(); iter != otherRoles.end(); iter++) { if (CMapManager::checkInCell(pointConfigs, attackerPoint, *iter) ) { //在攻击范围内的点,可以进行攻击 } } } clock_t endTime = clock();//结束时间 std::cout << "格子算法的检测,格子数"<<pointConfigs.size()<<"使用时间:" << (endTime-startTime)/1000 <<"毫秒"<<std::endl; //除以1000,是因为MacBook中使用微秒的,这里用毫秒把 } } return 0; }