跳到主要内容

状态创作

Guide

定义一层状态代表什么、层数如何合并或衰减,以及状态会响应哪些战斗时机

Mire Reaper 的 Behavior 已经把 Poison 与 Weak 接入敌人行动,状态资产会继续决定这些效果落到单位后如何显示、叠加、响应战斗时机并最终结束

先确认当前场景的 GameCardManager 已启用至少一个 Status Database,数据库配置决定 Game Card Editor 与运行时能够发现哪些状态

打开 Game Card Editor

Tools > TinyGiants > GCS > Game Card Editor

在窗口顶部选择 Status,再从左侧定位 Strength

Game Card Editor 的 Status 模式围绕 Strength 展开 List、Inspector、Preview 与 Used By

上图把 Status 的 ListInspectorPreviewUsed By 放进同一条创作链路,左侧先确定当前 GameStatus,中间定义玩家可见身份、叠层规则与 Behavior,右侧随即核对配置结果并追踪行为图引用,这些设置会共同决定状态进入战斗后的完整生命周期

四个区域共享当前选中的 Strength,因此先在 List 确认状态资产,后面的字段、行为、预览与引用检查就会沿同一份数据连续展开

在 List 中确认 Strength

Status 模式只会列出当前 GameCardManager 已启用的 Status Database 中的非空状态,每个列表行显示 Icon、名称、Buff 或 Debuff、Stack Rule 与 Decay Rule

PresetStatusDatabase 的状态列表,Strength 显示 Buff、Additive 与 Never

PresetStatusDatabase 当前包含 30 个随包状态,其中 Buff 与 Debuff 各 15 个,14 个使用 PerTurn,16 个使用 Never,除 Ritual 外的 29 个状态都会显示层数

List 还承担当前 Status Database 内的资产管理,具体操作与结果如下

操作结果
创建+ 创建以 NewStatus 为基名的子资产,默认使用 Buff、AdditiveMax Stacks = -1Never 并显示层数,Behavior 已创建但没有节点或连接
复制复制当前字段与 Behavior 并生成独立副本
删除 在确认后删除当前状态,不会保护或迁移行为图中的既有引用
搜索与筛选搜索读取子资产名,Type 在 Buff 与 Debuff 之间筛选,Tags 保留命中任一所选标签的状态,标签比较区分大小写
排序与拖动排序按钮在数据库顺序、名称升序与名称降序之间切换,拖动列表行只改变数据库中的创作顺序
批量操作多选复制、批量添加或移除 Tag、批量删除,以及通过 Ctrl/Cmd+CCtrl/Cmd+V 在同类型的活动数据库之间复制

窗口级交互见 Game Card Editor,确认 Strength 处于选中状态后,先让它的一层数值具有明确含义

让一层 Strength 具有明确含义

Basic

字段配置与职责
Name玩家看到的状态名称,填写非空值时会同步修改子资产名,留空时 List 回退显示子资产名,Display 显示 (unnamed),Validation 同时报告错误
Description说明状态改变什么,以及一层代表的强度、持续时间或次数,文字本身不会自动生成运行规则
TypeBuff 或 Debuff 的显示分类,也供 Filter 节点筛选状态集合,不会自动决定效果有益或有害
Tags用于 List 筛选、Status InfoFilter 节点与项目自定义规则,内置单位目标选择不会把状态 Tags 当作单位 Tags

Name 设为 Strength,保留说明 Your attacks deal 1 extra damage per stack.,选择 Buff 并添加 Power Tag

Strength 的 Basic 配置,包含名称、说明、Buff 与 Power Tag

这组字段已经明确一层 Strength 会增加 1 点攻击伤害,接下来为同一状态绑定玩家能够识别的 Icon

Visual

Icon 会进入 Status List、Display 预览和项目状态徽章,随包 Demo 还会在单位悬停信息中显示它

Strength 选择拳头图标

Strength 的 Visual 配置,Icon 引用状态拳头图标

身份与图标只说明玩家看到什么,Strength 再次被施加时怎样合并、能否无限增长以及何时衰减,则由 Stack 决定

确定 Strength 如何叠加与保留

StrengthStack Rule 设为 AdditiveMax Stacks 设为 -1Decay Rule 设为 Never,并启用 Show Stack Count

Strength 的 Stack 配置,使用 Additive、无限层数、Never 并显示层数

这组配置让每次施加的层数累加到现有值,不限制最终层数,也不会在回合结束时自动减少,徽章上的数字始终对应当前伤害加成

设置运行时含义
Additive现有层数加上传入层数
Max在现有层数与传入层数之间保留较大值
Replace用传入层数覆盖现有层数
Max Stacks只有正数才会限制最终层数,0 与负数在运行时都表示不限制,但 0 会触发编辑器警告,明确的无限层数使用 -1
PerTurn在状态持有者自己的回合结束后减少 1 层
Never一直保留到行为图、GCSApi 或项目逻辑主动修改
Show Stack Count控制项目状态徽章是否显示数字,不改变真实层数

同一套字段也能表达不同的层数语义

状态Stack RuleMax StacksDecay Rule适合的结果
StunnedReplace1PerTurn单层开关,重新施加仍保持一层
EmpowerMax1PerTurn只保留更高传入值并在持有者回合结束后消失
RestrictedAdditive3Never最多累积三层,直到被主动移除

Strength 的当前层数现在已经具有稳定语义,Behavior 可以直接读取这项运行时数值并把它写入伤害 Hook

用 FlowGraph 让 Strength 改写伤害

Behavior 下方的 Chip 会列出顶层 Entry,Strength 当前显示 Hook: OnDealDamage

Strength 的 Behavior 区,Edit FlowGraph 下方显示 Hook OnDealDamage

点击 Edit FlowGraph 打开 Strength 的行为图

Strength 的 FlowGraph 用 Damage Dealt Hook、Status Info、Math Binary 与 Write Hook 按层数增加伤害

上图中的执行链从 Damage Dealt Hook 取得当前伤害,Status Info 读取持有者的 Strength 层数,Math Binary 将两项数值相加,再由 Write Hook 把结果写回伤害链路,末尾的 Play Effect 为这次加成提供表现反馈

这条连接关系使 Description 中的“每层增加 1 点伤害”成为可执行规则,Max Stacks 只负责限制层数,实际效果计算始终读取单位当前持有的层数

Behavior 可以同时包含回合入口、生命周期入口、Hook 与表现节点,状态不需要主动响应战斗时机时也可以保持为空

空 Behavior 不会影响状态的保存、显示与叠加,只是不再自行执行回合或生命周期规则

这些入口与节点仍然遵守 FlowGraph 模型,具体节点见 Hook 节点Get 节点,可直接复用的组合见状态行为配方

行为图完成后,回到 Game Card Editor,右侧 Preview 会先核对玩家可见结果,再检查顶层入口是否已经进入摘要

在 Preview 中核对 Strength

Display

Display 会呈现 Icon、Name、Buff 或 Debuff、Description、Stack Rule、Decay Rule 与 Max Stacks

Strength 的 Display 预览,显示拳头图标、Buff、说明、Additive、Never 与无限层数

预览中的图标、Strength 名称、Buff 标签与 Additive · Never · Max ∞ 已经和前面的字段保持一致,Tags 与 Show Stack Count 不会出现在这张卡片中,实际徽章是否显示数字仍需进入 Play Mode 检查

Behavior Summary

Behavior Summary 会收集 Behavior 中的顶层 EntryNode,再从每个入口沿第一条 Out 控制连接向后显示最多 8 个节点

Strength 的 Behavior Summary,列出 OnDealDamage、OnStatusReceived、OnStatusExpired 与 OnStatusRemoved

当前四行摘要确认 Strength 存在伤害 Hook、接收、自然过期与主动移除入口,但摘要不会展开分支、遍历所有 Out 连接或证明每条路径都能执行到 Action,完整结构仍应在 FlowGraph Editor 中确认

Preview 确认了字段与入口后,再查看当前 Status Database 的 Issue Badge

清除 Validation 问题

Status 模式会扫描当前选中 Status Database 中的全部状态,而不只检查正在查看的 Strength

级别触发条件
ErrorName 为空
WarningMaxStacks == 0
WarningBehavior 非空且 FlowGraphValidator 返回结构问题

以下边界不会进入 Validation,需要在对应位置自行确认

自检位置Validation 未覆盖的内容
Inspector 与 DisplayDescription、Icon 或 Tags 为空,Type 与实际效果不一致,Show Stack Count 不符合层数语义
FlowGraph EditorBehavior 为空,入口与执行路径没有实现 Description 承诺的规则
字段与 Play ModeMaxStacks < -1,层数、徽章与实际效果之间的语义不一致

点击 Issue 中的 Ping 可以定位对应状态、字段或 FlowGraph,常见结构问题见创作错误

字段和行为通过检查后,Used By 会把 Strength 重新连接到施加或读取它的内容资产

用 Used By 追踪状态引用

Used By 会扫描当前 GameCardManager 已启用的 Card、Status 与 Enemy Unit Database,并在各资产的 Behavior 中查找对当前状态的直接引用

Strength 的 Used By 可见区域,包含 Rally、Intimidate、Lord of Ruin、Ritual 与 Pyre Effigy

当前可见区域依次列出三张卡牌、RitualPyre Effigy,继续滚动还会看到 Lantern SisterOrder KnightStrength 的完整引用结果如下

资产类型首个匹配节点
RallyCardChange Status
IntimidateCardChange Status
Lord of RuinCardUnit Status Stacks
RitualStatusChange Status
Pyre EffigyEnemyChange Status
Lantern SisterEnemyChange Status
Order KnightEnemyChange Status

同一资产即使多次引用 Strength 也只显示一行,并使用首个匹配节点作为上下文,Used By 还会递归扫描 Group Body,未启用数据库、其他场景、Prefab、代码与 GCSApi 调用不在结果中

点击任一结果会切换到对应的 Card、Status 或 Enemy 模式,引用位置已经明确后,就可以继续检查 Change Status 如何把资产定义转换为单位的运行时层数

从 Change Status 进入运行时生命周期

GameStatus 保存的是可复用定义,真正进入战斗后,每个 UnitState 都会独立保存当前层数与当前记录的施加者,修改一个单位的 StrengthPoison 不会回写状态资产,也不会影响其他单位

每次 ApplyStatus 真正改变层数时,运行时都会把施加者更新为本次调用传入的 sourcesource 为空时记录状态持有者

ModifyStatusStacksRemoveStatus 不会覆盖已有记录,若 ModifyStatusStacks0 层创建状态且尚无记录,则把状态持有者记为施加者,状态被移除时这条记录也会一起清除

Delta 模式下大于 0 的 Amount 会通过 ApplyStatus 进入完整施加链路

阶段运行结果
输入检查目标为空、状态为空或传入层数小于等于 0 时不执行
来源方 Hook存在来源单位时,来源方的 OnInflictStatus 可以修改层数或取消施加
接收方 Hook目标方的 OnReceiveStatus 可以继续修改层数或取消施加
合并与上限AdditiveMaxReplace 合并现有层数,再用正数 Max Stacks 裁剪结果
写入与事件层数发生变化时保存新值与施加者,并发送 OnStatusChangedOnStatusInflicted
FlowGraph 入口来源单位当前持有的状态响应 OnStatusInflicted,本次施加的状态定义响应 OnStatusReceived,这两项在层数合并后没有变化时仍会执行,只有目标单位当前持有状态的 OnStatusStacksChanged 以层数实际变化为前提

Change Status 会根据 Mode 与 Amount 选择不同入口

配置调用路径
Delta 且 Amount 大于 0调用 ApplyStatus,经过施加 Hook、Stack Rule 与 Max Stacks
Delta 且 Amount 小于 0调用 RemoveStatus,按绝对值减少层数
Delta 且 Amount 等于 0不执行
Set通过 ModifyStatusStacks 把当前层数调整到 Amount,结果最低为 0 并受正数 Max Stacks 限制,不经过施加 Hook、Stack Rule、OnStatusReceivedOnStatusInflicted

节点的目标来源、Status 与 Amount 端口见 Action 节点,生命周期入口的触发时机见 Entry 节点

主动移除以及主动把层数降至 0 会进入同一类生命周期,自然衰减至 0 则会进入另一类,这项区别可以在 Poison 的完整回合中直接观察

让 Poison 在回合中结算并衰减

Strength 使用 Never 并通过 Hook 持续改写伤害,Poison 则使用 Additive、无限层数与 PerTurn,它会在持有者回合开始时按当前层数造成无视 Armor 的伤害

在 FlowGraph Editor 中打开 Poison

Poison 的 FlowGraph 从 On Turn Start 读取当前层数并造成无视 Armor 的伤害

上图中的执行路径从 On Turn Start 进入 Deal DamageStatus Info 把当前 Poison 层数写入伤害 Amount,Ignore Armor 已启用,末尾的 Play Effect 负责飘字与中毒表现

图中没有减少层数的 Action,因为 PerTurn 会在持有者自己的回合结束后自动减少 1 层,玩家状态在玩家回合结束后衰减,敌人状态在该敌人的行动结束后衰减,完整时序见状态系统

PerTurn 自然衰减至 0 时,状态执行 OnStatusExpired 并发送同名事件,通过 RemoveStatus、负数 Delta 或 Set 主动降至 0 时,则执行 OnStatusRemoved 并发送对应事件,每次真实层数变化都会发送 OnStatusChanged

打开随包场景 Hunter/1-Fenmoss Hollow.unity,用 Poison Arrow 命中 Plague Stalker,卡牌会先造成 3 点伤害并施加 2 层 Poison

Poison Arrow 结算后,Plague Stalker 从 16 降至 13 HP 并获得两层 Poison

画面中的 13/16 HP 与两层 Poison 来自同一次 Poison Arrow 结算,结束玩家回合后,这两层状态会在敌人回合开始时进入刚才的 On Turn Start 路径

敌人受到 2 点无视 Armor 的伤害并完成行动后,PerTurn 再把 Poison 从两层衰减为一层,第二个玩家回合开始时可以同时看到两个结果

第二个玩家回合开始时,Plague Stalker 为 11/16 HP 并保留一层 Poison

13/16 → 11/16 证明 Poison 按两层数值结算了伤害,2 → 1 则证明 Decay Rule 在同一敌人回合结束后完成衰减,完整操作过程见第一场战斗

编辑器配置、FlowGraph 和 Play Mode 已经形成同一条可验证链路,项目代码需要查询或控制这些运行时状态时,则通过 GCSApi 接入

通过 GCSApi 读取与控制状态

GCSApi 按内容资产、单位实时层数、修改操作与事件提供公开入口

  • 查找状态内容ActiveStatusDatabases()Statuses()FindStatus(guid)
  • 读取单位状态GetStatusStacks(unit, status)HasStatus(unit, status, minStacks)GetStatusApplier(unit, status)StatusesOn(unit)
  • 修改状态ApplyStatus(target, status, stacks, source)RemoveStatus(target, status, stacks)ModifyStatusStacks(target, status, delta)
  • 监听状态变化OnStatusChanged()OnStatusTicked()OnStatusInflicted()OnStatusExpired()OnStatusRemoved()

Statuses() 会在 GCSApi.Manager 非空时为每次调用新建列表并汇总已启用数据库中的状态,频繁读取时应当缓存结果,Manager 为空时 ActiveStatusDatabases()Statuses() 返回空集合,FindStatus() 返回 null

StatusesOn(unit) 返回单位状态表的实时只读视图,不应由调用方修改

单位为空时,GetStatusStacks() 返回 0HasStatus() 返回 falseGetStatusApplier() 返回 nullStatusesOn() 返回空集合,没有活动控制器时,三项修改方法不会执行操作

OnStatusTicked 会在某个状态时机执行后检查持有者自身 HP 与当前状态层数,任一数值发生变化时便提供这项状态的前后值,检查覆盖 Battle Start、Turn Start、Draw、Discard、Turn End 与 Battle End 等已分发时机,不会捕获其他单位或其他状态在同次执行中的变化

其余四类事件分别覆盖每次层数变化、真正改变层数的施加、自然过期与主动移除

各入口的参数、返回值与订阅方式见 GCSApi 使用指南API 参考事件参考

StrengthPoison 现在已经从资产定义进入真实战斗

此时,卡牌、卡组、玩家与敌人已经各自完成创作,状态规则也经过了编辑器与 Play Mode 验证,切换到 Encounter,按遭遇战创作把这些内容装配为一场可启动、可验证的战斗