Grid系统概述
目录
概念及功能
网格对象(Grid Object) 和世界对象(World Object)
工作流程
-
概念及功能
TrinityCore 的 Grid 系统是一套复杂的地图分区管理机制,其核心目标是通过动态管控游戏世界的区域状态和对象生命周期,在保证玩家体验的同时最大化服务器性能。它具有以下功能:
-
地图划分与定位
TrinityCore 将每一张地图(Map)都划分成 64*64 的小方块,即网格(Grid),每个 Grid 的大小是 533.33 码。网格的坐标从右下角 (0,0) 到左上角 (63,63),以 (32,32) 为中心点。这种划分方式便于服务器和客户端对地图进行管理和定位,例如可以在GM模式下使用 “.go grid 48,18” 类似命令跳到当前地图编号为 (48,18) 的格子。 -
资源加载与卸载
客户端和服务端均按 Grid 为单位进行地图资源的加载和卸载。当角色在地图中移动时,客户端只加载角色附近的 Grid,当角色走到一个 Grid 边界时,再新加载相邻的 Grid,同时卸载远离角色的 Grid,从而有效控制内存占用。服务端也是如此,只有当某个 Grid 有角色进入时,才会将其标记为活跃状态,加载其中的动态信息,如怪物、NPC、矿点等;当 Grid 内没有活跃角色且超过一定时间后,会将其标记为不活跃状态,卸载其中的动态对象,回收 Grid。 -
对象管理
Grid 用于管理其中的各种对象,包括网格对象(grid object)和世界对象(world object)。Grid 类提供了一系列方法来插入、移除和统计这些对象,如add_grid_object
方法用于将一个对象插入到网格中,get_world_object_countingrid
方法用于获取网格中世界对象的数量。 -
寻路支持
Grid 与寻路功能相关,TrinityCore 可以利用导航网格(Grid)做寻路,通过 Detour 库等实现角色在地图上的路径规划,确保角色能够在网格划分的地图上顺利找到目标位置。
-
-
网格对象(Grid Object) 和世界对象(World Object)
-
世界对象(World Object)
是游戏世界中所有 “可交互实体” 的基类(WorldObject
),代表游戏中具有独立存在意义的实体,例如角色(Player)、NPC(Creature)、物品(Item)、动态物体(GameObject,如箱子、门)等。世界对象(World Object)是游戏逻辑的核心载体,拥有独立的属性(如位置、方向、状态)和行为(如移动、战斗、交互),并直接参与游戏玩法(如任务、战斗、交易)。- 世界对象负责实现具体的游戏逻辑,例如:处理玩家输入(移动、技能释放),NPC 的 AI 行为(巡逻、攻击、对话),物品的拾取、使用效果,碰撞检测、交互判定(如点击 NPC 触发对话),等等。
- 世界对象生命周期与实体本身绑定:例如玩家登录时创建,下线时销毁;NPC 被刷新时创建,被删除(如死亡后消失)时销毁。
- 当世界对象(如角色移动)从一个 Grid 进入另一个 Grid 时,其对应的网格对象会从旧 Grid 移除,添加到新 Grid 中。
-
世界对象在整个游戏世界中具有唯一标识(GUID),可被全局查询和引用。
-
世界对象是基类,派生类包括Corpse、
Creature
、DynamicObject、GameObject
、Player
、AreaTrigger、SceneObject、Conversation等,每个派生类都有独特的属性和方法(如Player
有背包系统,Creature
有 AI 组件)。
-
网格对象(Grid Object)
是与 Grid 网格绑定的 “辅助管理对象”(GridObject
),主要用于衔接世界对象与 Grid 系统,负责将世界对象 “锚定” 到具体的网格中,以便 Grid 系统对其进行定位、加载 / 卸载和区域管理。它更像是世界对象在 Grid 系统中的 “代理” 或 “索引”,本身不直接参与游戏逻辑,仅用于 Grid 内部的管理和查询。- 网格对象主要服务于 Grid 系统的管理需求,例如:记录世界对象在 Grid 中的位置偏移,辅助 Grid 快速定位实体;作为 Grid 内部的 “容器项”,参与 Grid 的对象列表维护(如添加、移除、遍历);触发 Grid 的状态变化(如当 Grid 中首个世界对象进入时,标记 Grid 为 “活跃” 并加载资源)。
- 网格对象生命周期与 Grid 绑定:当一个世界对象进入某个 Grid 时,Grid 会为其创建对应的网格对象;当世界对象离开 Grid 或 Grid 被卸载时,网格对象会被销毁。
- 网格对象仅存在于所属 Grid 中:每个网格对象严格属于一个 Grid,用于在该 Grid 内标记世界对象的存在,方便 Grid 快速定位其中的所有实体。
- 网格对象是模板类(
GridObject<T>
),其中T
通常是WorldObject
的派生类(如GridObject<Creature>
),它内部持有一个指向对应世界对象的指针,实现 Grid 与世界对象的关联。
-
-
工作流程
工作流程分为初始化与地图划分、Grid 激活与对象加载、活跃状态管理、Grid 失活与资源回收四个阶段,各阶段紧密衔接,形成完整的闭环逻辑。
一、初始化与地图划分:构建 Grid 的 “骨架”
Grid 系统的工作从地图(Map)加载开始,当服务器启动或角色首次进入某张地图时,系统会先完成 Grid 的基础构建:
-
地图元数据加载
从数据库(如map
表)和地图资源文件(.adt
等)中读取地图的基础信息,包括地图 ID、尺寸、地形数据等。每张地图被视为一个独立的 “世界区域”,由Map
类实例管理。 -
Grid 网格划分
按照固定规则将地图划分为64×64 的 Grid 网格(共 4096 个 Grid),每个 Grid 的物理尺寸为 533.33 码(约 133.33 游戏单位)。网格坐标以地图右下角为原点 (0,0),左上角为 (63,63),形成类似二维数组的索引体系(如 Grid (10,20) 代表第 10 列、第 20 行的网格)。 -
Grid 基础实例化
为每个 Grid 创建基础实例(Grid
类对象),此时的 Grid 仅包含坐标信息和空的对象列表,处于 **“未初始化” 状态 **(不加载任何世界对象,仅作为占位符存在)。这些 Grid 由GridManager
统一管理,通过地图 ID+Grid 坐标可快速定位。
二、Grid 激活与对象加载:从 “休眠” 到 “活跃”
Grid 默认处于 “休眠” 状态,仅当需要响应用户交互时才被激活。“有玩家角色进入” 是触发 Grid 激活的核心条件。
-
玩家位置检测
当角色(Player
对象)通过移动、传送、复活等方式进入某 Grid 时,系统通过角色坐标计算其所在的 Grid 坐标(如通过Map::GetGridX()
和Map::GetGridY()
方法),并检查该 Grid 的当前状态。 -
Grid 状态切换:从非活跃到活跃
- 若 Grid 处于 “非活跃状态”(无玩家且未加载对象),系统会将其标记为 “活跃状态”(Active),并触发
Grid::Activate()
方法。 - 激活时,Grid 会向
Map
模块注册自身,加入 “活跃 Grid 列表”,以便后续接收定期更新(如每 100ms 一次的逻辑帧更新)。
- 若 Grid 处于 “非活跃状态”(无玩家且未加载对象),系统会将其标记为 “活跃状态”(Active),并触发
-
世界对象加载
激活后,Grid 开始加载其内 “应存在” 的世界对象(WorldObject)。- 查询数据源:根据地图 ID 和 Grid 坐标,从数据库(如
creature
、gameobject
表)查询该 Grid 内的静态对象(如固定刷新的 NPC、门、矿点)和动态对象(如任务触发的怪物)。 - 实例化对象:根据查询结果,创建对应的世界对象实例(如
Creature
、GameObject
),初始化其属性(位置、方向、状态、AI 脚本等)。 - 绑定网格对象(GridObject):为每个世界对象创建对应的
GridObject
(网格对象),作为其在 Grid 中的 “代理”,并将GridObject
添加到 Grid 的对象列表(Grid::m_objects
)中。 - 通知周边系统:对象加载完成后,通知视野管理模块(让附近玩家可见)、AI 系统(启动 NPC 巡逻 / 战斗逻辑)、事件系统(注册交互事件,如点击 NPC 触发对话)。
- 查询数据源:根据地图 ID 和 Grid 坐标,从数据库(如
三、活跃状态管理:维持对象交互与更新
Grid 处于活跃状态时,核心任务是实时管理其内对象的状态和交互,确保游戏逻辑正常运行,主要包括以下操作:
-
定期逻辑更新
Grid 会随服务器的逻辑帧(通常每 100ms 一次)执行Grid::Update()
方法,完成:- 对象状态同步:更新世界对象的位置、血量、状态(如 NPC 的巡逻路径进展、GameObject 的开关状态)。
- AI 与行为驱动:触发 NPC 的 AI 逻辑(如检测玩家进入仇恨范围、执行技能)、物品的定时消失(如掉落的装备过期)。
- 事件检测:检查对象间的交互(如玩家点击门、拾取物品),触发对应的游戏事件(如任务进度更新、成就解锁)。
-
对象跨 Grid 移动处理
当世界对象(如玩家、移动的 NPC)从当前 Grid 进入相邻 Grid 时,触发对象转移流程:- 旧 Grid 移除对象:原 Grid 通过
Grid::RemoveObject()
方法,将对象的GridObject
从自身列表中移除,并通知旧 Grid(若此时旧 Grid 内无玩家,开始失活倒计时)。 - 新 Grid 添加对象:新 Grid 检测到角色进入后,若自身处于非活跃状态则立即激活,然后通过
Grid::AddObject()
方法创建新的GridObject
,将对象纳入管理,并同步其状态到新 Grid 及周边系统。
- 旧 Grid 移除对象:原 Grid 通过
-
动态对象生成与销毁
对于临时产生的对象(如玩家召唤的宠物、技能创建的幻象、任务触发的怪物),Grid 会在其生成时立即创建GridObject
并纳入管理;当对象过期(如宠物消失、幻象时间结束)或被销毁(如怪物被击杀)时,Grid 会移除其GridObject
并回收内存。
四、Grid 失活与资源回收:从 “活跃” 到 “休眠”
当 Grid 内不再有角色活动时,系统会逐步卸载资源,释放服务器内存和 CPU 占用。
-
失活触发条件
- 所有角色离开该 Grid(如移动到其他 Grid、下线、传送);
- Grid 内无角色的时间超过阈值(默认约 5 分钟,可通过配置调整)。
满足条件后,Grid 被标记为 “待失活状态”,触发Grid::Deactivate()
方法。
-
对象筛选与卸载
Grid 会先筛选可卸载的对象,保留必要数据:- 不可卸载对象:角色(
Player
)本身不会随 Grid 失活卸载(其生命周期与登录状态绑定);带有 “永久保存” 标记的对象(如玩家放置的营地)会保留状态。 - 可卸载对象:大部分 NPC、GameObject、临时物品等,会先将状态同步到数据库(如 NPC 的刷新时间、已打开的箱子状态),然后销毁其实例和对应的
GridObject
。
- 不可卸载对象:角色(
-
Grid 资源回收
- 清空 Grid 的对象列表,释放与对象相关的临时数据(如视野缓存、AI 状态)。
- Grid 从 “活跃 Grid 列表” 中移除,回到 “非活跃状态”,仅保留基础坐标信息,进入 “休眠”。
- 若后续有角色再次进入该 Grid,重复 “激活→加载” 流程。
五、特殊场景的补充处理
Grid 系统针对特殊需求做了优化,确保极端场景下的稳定性:
- 强制激活 / 失活:GM 可通过指令(如
.go grid
、.grid activate
)强制激活某 Grid,用于调试或事件触发;也可强制失活 Grid 以快速释放资源。 - 超大对象处理:对于跨多个 Grid 的大型对象(如团队副本的 Boss 场地),系统会将其关联到多个 Grid,确保在任何相关 Grid 激活时都能正确加载。
- 动态地图(如副本):副本地图的 Grid 生命周期与副本实例绑定,当副本内所有玩家离开并超过超时时间后,整个副本的 Grid 系统会被彻底销毁,而非仅失活。