距离上次发布已经有了很长一段时间,期间由于各种原因没有更新这方面的技术分享,在这里深表遗憾。在MMO或其他的游戏中,会有针对各种形状的计算,通常在攻击区域里不会很复杂,常见的为矩形、圆形、扇形。今天分享的是判断一个目标点是否在扇形内的计算,用到的是比较简单的运算,高效率的算法我很尽快更新。

数学知识

  在这里需要大家回顾一下初中以及高中的代数和平面几何的知识,想必大部分的朋友在这方面和我一样几乎忘记了或是对这些数学知识感觉有些头痛。不过大家没有必要担心,在实际运用中,我们都不会涉及太复杂的计算,因为我们不需要追求的十分精确。

  但是在以下内容中,大家需要知道一些基本的定理和公式:勾股定理、余弦定理。

  需要了解弧度和角度的一些转换:角度 = 弧度 * 180.0 / ∏

  一些数学函数:atan2、acos等。

  中心线:以中心线顺、逆时针展开半角,形成一个完整的目标扇形区域。

  极坐标概念:

  如上图坐标点A的平面坐标为(7.96, 5.43),对应的极坐标为(ρ, θ)

  相对坐标的概念:

  在扇形的计算中,我们的X轴与Y轴的方向与原点的的X轴和Y轴平行,在上图中,如果以A点作为中心点,那么B的相对坐标为(xb - xa, yb - ya)。

扇形与点的关系

  示例图1:

  必要的数据: 原点A(攻击者坐标)、方向点B(direction)、目标点C(point)、扇形角度(β)、扇形的半径(r)。

  扇形的有效区域:

  如上图中的扇形的角度为180°,其有效面积为:以X轴为中心分布的可攻击区域1(area1紫色区域)和可攻击区域2(area2绿色区域)。

  如果目标点的极坐标为(ρ,Θ),那么上述的目标点判断为:目标点到原点A的距离小于扇形的半径(ρ <= r)、在区域1(0 <= Θ <= α + β / 2)或区域2((a - β / 2) + 360 <= Θ <= 360)。

  因为我们的极坐标的范围为(0-360),所以如果算出来的扇形最小的角度为负,需要转换为正来比较。

  从上述的判断中,并没有将所有的情况考虑到,如果需要的扇形角度超过180或者方向点落在不同的象限内,计算的方式是不同的,接下来再看一张示意图。

  示例图2:

  如上图所示,这样的面积计算就不能使用第一种方式,方向的极坐标的角度为315°,而扇形的角度的不过270°,那么α、β、γ到底变成了怎样的关系,我们求出了方向的极坐标是否能把这样两个面积的角度分别计算出来?

  区域1(0 <= Θ <= α + β / 2),如果是上图的数据则为0 <= Θ <= 315 + 270 / 2,范围就是[0,450],如果用词判断则象限2中的空白部分的本不符合的点就符合条件了,其实我们可以看出区域1的实际范围为[0,90],同理区域2的范围为[180,360]。而超过了360°的角度只要减去360,这里的450转换为正常角度则为90刚刚是我们的正确角度。而第一种方式中的区域二的区域为[a - β / 2,360],在上图中刚刚为[180,360]是符合的。

  如何判断一个扇形是否跨域了X轴正方向?

  因为我们的扇形是以中心线分成两个部分,那么只需要判断这两个部分是否超过了就可以,当前如果中心线在X轴正方向时绝对是跨域了两个方向。

  三种跨域的方式:示例图1中(a - β / 2)的值小于零、示例图2中的(α + β / 2)大于360、方向点在X轴正方向上(极坐标的角度Θ为0或绝对坐标X大于0和Y等于0)。

  同时两个扇形的角度需要转换:小于0的角度需要转换为正(示例图1中a - β / 2,加上360)、大于360的角度需要转换为360以内的角度(示例图中的α + β / 2,减去360)。

  那么两个区域就为[0, a + β / 2]、[a - β / 2, 360],需要注意这里的角度都需要进行上述的转换。

  示例图3:

  如果扇形的范围没有跨域X轴正方向,那么角度的范围是[a - β / 2, a + β / 2]。

代码实现

  我们知道了点和面的关系后,就能够对目标点进行判定了,编码实现才有了依据。(上面三种情况是否会有遗漏,暂时没有考虑过,欢迎大家指正)

  基础结构定义:

struct point_struct {
double x;
double y;
point_struct() : x{.}, y{.} {}
};
using point_t = point_struct;

  相对坐标转换(没有方向):

/**
* 没有方向的相对坐标转换,x、y轴的方向与原点相同
* @param origin 坐标系原点
* @param point 需要转换的点
*/
/**
* Y
* |
* | CY
* | |
* | | .point
* | |
* | |
* | origin----------------- CX
* |
* |
* O--------------------------------------------------- X
*
*/
point_t absolute_to_relative(const point_t &origin, const point_t &point) {
point_t result;
result.x = point.x - origin.x;
result.y = point.y - origin.y;
return result;
}

  极坐标转换:

/**
* 简单极坐标转换(转换后的x为斜边,y为角度),
* 减少数学函数调用(可以忽略,因为通常情况下这几种情况命中概率极低)
* @param point 需要转换的点
*/
point_t to_spolar_coordinate(const point_t &point) {
point_t result;
result.x = ;
result.y = -;
if ( == point.x == point.y) {
result.y = ;
return result;
}
if ( == point.y) {
result.x = abs(point.x);
result.y = point.x > ? : ;
return result;
}
if ( == point.x) {
result.x = abs(point.y);
result.y = point.y > ? : ;
return result;
}
if (abs(point.x) == abs(point.y)) {
result.x = 1.41421 * abs(point.x);
if (point.x > && point.y > ) {
result.y = ;
} else if (point.x < && point.y > ) {
result.y = ;
} else if (point.x < && point.y < ) {
result.y = ;
} else if (point.x > && point.y < ) {
result.y = ;
}
}
return result;
}
/**
* 转换为极坐标(转换后的x为斜边,y为角度)
* @param point 需要转换的点
*/
inline point_t to_polar_coordinate(const point_t &point) {
point_t result;
result.x = sqrt(point.x * point.x + point.y * point.y);
result.y = (180.0 / PI) * atan2(point.y , point.x); //弧度转角度
result.y = result.y < . ? result.y + 360.0 : result.y;
return result;
}

  判断是否在扇形内:

/**
* 判断一个点是否在扇形内(相对中心点)
* @param center 扇形的中心点
* @param direction 中心线的方向坐标
* @param r 半径
* @param angle 角度(0 < angle < 360)
* @param point 需要检查的点
*/
bool in_circular_sector(const point_t &center,
const point_t &direction,
double r,
int angle,
const point_t &point) {
//实际使用中,我们会把方向点的极坐标放到外部进行计算
point_t d_rpoint = absolute_to_relative(center, direction); //方向相对坐标
point_t d_pc_point = to_spolar_coordinate(d_rpoint); //方向极坐标
if (- == d_pc_point.y) { //简单的如果转换不出,则需要调用角度函数计算
d_pc_point = to_polar_coordinate(d_rpoint);
}
point_t rpoint = absolute_to_relative(center, point); //目标相对坐标
point_t pc_point = to_polar_coordinate(rpoint); //目标极坐标
if (pc_point.x > r) return false;
bool result = false;
auto half_angle = angle / ;
auto angle_counter = d_pc_point.y - half_angle; //中心线顺时针方向的范围
auto angle_clockwise = d_pc_point.y + half_angle; //中心线逆时针方向的范围
/*
std::cout << "angle_counter: " << angle_counter << " angle_clockwise: "
<< angle_clockwise << " d_pc_point.y" << d_pc_point.y << std::endl;
*/
if ( == d_pc_point.y || angle_counter < || angle_clockwise > ) {
angle_counter = angle_counter < ? angle_counter + : angle_counter;
angle_clockwise = angle_clockwise > ? angle_counter - : angle_counter;
if (pc_point.y >= && pc_point.y <= angle_counter) {
result = true;
} else if (pc_point.y >= angle_clockwise && pc_point.y <= ) {
result = true;
}
} else {
result = angle_counter <= pc_point.y && angle_clockwise >= pc_point.y;
}
return result;
}

  实际使用的优化:

  可以先求出圆范围内的所有玩家对象,这样可以减少atan2调用,其次方向点的极坐标放到循环之外,减少循环次数。

PF人员招募

开篇语
  我们没有大神,只有解决问题的人。
  我们没有强悍的技术,只有一颗向往简单的心。
  我们没有惊人的理论,只有一堆不可思议的妄想。
  我们不需要复杂,只需要够简洁。
  我们不需要固定的思维,只需要你能想得到。

PF托管地址

  https://github.com/viticm/plainframework1

PF安装教程

  http://www.cnblogs.com/lianyue/p/3974342.html

PF交流QQ群

  (同时欢迎技术人员加入进行交流)

MMORPG大型游戏设计与开发(攻击区域 扇形)的更多相关文章

  1. MMORPG大型游戏设计与开发(概述)updated

    1.定义 MMORPG,是英文Massive(或Massively)Multiplayer Online Role-PlayingGame的缩写,即大型多人在线角色扮演游戏. 2.技术与知识 在这系列 ...

  2. MMORPG大型游戏设计与开发(UI SYSTEM SHOW)

    接下来一段时间,这些文件可能不再更新,期间我会学习和掌握一些前端知识.虽然我非常欣赏剑侠网络版叁和九阴真经的画面,但是那是一个庞大的游戏引擎,一般人是无法窥伺的,除非你是天才而且要拥有机器毫无中断的毅 ...

  3. MMORPG大型游戏设计与开发(服务器 游戏场景 核心详述)

    核心这个词来的是多么的高深,可能我们也因为这个字眼望而却步,也就很难去掌握这部分的知识.之所以将核心放在最前面讲解,也可以看出它真的很重要,希望朋友们不会错过这个一直以来让大家不熟悉的知识,同我一起进 ...

  4. MMORPG大型游戏设计与开发(游戏服务器 游戏场景 概述 updated)

    我们在玩游戏的时候,我们进入游戏后第一眼往往都是看到游戏世界中的场景,当然除了个别例外,因为那些游戏将游戏场景隐藏了起来,如文字游戏中的地点一样.既然我们接触了游戏世界的核心,那么作为核心的场景又包括 ...

  5. MMORPG大型游戏设计与开发(客户端架构 part8 of vegine)

    脚本模块是游戏设计中争论比较多的话题,那是因为作为脚本本身所带来的利弊.其实这都无关紧要,取舍是人必须学会的一项技能,如果你不会取舍那么就让趋势给你一个满意的答复.自从魔兽世界以及传奇(世界)问世以来 ...

  6. MMORPG大型游戏设计与开发(服务器 游戏场景 地图和区域)

    地图的数据以及区域的信息是场景的重要组成部分,这些数据同时存在客户端和服务器,而且都是由编辑器生成的.那么保存的文件数据结构是怎样的?一张3D的场景地图又是怎样处理这些数据的?同时告诉大家这里同样只是 ...

  7. MMORPG大型游戏设计与开发(服务器 AI 基础接口)

    一个模块都往往需要统一的接口支持,特别是对于非常大型的模块,基础结构的统一性非常重要,它往往决定了其扩展对象的通用性.昨天说了AI的基本概述以及组成,作为与场景模块中核心一样重要的地位,基础部分的设计 ...

  8. MMORPG大型游戏设计与开发(服务器 AI 概述)

    游戏世界中我们拥有许多对象,常见的就是角色自身以及怪物和NPC,我们可以见到怪物和NPC拥有许多的行为,比如说怪物常常见到敌对的玩家就会攻击一样,又如一些NPC来游戏世界中走来走去,又有些怪物和NPC ...

  9. MMORPG大型游戏设计与开发(客户端架构 part16 of vegine)

    由于近来比较忙碌和有些困倦的原因,所以关于这部分的文章没有及时更新,一句话:让朋友们久等了!今天所讲的是客户端vengine(微引擎)中最后一个部分,就像上节所说,这一部分的内容比较多.可能有些朋友看 ...

随机推荐

  1. ASP.NET Core 1.1.0 Release Notes

    ASP.NET Core 1.1.0 Release Notes We are pleased to announce the release of ASP.NET Core 1.1.0! Antif ...

  2. “.Net 社区虚拟大会”(dotnetConf) 2016 Day 1 Keynote: Scott Hunter

    “.Net 社区虚拟大会”(dotnetConf) 2016 今天凌晨在Channel9 上召开,在Scott Hunter的30分钟的 Keynote上没有特别的亮点,所讲内容都是 微软“.Net社 ...

  3. Angular企业级开发(1)-AngularJS简介

    AngularJS介绍 AngularJS是一个功能完善的JavaScript前端框架,同时是基于MVC(Model-View-Controller理念的框架,使用它能够高效的开发桌面web app和 ...

  4. [C#] 简单的 Helper 封装 -- SecurityHelper 安全助手:封装加密算法(MD5、SHA、HMAC、DES、RSA)

    using System; using System.IO; using System.Security.Cryptography; using System.Text; namespace Wen. ...

  5. [Nginx笔记]关于线上环境CLOSE_WAIT和TIME_WAIT过高

    运维的同学和Team里面的一个同学分别遇到过Nginx在线上环境使用中会遇到TIME_WAIT过高或者CLOSE_WAIT过高的状态 先从原因分析一下为什么,问题就迎刃而解了. 首先是TIME_WAI ...

  6. Python多线程爬虫爬取电影天堂资源

    最近花些时间学习了一下Python,并写了一个多线程的爬虫程序来获取电影天堂上资源的迅雷下载地址,代码已经上传到GitHub上了,需要的同学可以自行下载.刚开始学习python希望可以获得宝贵的意见. ...

  7. 【云知道】究极秒杀Loadrunner乱码

    Loadrunner乱码一击必杀 之前有介绍一些简单的针对Loadrunner脚本或者调试输出内容中乱码的一些设置,但是并没能完全解决一些小伙伴的问题,因为那些设置实在能力有限,还是有很多做不到的事情 ...

  8. 异步 HttpContext.Current 为空null 另一种解决方法

    1.场景 在导入通讯录过程中,把导入的失败.成功的号码数进行统计,然后保存到session中,客户端通过轮询显示状态. 在实现过程中,使用的async调用方法,出现HttpContext.Curren ...

  9. mono for android学习过程系列教程(5)

    这一讲主要需要了解的安卓UI元素是Spinner.这个元素类似我们 winform和webform里面的下拉选项. 首先我们先建立一个新的项目,命名为SpinnerExample. 然后在Layout ...

  10. 四、可空类型Nullable<T>到底是什么鬼

    值类型为什么不可以为空 首先我们都知道引用类型默认值都是null,而值类型的默认值都有非null. 为什么引用类型可以为空?因为引用类型变量都是保存一个对象的地址引用(就像一个url对应一个页面),而 ...