MMO游戏技能攻击区域的计算3--效率分析
Posted 肥宝Fable
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MMO游戏技能攻击区域的计算3--效率分析相关的知识,希望对你有一定的参考价值。
本文来自肥宝游戏,引用必须注明出处!
对于攻击区域的计算,请看上两遍文章:
这两篇文章已经对攻击区域进行详细讲解,分为没划分格子和划分格子的情况。这里就不在详述了。
在前面的文章,已经得出结论:由于服务端的承载问题,需要对地图划分格子。
但是划分格子后,通过格子配置,也可以实现对圆形,扇形,矩形等图形的计算。但是在获得简便的计算之后,却是用精度来做代价。
所以今天要对两种方式,做一次效率的分析。
先确定一些基础条件:
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;
}
以上是关于MMO游戏技能攻击区域的计算3--效率分析的主要内容,如果未能解决你的问题,请参考以下文章