MMO游戲技能攻擊區域的計算3--效率分析


本文來自肥寶游戲,引用必須注明出處!

對於攻擊區域的計算,請看上兩遍文章:

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;
}





注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com