跳到主要内容

表现层

GCS · Runtime

运行时状态如何到达 UI、单位视图、VFX、音频和浮动文本,以及为什么玩法从不等待缺失的视觉资产。

搭建自定义战斗 UI、接 FX 节点,或排查动画卡住阶段转换时,来查这一页。

表现层让战斗状态变得可见。它观察运行时状态和事件、发出反馈请求、播放结果动画,但从不拥有玩法规则。一次出牌可以同时展开成伤害数字、状态图标、VFX 和镜头震动,全部由同一条事件流驱动:

Hunter 战斗中 Poison Arrow 刚结算完的画面:敌人 HP 下降、Poison 层数图标出现、能量已消耗、卡牌进入弃牌堆

表现边界

区域拥有不拥有
RuntimeHP、护甲、能量、牌堆、状态、阶段、事件。像素布局、声音路由、粒子质量。
UI手牌 widget、单位血条、状态图标、意图 widget、选择/奖励面板。战斗规则或出牌合法性。
Unit view单位 sprite、程序化运动、受击/治疗/死亡反应、状态视觉。伤害计算或状态层数。
FX nodesVFX、SFX、浮动文本、闪烁、滑动、震屏、动画的请求。最终资产打磨、渲染管线兼容、mixer 设置。

即使视觉效果播放失败,玩法也照常结算。


事件驱动的 UI

反应式循环始终是同一套:controller 修改状态并触发事件,你的 handler 被唤醒,通过 GCSApi 读取最新状态,再更新 widget。

自定义 UI 应该订阅运行时事件,然后读取当前状态:

private IDisposable _energySub;

void OnEnable()
{
_energySub = GCSApi.OnEnergyChanged(_ => RefreshEnergy());
RefreshEnergy();
}

void OnDisable()
{
_energySub?.Dispose();
}

void RefreshEnergy()
{
energyLabel.text = $"{GCSApi.PlayerEnergy}/{GCSApi.MaxEnergy}";
}

事件告诉你什么时候该刷新,GCSApi 告诉你当前值是什么。


核心 UI 刷新事件

UI 区域监听事件读取状态
回合与阶段标签OnTurnStartedOnTurnEndedOnEnemyTurnStartedOnBattleEndedGCSApi.PhaseGCSApi.TurnNumberGCSApi.IsPlayerTurn
能量OnEnergyChangedOnEnergyGainedOnEnergySpentGCSApi.PlayerEnergyGCSApi.MaxEnergy
手牌卡牌生命周期事件、OnCardModifiedOnEnergyChangedGCSApi.Hand()GCSApi.CanPlayCardGCSApi.GetEffectiveEnergyCost
单位条OnUnitHpChangedOnArmorGainedOnArmorLostOnUnitDiedGCSApi.HpGCSApi.MaxHpGCSApi.ArmorGCSApi.IsAlive
状态 widgetOnStatusChangedOnStatusTicked、状态 transition 事件GCSApi.StatusesOn(unit)
敌人意图OnEnemyUnitIntentChangedOnEnemyPhaseChangedGCSApi.PendingIntentsGCSApi.CurrentIntentTagGCSApi.CurrentIntentValue
选择 UIOnEffectChoiceOfferedOnEffectChoiceSelectedOnEffectChoiceSkippedChoice payload 加 GCSApi.IsWaitingForChoice
奖励 UIOnBattleRewardOfferedOnBattleRewardSelectedOnBattleRewardSkippedReward payload 加 GCSApi.IsWaitingForReward

UnitView 行为

内置的 UnitView 绑定到一个 UnitState,并订阅:

  • OnUnitHpChanged
  • OnUnitDied
  • OnStatusChanged
  • OnStatusTicked

它用这些事件播放受击、治疗、死亡和状态动画,同时按 UnitId 注册自身,让表现节点能找到目标单位的 view。

自定义项目可以复用 UnitView、派生自己的 view 行为,或整个替换掉它。稳定的集成点是运行时事件 payload 加 UnitId


浮动文本

FloatingTextSystem 暴露一个表现请求事件:

请求字段含义
Target接收文本的单位。
KindDamage、Heal、Armor、Block 或 Custom。
ContentAmount 或 Text。
Amount要显示的数字。
Text自定义标签。
Color, UseCustomColor可选的颜色覆盖。
IncludeSign是否显示正负号。
ScaleMultiplier, DurationMultiplier单次请求的视觉微调。
Offset目标附近的位置偏移。

FlowGraph 的 Show Floating Text 节点会发出这些请求。自定义浮动文本 presenter 可以订阅 FloatingTextSystem.Requested


FlowGraph 表现节点

节点运行时效果
Play VFX在单位或位置上实例化或请求 VFX prefab。
Play Animation向目标单位的表现发送 animation trigger。
Play SFX播放一次性的 audio clip。
Flash Unit目标 view 存在时调用 UnitView.Flash
Slide Unit短暂移动目标的表现。
Show Floating Text发送 FloatingTextRequest
Shake Camera请求镜头震动。

在玩法状态改变之后再用这些节点,让视觉反映真实结果。


动画 Gate 与等待

状态机可以推迟转换,等表现类等待完成。

功能用途
Animation gate在 gated coroutine 或动画运行期间扣住阶段转换。
Flow wait让图按秒或按 animation id 延迟后续执行。
Enemy gaps在敌人行动之间加入可读的间隔。

Game Card Monitor 的 Gate 页签显示当前谁持有 animation gate、acquire/release 日志和超时时间,用于定位卡住的转换:

Game Card Monitor 的 Gate 页签,显示当前 animation gate 的持有者与 acquire/release 日志

这也是自定义 UI 应该反应式处理状态变化的原因。一次出牌可能引发状态变化、动画、等待、选择和后续转换。


VFX、音频与渲染管线范围

表现的图契约是稳定的,最终资产则是另一回事:

  • VFX prefab 可以之后替换或升级。
  • SFX clip 和 mixer 路由可以之后配置。
  • 渲染管线相关内容(材质、shader、后处理)属于资产层,不属于图。

不要把管线专属假设写进玩法图。图只负责请求玩法反馈,如何渲染交给资产和表现层决定。


失败行为

表现层要能体面地降级。

缺失项预期结果
缺少 unit view玩法照常结算;针对该单位的视觉请求可能被跳过。
缺少 VFX prefab玩法照常结算;没有粒子出现。
缺少 SFX clip玩法照常结算;没有声音。
缺少浮动文本 presenter玩法照常结算;请求没有可见的订阅者。
Animation wait 未安排执行立即继续。

用运行时日志和 Monitor 做诊断,但绝不要因为可选表现资产缺失而阻塞战斗。