MMO游戏技能攻击区域的计算3--效率分析

Posted 肥宝Fable

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}




以上是关于MMO游戏技能攻击区域的计算3--效率分析的主要内容,如果未能解决你的问题,请参考以下文章

MMO游戏技能攻击区域的计算2--给地图划分格子

MMO技能系统的同步机制分析

MMO即时战斗:地图角色同步管理和防作弊实现

开始写游戏 --- 第二十六篇

开始写游戏 --- 第二十二篇

第8周工作内容