战斗场景
Initialize System 仅用六个根对象便搭好了一整套可运行的战斗表现层,这是倾注心血的设计
选定的 GameEncounter 交给 GameCardManager 后,六个根对象会把玩家、敌人、牌堆与阶段状态呈现为可以看见和操作的战场,无论战斗由 GameCardEncounterBootstrap 固定启动,还是由地图、关卡或任务调用 GCSApi.StartBattle 动态启动,最终接管战斗的都是同一组 GameCardManager、Battle Runtime、Battle Canvas 与表现对象
Initialize System 建立场景骨架
打开一个空场景,通过以下菜单进入 Game Card System 窗口,再点击 Initialize System:
Tools > TinyGiants > GCS > Game Card System
命令执行完成后,Hierarchy 会形成一套可以继续指定 Encounter 与调整表现的场景骨架

上图以六个根对象分别承接战斗启动、相机与输入、背景、单位与卡牌交互、HUD 和效果反馈,后续替换画面或接入项目流程时,可以沿着这份 Hierarchy 直接找到对应入口:
| 根对象 | 核心组件 | 在完整链路中的职责 |
|---|---|---|
Main Camera | Camera、AudioListener、Physics2DRaycaster | 渲染世界空间战场,并把指针事件送给带 CardViewInput 的世界空间卡牌 |
GameCardManager | GameCardManager、GameCardEncounterBootstrap | 登记数据库、持有当前战斗状态,并在固定场景中提供默认 Encounter 启动入口 |
EventSystem | EventSystem、StandaloneInputModule | 将指针、Submit、Cancel 与按钮输入送入卡牌、HUD 和临时界面 |
Background | SpriteRenderer、BattleBackgroundFit、BattleEncounterBackground | 填满相机画面,并为固定启动场景应用 Encounter 图标或职业背景 |
Battle Runtime | 战场、手牌、Canvas 与 Presentation 子对象 | 将玩家、敌人、牌堆与战斗阶段转化为可操作的场景内容 |
Battle Fx Directors | IntentThreatTelegraph | 在高威胁攻击意图出现时播放独立的视觉与声音提示 |
-
除 Manager 会先按
GameCardManager组件查找外,同名但内部残缺的Battle Runtime或其他模板根对象不会被整棵替换 -
功能相同但名称不同的相机或 EventSystem 也不会被识别
-
重复初始化前应先核对根对象名称与内部结构,避免残缺对象没有补齐或场景中出现两套相同服务,Background 的组件、排序与职业背景配置仍会按照上方规则补充或改写
若需了解从空场景完成初始化与 Encounter 配置的全部操作可查看创建第二场战斗
启动、输入与背景构成场景基础
GameCardManager 将 Encounter 转为运行时战斗,Main Camera 与 EventSystem 负责接收世界空间卡牌和 Screen Space UI 的输入,Background 提供战场底图:
| 对象 | 模板默认值 | 修改时需要保留的边界 |
|---|---|---|
Main Camera | 正交 Size 5,位置 (0, 0, -10),Viewport 铺满屏幕,开启 HDR 与 MSAA | 可以替换构图、Render Texture 或相机栈,继续使用世界空间卡牌输入时,负责事件相机的 Camera 需要保留 Physics2DRaycaster |
EventSystem | 开启导航事件,Drag Threshold 10,输入轴为 Horizontal、Vertical、Submit、Cancel | 可以换成项目自己的输入模块,场景中只保留一个启用中的 EventSystem |
Background | Sorting Layer 为 Background,Order -20,Overscan 1.01,Vignette Strength 0.3 | 可以完整替换背景系统,最终渲染结果仍需位于单位、卡牌与 HUD 后方 |
GCSApi.StartBattle 动态传入的 Encounter 不会触发这次背景切换,动态流程需要由场景协调器更新背景,或者先将 Encounter 同步到 Bootstrap.DefaultEncounter,再主动调用背景组件的 Apply
完整的演示 Demo 会在单图背景之上绑定 EncounterParallax实现多图层动态控制,而初始化构架你的全新场景则是从干净的底图开始,确保你可以建立自己的视觉体系,若需了解完整的成品层级与动态效果,详见Demo 场景参考
Battle Runtime 将状态转化为可操作的战场
启动、输入与背景就绪后,Battle Runtime 会按玩家看到和操作的顺序展开单位、手牌、界面与反馈
🎬 Battle Runtime # 完整的战斗表现与交互根对象
├── ⚔️ Battle Board # 创建并摆放玩家与敌人单位视图
│ ├── 🦸 Player Area # 玩家单位的世界空间锚点
│ ├── 👹 Enemy Area # 按顺序排列敌人位置的父对象
│ │ ├── 1️⃣ Area1 # 第一名存活敌人的位置
│ │ ├── 2️⃣ Area2 # 第二名存活敌人的位置
│ │ └── 3️⃣ Area3 # 第三名存活敌人的位置
│ └── 🎯 Targeting Arrow # 从拖拽卡牌指向目标的视觉连线
├── 🃏 Hand # 手牌布局、对象池、指针输入与出牌展示
├── 🖥️ Battle Canvas # Screen Space HUD 与弹出界面的根对象
│ ├── 📊 HUD # 能量、牌堆、结束回合按钮与回合横幅
│ ├── 📚 Pile Inspector # 分页查看抽牌堆、弃牌堆与消耗牌堆
│ ├── 🔀 Choice Presenter # 显示流图候选项与确认控件
│ ├── 🎁 Reward View # 选择卡牌、能量、费用或跳过奖励
│ ├── 🏆 Result Popup # 胜利与失败结算面板
│ └── 🎭 Splash Screen # 展示遭遇战信息并延后启动战斗
├── 🗃️ Pile Cards # Pile Inspector 使用的世界空间卡牌容器
├── 🌫️ Pile World Blur # 打开牌堆时压暗后方战斗画面
├── 🃏 Choice Cards # 候选卡牌的世界空间容器
├── 🌫️ Choice World Blur # 选择期间压暗后方战斗画面
├── 🎁 Reward Cards # 奖励卡牌的世界空间容器
├── 🌫️ Reward World Blur # 显示奖励时压暗后方战斗画面
├── 🌫️ Result World Blur # 在结算面板后方压暗战斗画面
└── 🎛️ Presentation # 共享音频、VFX、文字、冲击与光标反馈
├── 🔊 Audio # 音乐、环境音、玩法音效与 Mixer 路由
├── 💥 VFX # 响应场景级与单位级特效请求
├── 💬 Floating Text # 伤害、治疗、状态与自定义战斗文字
├── ⏳ Delayed Trigger Indicator # 标记附着在单位上的延迟效果
├── 💢 Impact Director # 统一调度顿帧与命中反馈
└── 🖱️ Cursor # 应用战斗光标贴图与缩放
Battle Board 先建立单位与目标空间
一场战斗进入场景后,BattleBoard 会根据运行时状态创建玩家与敌人视图,将存活敌人依次放进可用区域,并同步处理 Tier 缩放、血条与状态偏移、意图位置、选中反馈和死亡隐藏时间:

Inspector 中可以直接看到玩家锚点、敌人父对象、三个默认敌人位置以及 Normal、Elite、Boss、Summon 四档视觉配置,截图之外的选择颜色、血条偏移与死亡节奏仍由同一个 BattleBoard 统一控制:
| 配置 | 模板值 | 对战场的影响 |
|---|---|---|
Player Area | Player Area | 决定玩家单位视图的世界空间位置 |
Enemy Area Root | Enemy Area | 作为整个敌方阵型的父对象 |
Enemy Areas | Area1、Area2、Area3 | 按顺序分配给存活敌人,同时显示四名以上敌人时需要增加位置 |
| 玩家区域位置 | (-5.2, 0) | 确定玩家一侧的版面锚点 |
| 敌人区域位置 | (5.2, 0) | 确定敌方一侧的版面锚点 |
| 敌人间距 | -1 | 根据当前敌人数收紧或展开阵型 |
| 单位缩放 | Normal 0.4、Elite 0.5、Boss 0.6、Summon 0.35 | 让不同 Tier 保持可辨认的视觉体量 |
| 意图 Y | Normal 1.4、Elite 2、Boss 2.4、Summon 1 | 将意图 UI 放到对应体型上方 |
| 死亡隐藏延迟 | 0.9 秒 | 为死亡表现留出播放时间,再隐藏单位视图 |
Battle Board 上的 BattleOpeningEnemyTooltip 会把开场敌人说明锚定到 Enemy Area,模板允许它在没有 Splash Screen 时单独运行,逐字间隔为 0.026 秒
项目可以替换它的 Prefab、文本与节奏,不需要开场敌人说明时可以直接停用该组件
Hand 再把卡牌操作接入战场
单位视图和敌人位置建立以后,HandController 会把抽牌、悬停、拖拽、出牌区域与目标选择连接起来,并将成功打出的卡牌交给离场展示和对象池回收

截图中可见的四个组件分别负责手牌布局、视图池、离场路线与出牌展示,Inspector 下方的 HandController 继续连接输入、目标选择与战斗状态
| 组件 | 模板默认值 | 在交互链路中的职责 |
|---|---|---|
HandLayout | Radius 21.2、Max Angle 16、Spacing 3.296、Hover Scale 1.15、Tween 0.15 秒、Entrance 0.38 秒 | 将手牌排成扇形,为悬停卡牌让位,并处理进入与重排动画 |
CardViewPool | Basic 卡牌 Prefab,Initial Per Bucket Size 4 | 每个 Bucket 初始容量为 4,首次使用时按需创建视图,释放后回到池中复用 |
CardFlyOutConfig | Default、Basic、Burst 从 Bottom Right 离场,Skill、Support 从 Bottom Left 离场 | 为不同卡牌类型选择左右离场方向,具体飞行过程由 CardPlayShowcase 完成 |
CardPlayShowcase | 进入 0.28 秒、停留 0.8 秒、飞出 0.65 秒、停留缩放 1.25 | 在卡牌离场前完成聚焦展示,并记录尚未结束的展示数量 |
HandController | 抽牌间隔 0.27 秒、悬停宽限 0.08 秒、Target Pick Radius 1.6、屏幕下方 32% 为出牌区域 | 连接指针输入、目标选择、Battle Board、对象池、手牌布局与出牌展示 |
卡牌尺寸变化会同时影响扇形间距、悬停高度、相邻卡牌切换与目标拖拽手感,调整 Prefab 后要把这些参数作为一组重新校准,运行时卡牌进入与离开这些视图的规则见手牌与三类牌堆
固定场景先由 Splash Screen 编排开场
当 Bootstrap.DefaultEncounter 已配置且 Auto Start On Play 开启时,BattleSplashScreen 会在 Awake 中关闭 Bootstrap 在本次运行中的直接自动启动,先展示遭遇战信息,再延后调用 StartBattle。开场音频、发牌、敌人提示与回合横幅也由这套开场流程协调,BattleSplashScreen 只读取 Bootstrap.DefaultEncounter,无法取得 GCSApi.StartBattle 动态传入的 Encounter
地图或任务动态选择 Encounter 时,需要禁用、移除或完整替换 Splash Screen,避免它继续读取并启动固定 Encounter,固定与动态入口的边界见战斗启动
战斗开始后由 HUD 保持常驻操作
战斗开始后,HUD 会持续显示当前资源与牌堆状态,并把结束回合和牌堆查看操作送回运行时
| 子对象 | 组件 | 玩家实际使用的功能 |
|---|---|---|
Energy Display | UnitEnergyDisplay | 显示玩家当前能量 |
End Turn Button | EndTurnButton、UIButtonSfx | 结束玩家回合,并播放按钮反馈 |
Draw Pile | DeckCounter | 显示数量并打开抽牌堆 |
Discard Pile | DiscardCounter | 显示数量并打开弃牌堆 |
Exhaust Pile | ExhaustCounter | 显示数量并打开消耗牌堆 |
Turn Banner | TurnBanner | 呈现玩家与敌人的回合切换,并在动画期间持有 Animation Gate |
HUD 外观可以全部重做,新的按钮与计数控件仍需调用结束回合与牌堆查询接口,不依赖模板 Prefab 的连接方式见自定义 UI 接入
需要查看或决定内容时进入临时界面
牌堆查看、FlowGraph Choice、胜利奖励与最终结果不会同时常驻屏幕,它们会在对应状态到来时接管输入,并配合前面的 World Holder 与 Blur Sprite 组织世界空间卡牌:
| 触发时机 | 子对象与组件 | 模板配置与运行边界 |
|---|---|---|
| 玩家查看牌堆时 | Pile Inspector、CardPileInspector | 每页 5 × 2 张卡牌,卡牌缩放 0.34,复用 CardViewPool 浏览抽牌堆、弃牌堆与消耗牌堆 |
| FlowGraph 进入 Choice 时 | Choice Presenter、BattleChoicePresenter | 卡牌缩放 0.34,将候选项、确认与跳过操作回传给 FlowGraph,场景缺少 Presenter 时 Choice 会直接进入 Skipped 分支,不会保留一层空白等待界面 |
胜利并发出 OnBattleRewardOffered 后 | Reward View、CardRewardView | 卡牌缩放 0.4,两段揭示延迟均为 0.5 秒,选择卡牌、能量、费用或跳过后调用 ApplyReward 或 SkipReward |
失败结束,或胜利奖励完成并发出 OnBattleEnded 后 | Result Popup、BattleResultPopup | 胜利与失败延迟为 0.9/1.0 秒,打字速度为每秒 42 个字符 |
-
Choice Presenter负责完成 FlowGraph 等待的用户选择,Reward View负责提交奖励结果、解除IsWaitingForReward并让战斗发出OnBattleEnded -
替换这两个界面时必须保留对应回调,奖励界面不能只关闭视觉对象而不提交奖励结果
-
场景中只保留一个
IChoicePresenter实现 -
Manager 会连同未启用对象一起查找 Presenter,同时保留模板组件与自定义组件时无法保证选中顺序,替换 Choice UI 时需要移除旧组件,不能只停用旧 GameObject
Presentation 会让结算结果形成完整反馈
当出牌、状态变化与阶段切换已经正确运行,Presentation 会把这些结果转化为音频、场景特效、浮动文字与冲击反馈,玩家由此能够判断刚才发生了什么,而不必只盯着数值变化
| 对象 | 组件 | 负责的反馈 |
|---|---|---|
Presentation/Audio | BattleAudio | 管理卡牌、UI、战斗、结算、音乐、Mixer、音量与玩法音频 |
Presentation/VFX | BattleVFXPresenter | 响应战斗开始、玩家回合、敌人回合与战斗结束事件,播放场景级 Ambient、BattleStart、TurnStart、Victory 与 Defeat VFX |
Presentation/Floating Text | BattleFloatingTextPresenter | 显示伤害、治疗、状态与自定义战斗文字 |
Presentation/Delayed Trigger Indicator | UnitDelayedTriggerIndicator | 标记附着在单位上的延迟效果 |
Presentation/Impact Director | BattleImpactDirector | 统一处理顿帧与命中冲击反馈 |
Presentation/Cursor | BattleCursor | 应用战斗光标纹理,模板缩放为 2 |
Battle Fx Directors | IntentThreatTelegraph | 任一 Attack 意图值达到或超过 12 时,在敌人上方 0.9 单位处播放威胁 VFX,并以 0.7 音量播放提示音 |
FlowGraph 的 Play VFX 节点会直接实例化节点自身配置的 Prefab,不经过 BattleVFXPresenter
节点效果与场景阶段特效可以独立替换,音频、VFX、浮动文字与 Gate 的接入边界见表现层
- Audio、VFX、Floating Text、Delayed Trigger Indicator、Impact Director、Cursor 与 Intent Threat Telegraph 可以按项目风格逐项替换或移除,移除后只会缺少对应反馈,不会改变运行时结算
- Splash Screen、Hand、HUD、Choice 与 Reward 承担启动、交互或运行回调,新的实现必须继续启动正确的 Encounter,并提交出牌、结束回合、Choice 选择与 Reward 结果
阶段停止推进时,先确认正在等待的对象
AnimationGate通过 Holder 计数等待表现释放- Choice 通过
IsWaitingForChoice等待选择回调 - Reward 通过
IsWaitingForReward等待奖励提交 CardPlayShowcase.ActiveCount只会让奖励与结算画面等待卡牌展示结束