样例讲解
三个随包文件把扩展面从头覆盖到尾:一个可复制粘贴的模板、一个带条件的真实卡牌效果、一个纯值节点。
面向希望从可运行代码开始理解节点扩展面的程序员。GCS 的自定义节点样例位于:
Assets/TinyGiants/GameCardSystem/Samples~/CustomNodes/
Unity 会忽略名字以 ~ 结尾的文件夹,所以这些文件以参考源码的形式随包发布,不会就地编译。把需要使用的 .cs 文件复制进你自己的 runtime assembly 再构建。每个样例都保持较小范围,完整覆盖 public 扩展面:[Serializable]、[FlowNode]、节点基类、推断字段、声明端口和 context helper。
推荐工作流
- 把一个样例文件从
Samples~复制到你自己的项目代码里 - 放进引用了
TinyGiants.GCS.Runtime的 runtime assembly - 把 namespace 改成你项目的
- 重命名 class 和
[FlowNode]的 display name - 用做卡作者应该看到的选择替换字段
- context 读取保持用
PullInputOr、ResolveUnits、ResolveCards - 战斗改动保持在
ctx.Controller里 - 等 Unity 编译,然后在 Add Node 菜单里找到节点
先复制再改还可以避免包更新覆盖你的游戏专属节点。
FlowNodeTemplate
FlowNodeTemplate.cs 是覆盖面最广的起点:所有必需部件集中在一个文件里,注册为 Action / Your Node Name。
| 部件 | 用途 |
|---|---|
[Serializable] | 让 Unity 把节点实例保存进 FlowGraph 资产 |
[FlowNode("Action", "Your Node Name")] | 把节点加入 Add Node 菜单 |
MutatorNode | 在 Execute 里运行可写的战斗逻辑 |
UnitSource TargetSource | 自动创建 Target 单位输入(默认 Opponent) |
int Amount | 自动创建 Amount scalar 输入 |
DamageStyle Style | 枚举配置行,显示在节点上而不是端口 |
INodePorts | 声明字段推断造不出来的 Result 输出 |
ctx.PullInputOr | 读接线值,回落到节点字段 |
ctx.ResolveUnits | 同时支持目标下拉和已接线的目标端口 |
ctx.Controller.DealDamage | 通过受支持的通道修改战斗状态 |
ctx.StoreResult | 把声明好的 Result 输出发布给下游节点 |
节点会改战斗状态、还可能暴露输出时,从这里开始。
安全的改动:
- 改 category 和 display name
- 用你自己的面向作者字段替换
TargetSource、Amount和枚举 - 用你的节点实际执行的战斗改动替换 controller 调用
- 在还没有已保存内容连接它之前,重命名 result 输出
高风险的改动:
- 移除
[Serializable] - 把文件挪进 editor-only assembly
- 直接改单位、卡牌或牌堆字段
- 已保存的图连上字段或输出之后再重命名它们
HealIfBelowHalfNode
HealIfBelowHalfNode.cs 是普通卡牌效果的最佳样例。它建模一个用户可见行为:治疗选中的单位,可选只治疗半血及以下的。
| 字段 | 作者看到什么 |
|---|---|
TargetSource | 谁能被治疗。默认 Self,可以暴露并接线 |
HealAmount | 恢复多少 HP。固定值或接线输入 |
OnlyBelowHalf | 半血条件是否生效 |
它的行为:
HealAmount通过ctx.PullInputOr(this, nameof(HealAmount), HealAmount)读取- 目标来自
ctx.ResolveUnits(this, TargetSource, nameof(TargetSource)) - 死亡和 null 单位被跳过,半血检查比较每个目标的当前 HP 和最大 HP
- 治疗走
ctx.Controller.GainHp Healed输出发布的是实际恢复的 HP,按治疗前后差值统计。满血目标贡献 0
项目专属动作可以参考这个模式:按自定义条件治疗友方、对被标记的敌人加成伤害、按已损失 HP 获得护甲,或者统计有多少目标通过检查并把数量传下去。
AddValuesNode
AddValuesNode.cs 是最小的纯值样例,注册为 Operator / Add Values:读两个输入,返回它们的和。
| 字段 | 含义 |
|---|---|
A | 第一个数字,字段作回落值 |
B | 第二个数字,字段作回落值 |
它的行为:
- class 继承
OperatorNode,从不调用ctx.Controller - 用
NodePort.ResultOut("Result", PortDataType.Int)声明Result - 从
Evaluate(IEvaluationContext ctx, string outPort)返回和
数学、比较、分数计算、转换器,以及任何要接进其他节点输入的项目公式都可以采用这个模式。有多个输出的节点逐个声明,然后按 outPort 分流:
public override object Evaluate(IEvaluationContext ctx, string outPort)
{
int value = ctx.PullInputOr(this, nameof(Value), Value);
return outPort == "Double" ? value * 2 : value;
}
从哪个样例起步
| 你想做的 | 从这里开始 |
|---|---|
| 改状态的卡牌效果 | HealIfBelowHalfNode 或 FlowNodeTemplate |
| 计算值 | AddValuesNode |
| 自定义 selector | AddValuesNode,基类换成 SelectorNode,返回单位或卡牌 |
| 自定义分支节点 | FlowNodeTemplate,基类换成 ControlNode,声明 ControlOut 端口 |
| 表现节拍 | FlowNodeTemplate,基类换成 VfxNode,实现 Play |
验证改造后的节点
- Unity 无 C# 错误编译通过
- 节点出现在 Add Node 菜单里它的
[FlowNode]category 下 - scalar 字段显示为可编辑值,并且能暴露成端口
<Name>Source字段暴露出对应的单位或卡牌输入- 声明的 result 输出可见,能连给下游
- 测试战斗在 Monitor 里显示预期的状态变化
Monitor 的 Flow 页是确认节点实际运行过的最快方式。每次图执行都会连同触发器、节点数和耗时一起被捕获;一张打出后经过你节点的卡,会在列表中显示为一行记录:

节点能编译但行为异常时,先用固定字段值测试,再接动态端口。固定版本正确之后,一次只暴露一个端口。
常见错误
| 症状 | 可能原因 |
|---|---|
| Add Node 里找不到节点 | 缺 [FlowNode]、class 是 abstract,或程序集没引用 TinyGiants.GCS.Runtime |
| 接线值被忽略 | 代码直接读字段,没走 ctx.PullInputOr |
| 接线目标被忽略 | 代码跳过了 ctx.ResolveUnits / ctx.ResolveCards |
| 输出连不上线 | 输出从未用 NodePort.ResultOut 或 NodePort.Out 声明 |
| 输出值永远缺失 | StoreResult 的名字和已声明输出名对不上 |
| 事件和视觉都不触发 | 代码直接改战斗对象,没调用 ctx.Controller |