● ● ● SCP-FOUNDATION // LEVEL FLOW · 关卡推进 · 世界状态门控 · ACCESS GRANTED

Foundation · Abyss Protocol — Player Level Flow & World-State Gating

关卡流程与世界状态 · 推进门控图谱

玩家怎么在关卡里一步步往深处推进,而世界状态是记录"队伍当前进度"的脊柱。流程被一串门控挡着——做完任务 / 拿到卡 / 恢复供电 / 解锁路线才能前进。门 / 开关 / 电梯 / 触发体 / 目标 / 钥匙卡都只是挂在世界状态上的门控装置。术语中文 · English 双标。

脊柱 ACSGameState.WorldProgress 更新 2026-06-13 实现 Core/CSGameState · World/CSObjectiveDirectorSubsystem · CSDoorActor · CSWorldStateSwitch · CSStreamingElevatorStation
00

分层半开放 · 触发与门控

Layered Semi-Open · Triggers & Gates

我们是分层半开放(layered semi-open):每一层(L0–L7)是一个可自由探索 / 战斗 / 搜刮的半开放沙盒不是线性走廊。但每层都有几个关键触发点(critical-path triggers)串起这一层的主线——击杀 / 拾取 / 交互 / 到达某些东西才推进下一步,最终解锁下潜到下一层世界状态记录"队伍这层的进度"。

EXPLORE半开放探索这一层自由战斗 / 搜刮
TRIGGER触发关键点击杀 / 拾取 / 交互 / 到达
ADVANCE世界状态前进服务器写 WorldProgress
UNLOCK门控放行 → 下潜下一层打开

触发 = 主动推进 Triggers (push)

完成动作即推进:击杀 X / 拾取 Y / 交互 Z / 到达某点 → 世界状态。这是比"门控"更普遍的推进方式。求值器(§04)已支持 7 种触发条件ACSObjectiveTriggerVolume 就是"到达型"触发。

门控 = 被动阻挡 Gates (block)

条件没满足就挡着:锁着的门、未解锁路线的电梯。门控世界状态决定放不放行(§05)。触发 / 门控是一体两面:一个触发点(击杀 boss)写标签 → 一道门控(下潜电梯)读标签放行。

脊柱 The Spine

触发写、门控读,中间隔着世界状态WorldProgress,共享 + 存档)。装置彼此不直接连线,全靠脊柱间接耦合(§01 / §03)。加载存档回到原处、合作玩家同步同一进度。

主线 vs 开放内容 Critical Path vs Side

每层主线 = 那几个关键触发(必做才下潜);其余半开放空间填充支线 / 搜刮 / 痕迹叙事(SD_48 每层三类空间 + 支线小案)——可探可不探、丰富而不卡进度。

设计意图:推进感来自"我对这一层做了事、世界为我让路"——不是线性关卡的"走到头开门",而是沉浸式模拟 / 类银河城的状态驱动:在半开放空间里完成几个关键节拍即解锁深入。落点对齐 第一次下潜循环L0 结构(21 间四层,半开放)。
01

世界状态 = 进度脊柱

WorldProgress — The Backbone

ACSGameState.WorldProgress服务器权威 · server-authoritative 的复制结构,存着整个 Site 的共享进度。它按玩家分(钥匙卡这类私有物在背包里);后加入的玩家从共享世界存档读取,全队收敛到同一状态。

字段存什么谁读它当门控
WorldStateTags世界标签(如 Power.SectorB.On / Site.FirstDescent门、目标可见性 / 武装、叙事节拍
CompletedObjectiveIds / ObjectiveStates已完成 / 各步状态门、任务面板、后续目标的前置
UnlockedRouteIds已解锁的主路线(如 Route_S0_L0_MainElevator电梯层级门禁、门
ObjectiveEventIds已应用的进度事件Id(幂等去重 + 可追踪)求值器(防重复折叠奖励)
ArchivedSampleIds / AvailableAIMProtocolIds / UnlockedRecipeIds样本归档 / AIM 协议 / 配方解锁归档 / 制作 / S0 整备系统
S0RequisitionCredits / S0SharedMaterialStockS0 共享物资 / 信用点整备 / 经济
写入只走服务端入口:ApplyObjectiveEvent(折叠"完成目标 + 写标签 + 解锁路线 / 配方"为一笔幂等事件)/ CompleteObjective / AddWorldStateTag / RemoveWorldStateTag(断电)/ UnlockRoute。读取:HasWorldStateTag / IsObjectiveCompleted / IsRouteUnlocked。任何改动经 OnWorldProgressChanged 委托广播 → UI / 门 / 叙事即时刷新。
02

一次下潜的流程

First-Descent Loop — Worked Example

用 Demo 的第一次下潜循环把门控串起来:每一步玩家做的事,对应一次世界状态读 / 写。蓝=读门控 / 橙=写进度

L0 · 醒来入口区(新手)待办:修电梯回地表
拾取找保险件写:L0_FindFuse(Collect)
交互修电梯写:L0_RestoreElevatorFuse → 解锁路线
电梯上行 S0读:Route 解锁? 写:Site.ReachedS0
S0 · 枢纽简报整备写:S0_Resupply → Site.S0_Briefed
回 L0备料(二选一)读:Site.S0_Briefed 写:拿门禁卡 或 凑材料 → Access.L1
电梯下潜 L1读:Access.L1?
收容活体过滤循环修3节点 + 守60秒 → Site.ContainmentStabilized
返回回 S0写:Site.ReturnedToS0 → Site.FirstLoopComplete

门控落在哪 Where Gates Sit

最小循环是 L0 修电梯 → 上行 S0 → 回 L0 备料 → 下潜 L1 → 撤回 S0电梯是两处关键门控:上行段解锁 Route_S0_L0_MainElevator 后放行(到顶写 Site.ReachedS0);下潜 L1 段读 Access.L1 才放行。回 L0 的备料是「二选一门」L0_GetKeycard(拿 Keycard_L1 L0_GatherMaterials(清剿 EG_L0_Hostiles)任一完成即写 Access.L1,另一条自动归档(求值器 OR 组 L1Access)。可控制台验:CompleteObjective L0_GetKeycardAccess.L1 置位 → 下潜电梯放行;只做 L0_GatherMaterials 同样开。

前置门控现已运行时生效 Prereq Gates Now Live

任务的前置世界标签(工具里蓝色「解锁线」写入的)现由求值器 UCSObjectiveDirectorSubsystem::IsArmed 真正读取:前置没满足,该任务的目标不武装(事件不计数)。故 MQ_L0_Prep 在没拿到 Site.S0_Briefed 前推不动、MQ_Containment_Filtration 在没 Access.L1 前推不动——不再只靠电梯物理挡。武器库门仍可配刷卡(Keycard_*),同挂一根世界状态脊柱。

🔶 循环 A/B/E + OR门 + 前置门控 已建待 PIE ⬜ 污染 C / 收容导演 D / HUD 接真 F + 关卡接线(Vol_S0_Hub/Keycard_L1拾取/EG_L0_Hostiles/电梯标签) · 详见 第一次下潜 · 关卡设计

03

门控装置一览

Gating Devices

所有"挡路 / 推进"的装置,按它世界状态(当门)还是世界状态(推进)归类。形态不同,全挂同一根脊柱。

装置读什么(当门控)写什么(推进)
ACSDoorActor 解锁条件:标签 / 目标 / 路线 / 钥匙卡首次开门:WorldTagOnFirstOpen / ObjectiveOnFirstOpen
总闸 / 拉杆ACSWorldStateSwitch (可选)需物品(保险丝 / 电池)AddWorldStateTag(通电)/ RemoveWorldStateTag(断电)
电梯ACSStreamingElevatorStationRequiredRouteId + RequiredWorldStateTags(按层)CompleteTravelSite.FirstDescent / Site.ReturnedToS0
目标触发体ACSObjectiveTriggerVolume进入 → NotifyReachVolume + 盖复活检查点
任务目标UCSObjectiveDirectorSubsystem可见性 RevealWorldStateTags / 武装 PrereqWorldStateTags条件达成 → ApplyObjectiveEvent 折叠奖励
钥匙卡 / 物品UCSInventoryComponent(玩家私有)门的 Keycard 条件拾取 → NotifyCollect
容器 / NPCACSLootContainer / ACSNarrativeNPC首搜 Site.FirstSearch / 谈完 SetTagsOnEnd
关键解耦:持有"我连着哪个开关",开关也持有"我开哪扇门"——开关写标签、门读标签,中间隔着世界状态。所以一个总闸能同时点亮一片门,跨流送关卡也不会失联,且天然可存档。
04

状态怎么前进(写)

Advancing State — The Write Side

世界状态前进的主干 = 玩法事件 → 任务求值器 → 折叠奖励;另有几条"直接写"的旁路(开关 / 门首开 / 电梯 / 容器 / 对话)。全在服务端、零 RPC。

主干:任务求值器 Quest-C

UCSObjectiveDirectorSubsystem(World 子系统,仅权威、无复制)按目标的 ConditionType 收玩法事件、达阈值即 ApplyObjectiveEvent 自动完成并折叠奖励(写标签 / 解锁路线 / 配方)。七种条件:

ConditionTypeConditionKey谁喂事件
Interact交互物的 Obj.<Tag>交互 → InteractOnServer 扫 actor tag → NotifyInteract
CollectItem物品 ItemId拾取 → AddItemNotifyCollect
KillCount遭遇组 EncounterGroupId敌人 HandleDeathNotifyKill
ReachVolume触发体 VolumeIdACSObjectiveTriggerVolumeNotifyReachVolume
RepairCount / HoldTimer收容 EncounterId收容导演 → NotifyRepairProgress / NotifyHoldComplete
WorldStateTag世界标签标签置位且本目标已武装即完成(被动重算)
Manual控制台 / GM / 脚本(向后兼容缺省)

旁路:直接写标签的装置 Direct Writers

  • 总闸 ACSWorldStateSwitchAddWorldStateTag(Power.X.On)(通电)。
  • 门首开 ACSDoorActorWorldTagOnFirstOpen / ObjectiveOnFirstOpen(开门驱动剧情)。
  • 电梯 CompleteTravelSite.FirstDescent / Site.ReturnedToS0
  • 容器首搜Site.FirstSearchNPC 谈完 → 会话表 SetTagsOnEnd
幂等保证:ApplyObjectiveEventEventId 去重(ObjectiveEventIds.Contains → 已应用直接返回),所以重复触发 / 多客户端不会把奖励折叠两次。
05

状态怎么挡路(读)

Gating on State — The Read Side

门控读世界状态决定放不放行。读取在服务端裁决,但因为世界状态复制到所有客户端、玩家背包对 owner 复制,客户端本机也能算出"开 / 锁 + 缺什么",提示无需等服务器回包。

门:四种条件 Door Conditions

ACSDoorActor.UnlockRequirements 一串 FCSDoorRequirementWorldStateTag / ObjectiveComplete / RouteUnlocked / Keycard),全满足才开。详见下方门小节。

电梯:按层门禁 Elevator

CanAccessLayer 读目标层的 RequiredRouteId + RequiredWorldStateTags;不满足返失败原因("route X required")。与门同一套世界状态读法。

目标:可见 / 武装 Objective Gating

Hidden 类目标在 RevealWorldStateTags 任一置位时才在任务面板现身;目标按 PrereqWorldStateTags 武装后才参与自动完成——即任务链本身也被世界状态门控

反馈:锁灯 + 提示 Feedback

门锁灯红 / 绿(共享门控),按 E 锁着时把具体原因推给玩家("需要红卡 / 先通电")。"按了没反应、也不知道原因"是门控最让玩家受挫的体验,刻意避免。

门 · ACSDoorActor 细节 Door Detail

玩家按 E → UCSInteractionComponent 找焦点 → 客户端 ServerInteract RPC → 服务器 Interact_Implementation:开着→关;关着→CanOpenNow 求值,满足则开(扣一次性卡 + 破封 + 首开副作用),否则推送原因。复制态 bIsOpen / bIsUnlockedOnRep_DoorVisual 驱动门扇摆动 + 锁灯)。

CLIENT按 E找到聚焦门
SERVER裁决条件EvaluateRequirements(pawn)
SERVER开 / 拒满足→开+扣卡 · 否则→推原因
REPNOTIFY各端表现门扇摆动 + 锁灯
  • 锁存 bLatchUnlock(默认 true=破封后永久通行;false=每次重验,配掉电门 / 每次刷卡)。自动关 bAutoClose+AutoCloseDelay
  • 钥匙卡 bConsumeOnUse=开门即从背包 RemoveItem(一次性钥匙)。
  • 即时刷新:门订阅 OnWorldProgressChanged → 通电 / 解锁那一刻锁灯 + 提示立即更新(门不自开,仍需按 E)。
  • 默认网格取门交互包 SM_DoorFrame / SM_MetalDoor / SM_CardLock(编辑器可换);开关默认 SM_Lever
06

权威 · 存档 · 复活

Authority · Save · Respawn

服务器权威 Authority

流程的每一笔推进、每一道门的放行都在服务端裁决。客户端只发请求(交互 RPC)+ 收复制结果。客户端改不了世界状态、改不了门 = 无作弊面。

存档 Save

世界状态整组随存档持久(WorldProgress);门按 PersistentId 存开 / 解锁态(FCSWorldActorSaveData.Doors);开关态随世界标签一并存。读档=回到原进度。

死亡 → 复活 Respawn(循环不中断)

死亡 = 简单检查点复活(满血 + 保背包,无结算屏):HP 跨 0 → HandlePlayerDiedServerRespawnAtCheckpoint(同 pawn 传送 + 满血 + 清 buff)。检查点由 ACSObjectiveTriggerVolume(bStampCheckpoint) 沿途盖。世界状态因个人死亡回退——队伍进度是共享脊柱。

07

类 · 数据 · 接线

Classes · Data · Wiring

脊柱 Spine

ACSGameStateWorldProgress + 写 / 读 API + OnWorldProgressChanged)。新增 RemoveWorldStateTag(断电 / 反复开关)。

求值器 Director

UCSObjectiveDirectorSubsystem(Quest-C,七条件 + Notify* + 折叠奖励)。静态定义 Quests.csv / Objectives.csvUCSItemDataSubsystem

门控装置 Gates

ACSDoorActor / ACSWorldStateSwitch(新,World/)· ACSStreamingElevatorStation · ACSObjectiveTriggerVolume · ACSLootContainer · ACSNarrativeNPC,各实现 ICSInteractable

接线约定 Wiring

编辑器给交互物加 Obj.<Key> actor tag 即挂目标;门 / 开关 / 触发体的世界状态字段在细节面板填,或写进 Scripts/CreateL0Blockout.py 摆放时设。命名沿用 Power.* / Site.* / Obj.*

当前状态:门 / 开关 C++ 运行时目标整编通过编辑器目标待关编辑器后整编 + 重启(新 UCLASS 无法 Live Coding 热加)。求值器 / 触发体 / 复活(A/B/E)代码 + 编译通过待 PIE。网络相关一律 Listen Server + 1 客户端验
08

行业对照

Convention — How Progression Gating Is Normally Done

"关卡推进门控正常怎么做"——业界主流就是一套世界状态 / 旗标系统 + 读它的门控装置,正是本系统的形态。条件实现的轻重分四档:

做法谁这么做 / 评价
每门 / 每关硬编码 bool、关卡蓝图连线入门,不扩展、难存档、跨流送断线
世界状态 / 旗标系统 + 数据驱动门控(门读条件列表、装置写标签)← 我们这档。沉浸式模拟 / 类银河城 / 生存的主流(Source I/O、各类任务旗标系统)。可存档、多对多、解耦
②的键用 GameplayTag + 标签查询(FGameplayTagQueryUE5 官方推荐写法,Lyra 走这条;带可视化查询编辑器
GAS:推进 / 门控做成能力 + GameplayEffect 标签大型项目;对本作过度设计
为何不上第③档(GameplayTag):本项目一开始就定了统一用 FName、不用 GameplayTag(世界状态 / 任务 / 背包全是 FName)。中途引入 = 跨系统架构迁移,违反"不在单元中途做架构迁移"红线。所以我们用 FName 手写等价的"类型化条件 + 世界状态"——没有采用最新那一档做法,但与项目自身约定一致。其余(服务器权威、求值器折叠奖励、装置-标签解耦、复制 + 存档)都是标准做法。

术语表

Glossary
术语English含义
关卡流程level flow / progression玩家从入口往深处推进的路径,被一串门控分段
门控gate挡路装置;读世界状态决定放不放行(门 / 电梯 / 目标 / 卡)
世界状态 / 进度world state / WorldProgress共享 + 复制 + 存档的"队伍进度记忆",门控的脊柱
进度事件objective eventApplyObjectiveEvent 一笔:折叠"完成目标 + 写标签 + 解锁",按 EventId 幂等
任务求值器objective director按条件类型收玩法事件、达阈值自动完成目标的权威子系统(Quest-C)
解锁条件unlock requirement门的 FCSDoorRequirement;标签 / 目标 / 路线 / 钥匙卡,全满足才开
破封锁存latch unlock门满足条件开过一次后永久通行
路线routeUnlockedRouteIds 项;电梯层级门禁读它(如 Route_S0_L0_MainElevator
世界进度委托OnWorldProgressChanged世界状态变化广播;门 / UI / 叙事订阅它即时刷新(非每帧轮询)
服务器权威server-authoritative推进 / 门控真值只在服务器改,客户端发请求 + 收复制结果
§

状态图例 & 调参

Status Legend & Tuning
✅ 完成 已实现并通过 PIE / 用户验收 🔶 待验证 代码 + 编译通过,待 PIE / Listen Server 验 ⬜ 计划 / 缺口 设计有、代码无

调参入口 Tuning Entry Points

  • 任务链 / 目标条件 / 奖励(可视化):任务流程节点编辑器 拖节点连线 → 导出增量补丁 → python Scripts\ApplyQuestGraph.py QuestPatch.json upsert 进 Quests.csv/Objectives.csvpython Scripts\GenerateGameData.py。也可直接手改两张 CSV。给交互物加 Obj.<Key> tag 即挂 Interact 目标。
  • 门:ACSDoorActor 实例的 UnlockRequirements / bLatchUnlock / bAutoClose / WorldTagOnFirstOpen
  • 开关 / 总闸:ACSWorldStateSwitchWorldStateTag / bOneShot / RequiredItemId
  • 电梯门禁:LayerDefinitionsRequiredRouteId / RequiredWorldStateTags
  • 触发体 / 检查点:ACSObjectiveTriggerVolumeVolumeId / bStampCheckpoint
  • 关卡里批量摆门 / 开关:写进 Scripts/CreateL0Blockout.py(见 L0 灰盒生成)。
口径:本页为关卡推进与世界状态门控的设计规格 + 数据流,落点忠实于 Core/CSGameState · World/CSObjectiveDirectorSubsystem · CSDoorActor · CSWorldStateSwitch · CSStreamingElevatorStation · CSObjectiveTriggerVolume 现状,单元状态以 DevelopmentUnits.md 为准。类对类地图见 UE_CodeArchitecture.md · World。Demo 循环见 第一次下潜;任务系统见 系统设计总览