TrinityCore框架_地图管理
最近更新:2024-09-26
|
字数总计:2.6k
|
阅读估时:9分钟
|
阅读量:次
- 地图管理
- 概述
- 如何驱动地图数据
- 网络数据
- 定时更新
- 整体更新流程(***流程复述)
- 流程总结
- AOI
- 静态数据
- 动态数据
- 动态数据管理
- 碰撞检测
地图管理
概述
- 哪些模块会用到地图模块
- 移动(movements)
- 副本
- 寻路
- 。。。
- 地图模块要实现哪些功能
- AOI
- 管理地图地理信息
- 管理地图对象信息
- 功能
- 视野
- 数据同步
- 碰撞检测
- 寻路算法
- 如何驱动地图数据?
- 网络数据(玩家的活动)
- 定时的更新(地图中怪物活动等)
如何驱动地图数据
网络数据
- 数据流程图

- 红色,adapter到network thread
- 黄色,network thread到msg queue(解出具体的消息packet)
- 蓝色,main线程和mapUpdater线程交换map数据包
- 绿色,对外发送数据
- adapter接受连接,然后分配到负载均衡的network thread中
- network thread解包放入到msg queue中
- main线程不断轮询取出数据包,处理一部分网络数据(这里存在一个filter筛选需要直接在主线程处理的数据包,player在map中的就不处理,不在map中的,例如登录、查询数据等就直接处理),另一部分放入到map queue中。
- map线程不断从map queue中取数据进行处理。
定时更新
- dynamicTree.Update
- 一个组织对象(包围盒)的树状结构,方便我们快速检测对象碰撞
- 处理网络数据包
- 处理重生相关
- 处理玩家更新
- 处理视野
整体更新流程(***流程复述)
- main.cpp中使用threadpool中的thread创建多个共享io_context的工作线程,用来接收连接(然后进行负载均衡)
- sWorld->SetInitialWorldSettings()(主线程从配置中初始化map updater线程池(这里是另一个线程池)中的线程数量)
- sWorld->Update(在WorldUpdateLoop函数中,main.cpp中这里是死循环,1ms更新一下,也就是主线程中不断定时循环。这个函数结束后,就要释放线程池,reset context等相关释放清理工作了。)
- 通过fliter去筛选出在主线程中处理的网络packet(通过协议号,从NetWorkThread[]处理过后放入的msg queue中取出包,然后处理)
- sMapMgr->Update(这里主线程将包放到map queue中,以MapUpdateRequest这个类的形式,这也是在主线程中的)
- 此时主线程进入m_updater.wait状态,也就是说除非map queue中的updater都进行完,否则主线程就停止了
- 循环调用sWorld->Update
- m_map.Update(这里会另外有一个线程池,即map updater线程池,不断从queue中取出包去调用m_map.update,mapUpdter中的work thread不断从queue中取出数据操作。存在于map.h与map.cpp文件,也是地图管理的核心代码。)
- _dynamicTree.update(地图中的动态树包围盒更新,balance)
- 取出所有的用户的worldSession进行更新(这个worldSession是处理业务数据包的)

- player->update
- visitNearByCellsOf()(在当前地图,让我被其他用户看到)
- SendObjectUpdate()
- ScriptProcess()(处理额外脚本)
流程总结
- 主线程会等待所有map线程执行完,这是一个设计错误
- 如何修正此设计弊端:主线程可以作为map线程的一部分,不必等待map线程都执行完,参考redis io多线程设计
- 线程是不安全的,之所以这么设计是因为不同的map之间是不会有交集的
- 但是代价是假如有一个map线程宕机了,整个程序都会宕机,可以设计成多进程
AOI
- 概念:Area of interest是一种游戏中用于管理空间对象的算法,用于减少计算量
- 数据
- 静态数据
- 动态数据
静态数据
- 工具生成
- mapextractor
- *.map文件
- 游戏中地图数据(地形数据)
- 数据包括:area data;height data;liquid data;hole data等
- mmaps_generator
- .mmap文件、.mmtile文件
- 游戏中地图移动数据
- 主要是navmesh,导航数据
- vmap4extractor
- .vmo文件(建筑),.m2文件(3.5以后的版本)
- 场景信息(建筑物、山脉、水体等)
- *.mdx(物体包围盒信息)
- 3d模型信息(角色、怪物、装备等)
- 用于未来进行碰撞检测
- 目录:Buildings
- vmap4assenbler
- *.vmtile
- 生成vmap4数据库(将mmaps_generator和vmap4extractor生成的数据合并到vmap4文件夹中,未来只需要读取vmap4这个文件夹)
- 数据划分
- map对应64X64个grid
- 一个grid对应8X8个cell
- grid和cell都有自己的坐标系
- 以后的角色和怪物都会落到具体的cell中
- 数据组织
- 静态数据:GridMap GridMaps[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; // 6464,他是从前面生成的那些静态数据读取出来写入的
- 动态数据:NGridType* i_grids[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; // 64*64,比如说有玩家进入,生成的怪物放到这个里面
动态数据
- 动态数据(mmogame是为角色玩家而设计的,假如某个玩家进入某个map,那该map才会被加载,假如玩家在某个cell,那只会加载该grid在内存里,其余部分不加载)
- 数据组织
- NGridType* i_grids[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]
- MapRefManager m_mapRefManager,链表结构,当玩家登陆后,传送到上一次保存的地图位置(加载该map),就会调用map.m_mapRefManager.link(map, player),将该玩家加入到这个结构的最末位
- DynamicMapTree _dynamicTree
- 数据驱动
- 玩家进入区域后被加载激活
- 服务端的数据都是配合玩家进行表演
- 地图数据加载
- HandlerPlayerLogin
- AddPlayerToMap
- EnsureGridLoadedForActiveObject,加载地图数据主要代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| bool Map::EnsureGridLoaded(Cell const&cell) { EnsureGridLoadedForActiveObject(GridCoord(cell.GridX(),cell.GridY())); NGridType *grid = getNGrid(cell.GridX(),cell.GridY()); ASSERT(grid != nullptr); if(!gird->isGridObjectDataLoaded()) { grid->setGridObjectDataLoaded(true); ObjectGridLoader loader(*grid, this, cell); loader.LoadN(); Balance(); return true; } return false; }
|
- AddToGrid ,玩家也加入到动态数据i_grid中
- player->SetMap(this),将玩家也加入到m_mapRefManager结构中
- player->AddToWorld() ,将玩家也加入到_dynamicTree当中
- grid状态机
- 状态值
- Invalid
- Idle
- Active
- Removal
- 状态定时维护
- MapManager::Update(主线程)
- m_updater.shedule_update
- m_updater.wait()(主线程等待)
- DelayedUpdate(uint32(i_timer.GetCurrent()))(主线程)
- si_GridStates[grid->GetFridState()]->Update(*this, *grid, *info, t_diff);(主线程)
- 地图数据卸载
- LogoutPlayer
- _map->RemovePlayerFromMap(_player, true) // 此时不会立即卸载地图数据,而是会等定时器检测到idle状态一段时间后
- Invalid->Idle->Active->Idle->Removal
- map.UnloadGrid(grid, false) // removal状态一定时间后在主线程的DelayedUpdate中对removal状态的检测中,假如定时器到期调用map.UnloadGrid卸载数据
动态数据管理
碰撞检测
- 碰撞检测目标:对象模型
- 数据来源:加载地图数据时,vmap数据
- 功能:
- 是否在视线范围内
- 碰撞检测,算命中位置
- 获取地图信息
- 组织:
- 每一个grid对应一个BIH树,用于碰撞检测
- 一个map的grid是6464,BIH树也是6464,和grid一样,不是每一个grid都会被加载,只有玩家进入一个grid时才会加载,BIH树也是如此
- 实现
- AABB算法-轴对称边界盒算法——解决了光线与包围盒相交,避免费时计算
- 算法步骤
- 构建BIH树(快排思想)
- 计算包围盒(vmap4数据中已经有了,进行加载)
- 根节点包含该grid所有对象模型的包围盒
- 选取一个切割平面,将grid当中所有对象分为两个部分(这样,在碰撞检测的时候,可以每次排除一半)
- 将3个中的两个子集分别递归执行2,3,直到停止递归条件(递归深度为64就停止)
- 注意:每个节点是其子节点的并集,就是上层节点是包含下层节点对象的
- 光线追踪
- 从根节点开始遍历,判断节点是否与光线相交
- 如果不想交,直接跳过该节点,以及其子树
- 如果相交,则继续向子树遍历该节点的子树
- 作用
- 通过使用BIH树,可以有效减少碰撞检测次数,提高检测效率
- 但是构建和遍历都是需要计算成本,而且BIH树本身是不稳定的(随时有新加入节点或删除节点)
- 优化
- 初始化时构建一次
- 定时构建
- 如果BIH树发生改变才会重新构建
- 200ms才检测一次是否需要构建(不会实时构建,成本太大)
2024-06-23
该篇文章被 Cleofwine
归为分类:
Game