• 灯塔AOI简易实现


    首先我们来讨论下游戏开发中的几个坐标系,为了方便解释,我截取了灯塔AOI DEMO当NPC数目为0时候的样子(代码地址觉得有帮助的童鞋记得给我代码点个星_

    先对这张图简单说明下:

    • 蓝色的坐标轴表示是灯塔AOI坐标系,绿色的坐标轴表示的是游戏坐标系,向左为X轴正方向,向上为Y轴正方向(这个坐标是我自己后面画上去的)
    • 深蓝色的点表示灯塔AOI坐标,左下的表示(0,0),右上表示(1,1)
    • 深绿色的点表示游戏坐标,左下表示(0,0),右上表示(1,1)
    • 每个灰色的小格子代表一个游戏坐标(边长为15像素)
    • 每个黄灰的大格子代表一个灯塔AOI坐标(边长为11个灰色小格子)
    • 瓦片地图大小为宽:100个灰色小格子 高:70个灰色小格子
    • 深蓝色的方块为玩家,大红色方框为玩家在游戏坐标系中的视野(视野半径为10个灰色小格子),兰色方框表示玩家在灯塔坐标系中的视野

    像素

    这个是客户端图形实际显示要用到的坐标(和游戏逻辑无关),每个图片长宽各多少像素,描点在什么位置,但是在游戏中直接使用像素作为计量单位(坐标点)十分不方便,比如物体的移动,况且移动也不会一个像素一个像素移动,所以我们做了一层抽象(格子),游戏相关的贴图(地图/人物/道具)必须是格子的整数倍,这样游戏中所有的物体才能被正常显示,而不会产生偏差。

    格子/瓦片(游戏坐标系)

    什么是格子?格子是游戏逻辑使用的最小单位,代表游戏坐标系的点,它的单位为像素(DEMO中15*15像素为一格),游戏中所有设定都必须是格子的整数倍,所以不会出现有物体在两个格子之间,导致坐标不确定的情况,物体的移动的步长单位也变成多少格,而不是像素了,即如果使用像素作为坐标点,从(0,0)移动到(0,1),则移动了1个像素,现在用格子作为坐标点,从(0,0)移动到(0,1),则移动了15个像素。

    什么是瓦片?一张大的世界地图或者背景图可以由几种地形来表示,每种地形对应一张小的的图片,我们称这些小的地形图片为瓦片。把这些瓦片拼接在一起,一个完整的地图就组合出来了,这就是瓦片地图。

    在DEMO中,为了简化逻辑,我将瓦片设置为一倍格子大小,玩家和NPC的大小也设置为一倍格子大小。

    灯塔AOI坐标系

    为什么需要灯塔AOI?假设我们想知道某点周围10格内有哪些对象,在没有灯塔AOI的情况下,我们需要遍历所有的对象计算其是否在范围内,随着地图内的对象越来越多,查找的效率也会越来越差,所以我们需要一种方法来过滤那些明显不需要参与计算的对象,所以我们将地图分割成一个个区域,在其中心放置一个假想的"灯塔",每个"灯塔"都会保存区域内的对象,这样当我们需要知道某点周围10格内有哪些对象时,我们只需要计算出范围内有哪些"灯塔",然后获取这些"灯塔"保存的对象列表,针对这些对象进行计算就能节省大量计算。为了方便表示和管理这些"灯塔",我们为其分配了新坐标(左下为(0,0)),这个新的坐标系即灯塔AOI坐标系(这个坐标系是用来做碰撞检测的)

    灯塔视野和玩家视野

    "灯塔"的视野越小,在碰撞检测时能过滤的无效对象就越多,但是整张地图"灯塔"的数目也就越多,消耗的内存就越大,而且对象进出灯塔的计算量就越多

    "灯塔"的视野越大,在碰撞检测时能过滤的无效对象就越少,碰撞检测的计算量就越大,想象下只有一个"灯塔"的情况,即退回了没有灯塔AOI系统的情况

    由于玩家的视野一般是固定的(屏幕显示区域大小一般固定),所以灯塔视野大小一般是玩家视野的1/2或1/3比较合适

    灯塔坐标的计算

    假设要计算游戏中某点所处的灯塔坐标(详细逻辑见代码注释):

    ### 将游戏坐标系和灯塔AOI坐标系对齐,然后除以灯塔边长(两倍视野内包含的格子数 + 自身坐标格子)
    static_cast<int>(std::floor(static_cast<float>(游戏X坐标 - 游戏地图左下角原点X坐标)  /  (2  *  "灯塔"视野  +  1))
    static_cast<int>(std::floor(static_cast<float>(游戏Y坐标 - 游戏地图左下角原点Y坐标)  /  (2  *  "灯塔"视野  +  1))
    

    灯塔AOI逻辑

    对象进入(角色登入、生成怪物):

    • 根据对象坐标计算对象所属灯塔,将对象添加到灯塔的对象列表,如果灯塔上绑定了观察者,则通知观察者有对象进入
    • 找出对象视野范围的灯塔,将自身绑定为其观察者,绑定灯塔会将自身现有对象列表发送给对象

    对象离开(角色登出、怪物被杀死):

    • 根据对象坐标计算对象所属灯塔,将对象从灯塔的对象列表中移除,如果灯塔上绑定了观察者,则通知观察者有对象离开
    • 找出对象视野范围的灯塔,解除其观察者绑定,解除绑定灯塔会将自身现有对象列表发送给对象

    对象移动

    • 如果对象所属灯塔没变,则不做任何操作
    • 如果对象所属灯塔改变,则对旧灯塔执行对象离开逻辑,对新灯塔执行对象进入逻辑,但是要注意的是对视野的处理,前后视野交集内的灯塔不需要执行解绑和绑定操作
  • 相关阅读:
    迭代器和生成器
    案例:复制大文件
    案例:使用seek倒查获取日志文件的最后一行
    Leetcode165. Compare Version Numbers比较版本号
    Leetcode137. Single Number II只出现一次的数字2
    Leetcode129. Sum Root to Leaf Numbers求根到叶子节点数字之和
    Leetcode116. Populating Next Right Pointers in Each Node填充同一层的兄弟节点
    Leetcode114. Flatten Binary Tree to Linked List二叉树展开为链表
    Leetcode113. Path Sum II路径总和2
    C++stl中vector的几种常用构造方法
  • 原文地址:https://www.cnblogs.com/silvermagic/p/9373414.html
Copyright © 2020-2023  润新知