SLG游戏(如《率土之滨》、《万国觉醒》等)的战争迷雾(Fog of War)存储是一个经典且有趣的设计问题。它需要在数据准确性、存储空间和计算效率之间找到一个精妙的平衡。

接下来我们将从核心思想、具体存储方案、优化技巧以及示例几个方面来详细阐述。
战争迷雾的本质是:记录一个玩家对于游戏地图的“探索”和“视野”状态。
● 探索:一旦被探索,地图区域就会永久打开(即使单位离开,也不再是全黑,通常是半透明的“已探索但无视野”状态)。
● 视野:由于己方单位存在而提供的实时可见区域。单位离开,视野随之消失。
因此,存储方案通常需要处理这两种状态。
没有一种方案是完美的,需要根据你的游戏地图大小、玩家人数、游戏类型(全球同图 vs. 分区小地图)来选择或组合使用。
这是最直观和常用的方法,将地图网格化,用每一个比特位(bit)来表示一个最小单位区域(通常是一个格子)的状态。
● 如何存储:
1. 将地图划分为网格:例如,你的地图是 1000x1000 格。
2. 为每个玩家创建两个位图:
● explored_mask: 记录已探索区域。某位为 1 表示已探索,0 表示未探索。
● vision_mask: 记录当前视野区域。某位为 1 表示可见,0 表示不可见。这个 mask 需要每帧/每秒根据单位位置重新计算。
1. 最终显示:客户端根据两个 mask 的组合来决定显示状态:
vision_mask[i] == 1 -> 完全可见vision_mask[i] == 0 && explored_mask[i] == 1 -> 已探索但不可见(战争迷雾)vision_mask[i] == 0 && explored_mask[i] == 0 -> 未探索(全黑)
● 优缺点:
○ 优点:查询和更新效率极高(位操作非常快),数据结构紧凑(1000x1000的地图,每个 mask 仅需 ~125KB 内存 (1000*1000/8/1024))。
○ 缺点:当地图极大时(例如全球同图的大型SLG),每个玩家都存一个完整的位图,内存消耗会线性增长(1万玩家 * 125KB = 1.25GB,这还只是基础内存)。同步整个位图给客户端流量较大,需要做差分同步。
这是对位图方案的优化,旨在减少存储和同步开销。将大地图划分为更大的区块(Chunk),例如 16x16 或 32x32 格为一个区块。
● 如何存储:
1. 地图被划分为 M x N 个区块。
2. 为每个玩家存储两个信息:
● explored_chunks: 一个记录了所有已探索区块ID的集合(Set)。或者也是一个位图,但尺寸小了很多(M*N bits)。
● vision_chunks: 一个记录了当前拥有视野的区块ID的集合。这个集合需要频繁更新。
● 优缺点:
○ 优点:极大减少了存储和网络同步的数据量。同步“哪些区块可见”比同步一个庞大的位图要节省得多。
○ 缺点:精度是区块级别的,不够细腻。一个区块内只要有一个格子被看到,整个区块都会被标记。对于需要精确视野的游戏(如MOBA、RTS)不适用,但对于大型SLG来说是完全可接受的折衷。
如果你的游戏地图巨大,但每个玩家的单位很少,视野范围相对整个地图非常稀疏(例如早期游戏阶段),这种方法可能有效。
● 如何存储:
1. 为每个玩家存储两个集合:
● explored_set: 一个存储了所有已被探索过的区块坐标(或格子坐标)的集合。
● vision_set: 一个存储了当前所有可见的区块坐标(或格子坐标)的集合。
● 优缺点:
○ 优点:在视野极其稀疏时,非常节省内存。
○ 缺点:随着探索和扩张,集合会越来越大,查询“某个点是否可见”的效率(O(n))会远低于位图的O(1)。通常不作为主存储方案。
在实际的大型SLG项目中,通常会采用一种混合方案:
1. 两层存储结构:
● 底层(基础存储):使用区块化(Chunking) 的位图来记录玩家的 explored_mask。例如,将一个 2000x2000 的地图划分为 500x500 个区块(每个区块 4x4 格)。这样 explored_mask 只有 500*500/8 ≈ 15.6KB 每玩家。
● 上层(实时计算):服务器不为每个玩家存储完整的 vision_mask,而是在需要时(如同步给客户端时)动态计算。
○ 根据这个玩家所有单位的当前位置和视野范围,实时计算出哪些区块处于视野内(vision_chunks)。
○ 将 vision_chunks 与 explored_chunks 进行合并,得到最终需要同步给客户端的视野信息。
1. 差分同步 (Delta Sync):
● 不要每帧都把完整的视野信息发送给客户端。
● 服务器端缓存玩家上一帧的 vision_chunks_old。
● 每帧计算新的 vision_chunks_new。
● 只同步变化的部分:chunks_that_became_visible = vision_chunks_new - vision_chunks_old 和 chunks_that_became_invisible = vision_chunks_old - vision_chunks_new。
● 客户端根据这些增量信息来更新本地的视野显示。这能减少90%以上的网络流量。
1. 探索状态的更新:
● 当一个新的区块进入视野(chunks_that_became_visible)时,服务器会将这些区块在玩家的 explored_mask 中永久标记为 1。
● explored_mask 是一个只增不减的数据,只需要在玩家初次探索某个区块时写一次数据库,之后可以常驻内存。
1. 数据库存储:
● 玩家的 explored_mask(区块位图)需要持久化到数据库。可以将其转换为一个二进制Blob(如MySQL的BLOB类型)或者一个很长的二进制字符串(BIT类型)存储。
● 当玩家下线再上线时,从数据库读出他的 explored_mask 加载到内存。
1. 玩家登录:服务器从DB加载该玩家的 explored_mask(区块位图)到内存。
2. 游戏运行:
● 每10秒(或其他节奏),服务器遍历该玩家的所有单位,计算它们视野所能覆盖到的所有区块,得到 current_vision_chunks。
● 对比上一轮的 last_vision_chunks,得到需要移除视野和添加视野的区块列表。
● 将 current_vision_chunks 中新出现的区块,在 explored_mask 中标记为已探索。
● 将区块变化列表(差分数据)发送给客户端。消息体非常小:{remove: [chunk_id_1, chunk_id_2], add: [chunk_id_3, chunk_id_4]}。
1. 客户端:根据收到的消息,更新本地地图的渲染状态。
2. 玩家下线:服务器将更新后的 explored_mask 写回数据库。
方案 | 适用场景 | 优点 | 缺点 |
精细位图 | 中小型地图,RTS/MOBA | 精度高,速度快 | 存储和同步开销大 |
区块化位图 | 大型SLG(首选) | 存储和同步开销小 | 精度为区块级 |
坐标集合 | 超大地图,极稀疏视野 | 极端稀疏时效率高 | 集合大了以后效率骤降 |
对于你正在设计的SLG游戏,采用“区块化混合方案”并结合“差分同步”,是最稳健和高效的选择。它完美地平衡了内存、网络和CPU计算资源。
Copyright © 2021-2022 hsroot.com. All Rights Reserved. 华上网络版权所有 湖南华上网络科技有限公司 湘ICP备2021013396号