游戏中,”玩家”看到了”怪物”,怪物却没看到玩家,往前走几步,怪物发现了玩家,开始追击玩家攻击,这是如何实现的呢?

实现流程

精灵

无论是”怪物(Monster)”还是”玩家”(Role)甚至其他等等都可以被看作”精灵(Sprite)”,在游戏中 精灵都以”场景对象(SceneObj)”的形式来体现

视野的初始化是在帧更新中进行的,
AbstractWorld -> update 中 (Gamemap) map.update(_now); //世界地图刷帧
GameMap ->update 中 (ISceneObj) item.update
(now)_; //场景对象刷帧

场景对象接口(ISceneObj) 刷帧实现
image.png

SceneObj:场景对象

对象在刷帧时 在 initAOI( )中 通过 _getAoiManager().initAOI( )_; 初始化了该场景对象的视野

  1. public void initAOI() {
  2. ...验证
  3. IGameMap map = owner.getMap(); //拿到场景对象当前地图
  4. GridManager gridmanager = map.getGridManager();//拿到地图格子管理器 (视野是通过二维来实现的)
  5. gridmanager.addSceneObj(owner);//将自己放入到视野格子坐标列表数组内
  6. //获取地图视野长度
  7. int width = (int) ( (int)Math.max(1, map.getVisionLength() / gridmanager.getGridw()) * gridmanager.getGridw() * 2);
  8. //获取地图视野宽度
  9. int height = (int) ( (int)Math.max(1, map.getVisionLength() / gridmanager.getGridh()) * gridmanager.getGridh() * 2);
  10. //获得当前自身的格子
  11. pos = gridmanager.getGridPos((int)owner.getX(), (int)owner.getY(), owner);
  12. //获得当前的视野左上到右下的格子
  13. grids = map.getGridManager()
  14. .getDiaxgonalVertexNotFix((int)owner.getX(), (int)owner.getY(), width, height, owner);
  15. ...遍历发送

重点看

  1. gridmanager.addSceneObj(owner);//将自己添加到视野格子坐标内

    1. 两个数组 protected int[ ] _grids; protected int[ ] _pos ;
      pos : 场景对象当前x坐标 除 gridw,场景对象当前y坐标 除 gridh,gridw和gridh是格子的宽度,得到 角色当前的格子坐标
      grids:场景对象根据 公式(开始x点 = 当前x-((地图视距/4)42)/2,结束x点 = 当前x+((地图视距/4)42)/2,y点一样)
      目的 算出当前格子下,左上格子和右下格子的坐标点,取出一个对角线

      GridManager

      protected Map<_Integer, int[]> objOldPostion = new HashMap<>( )_;

1.initAOI时将场景对象 id和当前格子坐标put进入
2.checkAoiUpdate(),时,刷帧更新当前格子坐标
因为添加前先执行移除操作,这里防止并发问题加了lock锁

这里面维护了地图上的精灵,键是精灵的ID,值是地图的格子坐标,是除以 格子宽度 之后的坐标

上面算出场景对象的视野格子后,继续往下执行
doInDiagonalVertex(_int[] diagonalVertex(场景对象视野格子(上面算出的grids)), SceneObjProcessor processor(场景对象的消息处理器))
这个方法将_得到 grids 内所有的场景对象,并执行SceneObjProcessor的run方法,调用其本身的onSeeObj(obj)方法,同时,自身作为场景对象也会调用onSeeObj(obj)

你看见我,我看见你

GridManagerUseArrayList

继承自了 GridManager,在GameMap构造中创建,内维护了 sceneObjs 二维数组,数组最大值是地图的长度和宽度,获取场景对象格子坐标(obj.pos)时,都是在场景对象地图(sceneObjs)中拿到的

ISceneObj

通过消息下发实现 看见 onSeeObj(ISceneObj obj) 和 离开 onNotSeeObj(ISceneObj obj) 两个方法,作为接口,不同的场景对象可以有不同的实现方式

分析

1.通过抽象来实现不同精灵不同的视野实现,通过刷帧实现视野消息同步,通过坐标格子来确定场景对象的位置(坐标格子是根据角色的xy坐标来换算)
2.这块没理解通透,回头深刻理解了再回来补分析

脑图结构

视野管理.xmind

流程图

AOI视野初始化流程

可以直接从这个地方开始看起(aoi部分)image.png
AOI初始化.png

AOI刷帧消息流程

初始化后,

怪物 将视野范围内的可攻击精灵 添加进了仇恨列表(hateMap),

玩家和视野范围内的怪物 通过消息下发已经可以看到怪物,并且怪物也对玩家进行了移动同步消息下发

玩家视野范围内如果有玩家,则会触发视野事件,将玩家添加进入了自身 AOIManage eventMap 中

image.png