Phaser 小地图与战争迷雾:探索状态要比画一个缩略图更可靠

讨论 Phaser 游戏中的小地图、战争迷雾、探索状态、视野更新、图标投影、性能优化和存档同步。

小地图不是把场景缩小

很多 Phaser 项目第一次做小地图,会想到把整个地图截图缩小放到角落。这样能快速看到布局,但很快会遇到问题:未探索区域不该显示,动态敌人要更新,宝箱开过要变状态,地图太大时截图成本高,移动端小地图还会挡住操作。真正可上线的小地图不是缩略图,而是地图状态的可视化。

我做过一个俯视角探索 H5,第一版用 RenderTexture 把地图缩小。测试反馈很快:玩家还没走到的房间已经在小地图上暴露,隐藏门也被看见;敌人图标频繁刷新导致低端机掉帧;切换楼层后旧图标残留。问题的根本是小地图没有自己的数据模型,只是拿主场景画面凑出来。

小地图和战争迷雾需要三类数据:地图结构、探索状态、动态标记。地图结构来自关卡数据,探索状态来自玩家视野,动态标记来自单位、任务、宝箱和传送点。Phaser 只负责把这些数据画出来。

flowchart TD
    A[关卡地图数据] --> B[MiniMapModel]
    C[玩家位置和视野] --> D[探索状态更新]
    D --> B
    E[敌人/宝箱/任务/出口] --> F[动态标记层]
    B --> G[小地图渲染]
    F --> G
    G --> H[HUD 或独立 MiniMapScene]

探索状态要格子化

战争迷雾最稳的做法是把地图切成逻辑格或区域。每个格子有状态:unknown、discovered、visible。unknown 从未探索,discovered 探索过但当前不可见,visible 当前在视野内。这样可以表现已探索地图和当前视野差异。

状态不要直接依赖显示对象。玩家移动时,根据视野半径或房间连接更新格子状态。渲染层根据状态画黑雾、半透明灰雾或正常地图。这样切换楼层、保存进度和回放都很清楚。

如果地图是房间制,也可以按房间更新探索状态。进入房间后整个房间 discovered,玩家视野内 visible。房间制更省性能,也更符合很多 Roguelike 和解谜游戏。开放世界则更适合细格子或区块。

视野更新要限频

玩家每帧都在移动,但迷雾不一定每帧重算。可以在玩家跨过一个格子、进入新房间或经过固定时间间隔时更新。视野计算如果涉及射线、墙体遮挡和大量格子,每帧重算会浪费。

简单项目可以用圆形半径,复杂项目可以用 shadow casting 或射线检测。关键是要和玩法需要匹配。如果迷雾只用于探索反馈,圆形足够;如果敌人是否可见影响战斗,就要更严谨。

视野更新还要处理多个视野源。队友、召唤物、侦察道具、临时灯塔都可能提供视野。MiniMapModel 可以接收多个 reveal source,合并 visible 状态。source 过期后,visible 退回 discovered。

小地图图标要有投影规则

世界坐标到小地图坐标需要统一投影。不要每个图标自己算缩放。MiniMapRenderer 应该知道地图边界、缩放比例、旋转和 UI 尺寸。图标只提供世界位置和类型。

图标也要按状态显示。未探索区域里的宝箱不显示,已探索但当前不可见的敌人可以不显示或显示最后已知位置,任务目标可以在边缘显示方向箭头。不同游戏规则不同,但必须明确。否则小地图会泄露信息。

动态图标数量要控制。几十个敌人全部显示在小地图上会很乱,也会增加更新成本。可以只显示 Boss、任务目标、队友、出口和特殊事件。普通小怪只在当前房间或视野内显示。

渲染方式要按地图规模选

小地图可以用 Graphics 画格子,用 RenderTexture 缓存静态层,再用 Sprite 图标画动态层。静态地图结构不需要每帧重画,探索状态变化时局部更新或低频重绘。动态图标可以单独一层每帧更新位置。

如果地图很小,直接 Graphics 全量重画也可以。不要一开始就做复杂缓存。性能问题出现时,再把静态层、迷雾层、图标层拆开。拆层的目的不是架构漂亮,而是减少不必要的重绘。

移动端小地图还要考虑清晰度。缩得太小,玩家看不懂;太大,挡住操作。可以提供展开地图按钮:角落显示简版,点击后打开大地图。大地图可以显示更多图标和任务路线,角落小地图只显示方向和附近结构。

存档要保存探索

探索状态是玩家进度的一部分。玩家开过地图、发现隐藏房间、点亮传送点,都应该保存。不要只保存玩家位置。刷新后迷雾全黑,会让玩家觉得进度丢失;全亮又会泄露未探索区域。

保存探索状态时,要注意体积。格子地图很大时,可以按区块压缩,或者只保存 discovered 的格子列表。房间制地图则保存 roomId 状态。存档里也要记录地图版本。如果地图结构更新,旧探索状态需要迁移或重置。

联网项目里,探索状态可能需要服务端同步。尤其是合作探索,队友发现区域是否共享,要由玩法规则决定。客户端可以表现迷雾,但长期状态最好有权威来源。

一个探索状态接口

下面示例展示最小模型。它不依赖 Phaser,可以单独测试。

type FogState = 'unknown' | 'discovered' | 'visible';

class FogMap {
  private cells = new Map<string, FogState>();

  reveal(pos: GridPos, radius: number) {
    for (const cell of cellsInRadius(pos, radius)) {
      this.cells.set(cellKey(cell), 'visible');
    }
  }

  endFrame() {
    for (const [key, state] of this.cells) {
      if (state === 'visible') this.cells.set(key, 'discovered');
    }
  }
}

真实项目要处理多个视野源和遮挡,但状态机思想相同。visible 是当前帧或当前更新周期状态,discovered 是历史探索状态。

调试要看迷雾数据

战争迷雾问题很难靠画面判断。开发版可以显示格子状态、视野源、更新频率、图标过滤原因。点击小地图图标时,显示它的世界坐标、是否在已探索区域、是否被规则隐藏。

还可以提供“揭开全图”“重置迷雾”“显示敌人最后位置”等调试命令。测试隐藏房间和任务导航时,不需要每次从入口跑完整地图。调试工具能显著提升内容验证效率。

性能面板也要记录迷雾更新耗时、重绘次数、动态图标数量。小地图通常被当成 UI 小功能,实际在大地图上可能成为性能热点。没有指标,很难发现它在后台消耗帧预算。

上线前检查清单

上线前检查:探索状态是否保存,未探索区域是否不会泄露宝箱和隐藏门,动态图标是否过滤,视野更新是否限频,大地图和小地图是否共用数据,地图版本变化是否处理,低端机重绘是否可接受。

还要测试:切换楼层、传送、复活、队友共享视野、隐藏房间、地图旋转或缩放、页面刷新恢复、任务目标在未探索区域。小地图一旦给错信息,玩家会做出错误决策。

大地图交互要和小地图分工

角落小地图适合快速定位,不适合承载所有信息。玩家想查看完整路径、任务目标、传送点和已探索房间时,应该打开大地图。大地图可以暂停玩法或半暂停,提供缩放、拖动、图例、任务筛选和传送入口。

小地图和大地图要共用 MiniMapModel,只是渲染比例和信息密度不同。不要为大地图再写一套图标逻辑。否则小地图显示出口,大地图不显示,玩家会困惑。图标过滤可以按 displayMode 区分:mini 只显示关键点,full 显示更多细节。

大地图交互也要处理输入冲突。拖动大地图时,不能让游戏角色移动;点击传送点前要确认;战斗中是否允许打开大地图,要由玩法决定。地图 UI 是信息工具,不应该绕过玩法限制。

多楼层和室内外地图

一旦游戏有楼层,小地图复杂度会明显上升。当前楼层、相邻楼层、楼梯、传送门、地下区域和室外区域都要有表达。最简单的做法是每个楼层一个 MiniMapModel,切换楼层时切换显示。跨楼层任务目标用方向提示,而不是强行叠到当前图上。

室内外切换也要注意比例。室外地图可能很大,室内房间很小,如果使用同一缩放规则,室内小地图会显得空,室外则挤成一团。可以按区域配置 minimapScale 和 iconPolicy。地图系统要允许不同区域有不同显示策略。

楼层探索状态也要分别保存。玩家探索了地下二层,不代表一层已探索。传送时要刷新 visible 状态,避免旧楼层的敌人图标残留。这个问题在 Phaser Scene 不切换、只换地图数据时尤其容易出现。

路线提示要谨慎

小地图常被要求显示任务路线。路线提示很有用,但会降低探索感。可以只在玩家手动追踪任务时显示路线,或者只显示大方向,不画完整路径。隐藏区域里的路线尤其危险,可能提前暴露秘密房间。

路线计算也要走寻路规则。不要简单画一条直线到目标,否则墙体和楼层会让提示误导玩家。若目标不可达,显示“目标在其他区域”或“需要解锁道路”,而不是画一条穿墙线。

迷雾和敌人 AI 的关系

战争迷雾如果只影响 UI,很简单;如果影响敌人 AI 和战斗,就要更谨慎。玩家看不到敌人,不代表敌人不存在;敌人看不到玩家,也不一定停止巡逻。视野系统需要明确谁使用它:只给玩家展示,还是同时影响 AI 感知。

如果迷雾影响 AI,建议玩家视野和敌人感知分开。玩家视野决定小地图和屏幕提示,敌人感知决定敌人是否追击。两者可以共享遮挡查询,但不要共用同一个 visible 状态。否则一个临时侦察道具可能意外让所有敌人都“看见”玩家。

隐身、侦察和噪声也会让系统复杂。玩家不在敌人视线内,但发出声音,敌人可以走向最后声音位置;玩家用侦察技能看到敌人,小地图显示敌人,但敌人不一定看到玩家。把这些语义写清楚,迷雾系统才能服务玩法,而不是只做遮罩。

地图 UI 的信息层级

小地图承载的信息太多时,会变成噪声。建议把图标分为常驻、条件显示和手动筛选。常驻只放玩家、出口、关键任务;条件显示放附近敌人、可交互物;手动筛选放采集点、商店、传送点。大地图可以显示更多,但也要有图例和筛选。

图标还要处理重叠。多个敌人在同一房间,不需要显示十个点,可以显示聚合数字。宝箱和任务目标重叠时,任务目标优先。地图 UI 是信息设计,不是把所有对象投影到角落。

玩家关闭某类图标后,要保存偏好。探索型玩家可能不想看路线提示,收集型玩家可能想看资源点。给玩家控制权,比强行展示所有信息更好。

地图图例也不能省。图标越多,越需要一个可随时打开的图例说明。不要指望玩家记住每个小图标的含义,尤其是在活动不断新增资源点和事件点以后。

图例本身也要按当前地图过滤。当前区域没有商店,就不必显示商店图标说明。信息少一点,玩家理解得更快。

地图缩放也要有边界。大地图允许缩放时,图标不能随地图无限缩小到看不见,也不能放大后遮满房间。可以让地图缩放,图标保持屏幕尺寸;也可以在不同缩放级别切换图标密度。缩放策略要统一,不要每类图标各自处理。

拖动地图时还要限制边界,不能把地图拖到完全看不见。移动端上,回到玩家位置按钮也很实用。

这个按钮应始终在安全区内,不能被地图内容或系统手势区域遮挡。

大地图关闭后,镜头和输入状态也要恢复到打开前。否则玩家可能从地图返回后角色仍然不能移动,或者摇杆状态残留。

结语

Phaser 小地图和战争迷雾的核心是状态,不是截图。地图结构、探索状态、动态标记和渲染层分开,功能才会稳定。否则每加一个图标、一个隐藏房间或一个新楼层,都会让小地图逻辑越来越乱。

好的小地图让玩家理解空间,但不泄露不该知道的信息。战争迷雾让探索有意义,但不能让玩家迷失在 UI bug 里。数据模型清楚,Phaser 的绘制只是最后一步。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页