跳到主要内容

战斗启动

GCS · 项目设置

一个场景固定运行一场遭遇战时使用 Bootstrap;地图、任务或关卡流程需要动态选择遭遇战时,调用 GCSApi.StartBattle,并通过生命周期事件把控制权交回自己的游戏逻辑

当 GCS 战斗只是完整游戏循环中的一个环节,而不是整个场景的全部内容时

GameCardManager 负责让内容可用,什么时候开战则是另一件事:先选出 GameEncounter,再决定何时启动,以及胜利或失败之后游戏回到哪里

选择启动方式

进入 Play Mode 后总是运行一场固定遭遇战时,使用 GameCardEncounterBootstrap。Demo、独立战斗测试,以及专门为某场遭遇战加载的战斗场景都适合这种方式

使用 GameCardEncounterBootstrap 自动启动

GameCardEncounterBootstrapGameCardManager 挂在同一个 GameObject 上。它的字段只控制自动启动这条路径:

字段默认值作用
Default Encounter新组件为空自动启动时传给 GameCardManager.StartBattle 的遭遇战
Auto Start On Play开启Start 中启动 Default Encounter;由其他系统选遭遇战时应关闭
Use GES Events关闭决定战斗启动后调用 UnityEvent,还是触发已配置的 GES 事件
On BootstrapUse GES Events 关闭时,在 Bootstrap 调用 StartBattle 后触发
On Bootstrap EventsUse GES Events 开启时,在 StartBattle 后按列表顺序触发

GameCardEncounterBootstrap 已指定默认遭遇战,并开启 Auto Start On Play

Inspector 会检查 Default Encounter 是否位于启用的遭遇战数据库中。状态显示 Ready,说明选择和数据库登记一致。自动启动已开启但字段为空时,Play Mode 只会记录警告,不会启动战斗

Bootstrap 上的 UnityEvent 和 GES 列表是这个组件的启动后输出。代码直接调用 GCSApi.StartBattle 时不会触发它们,因此地图或关卡流程不能只依赖这些字段监听战斗生命周期

不使用 Bootstrap 启动遭遇战

场景仍然保留 GameCardManager,将 Auto Start On Play 关闭,再把选中的遭遇战交给公开 API:

using TinyGiants.GCS.Runtime;
using UnityEngine;

public sealed class EncounterLauncher : MonoBehaviour
{
[SerializeField] private GameEncounter encounter;

public void StartConfiguredEncounter()
{
if (encounter == null) return;
GCSApi.StartBattle(encounter);
}

public void StartEncounter(GameEncounter selectedEncounter)
{
if (selectedEncounter == null) return;
GCSApi.StartBattle(selectedEncounter);
}
}

地图节点、UI 按钮、任务系统或你自己的 GES 事件处理器都可以调用这两个公开方法。GCSEventNames 里没有一个内置的“开始遭遇战事件”:GCSApi.StartBattle 是启动命令,战斗生命周期事件则是启动之后发出的通知

直接启动仍要求 Manager 启用的卡牌、状态、敌人、玩家和卡组数据库覆盖这场遭遇战引用的内容。把遭遇战本身也放在启用的遭遇战数据库中,可以保证它继续出现在工作台、选择器和 GCSApi.Encounters() 的查询结果里

监听战斗开始与结束

必须先订阅,再调用 StartBattleOnBattleStarted 会在创建战斗状态的过程中同步发出,启动之后才订阅会错过这一场战斗

事件参数准确时机
GCSEventNames.OnBattleStarted玩家、敌人、初始牌库和起手手牌已经创建;随后解析敌人意图,并进入第一个玩家回合
GCSEventNames.OnBattleRewardOfferedBattleRewardOfferedEventArgs已判定胜利,GCS 正在等待玩家选择或跳过奖励
GCSEventNames.OnBattleEndedBattleEndedEventArgs失败时立即发出,Won == false;胜利时要等奖励被选择或跳过后才发出,Won == true

GCSApi.Subscribe 返回 IDisposable。保存订阅句柄,并在监听对象禁用或销毁时调用 Dispose

完整的地图到战斗循环

把下面的协调器和 GameCardManager 放在始终保持激活的对象上,MapRootBattleRoot 只是它控制的子对象,BattleRoot 只承载表现对象,不包含 Manager。协调器会先激活战斗表现,再调用 StartBattle,这样各个 Presenter 能在 OnBattleStarted 发出前完成订阅

using System;
using TinyGiants.GCS.Runtime;
using UnityEngine;

public sealed class MapEncounterFlow : MonoBehaviour
{
[SerializeField] private GameObject mapRoot;
[SerializeField] private GameObject battleRoot;

public event Action<GameEncounter> EncounterStarted;
public event Action<GameEncounter, bool> EncounterFinished;

private IDisposable battleStartedSubscription;
private IDisposable battleEndedSubscription;
private GameEncounter activeEncounter;

private void OnEnable()
{
battleStartedSubscription = GCSApi.Subscribe(
GCSEventNames.OnBattleStarted,
OnBattleStarted);

battleEndedSubscription = GCSApi.Subscribe<BattleEndedEventArgs>(
GCSEventNames.OnBattleEnded,
OnBattleEnded);
}

private void OnDisable()
{
battleStartedSubscription?.Dispose();
battleStartedSubscription = null;
battleEndedSubscription?.Dispose();
battleEndedSubscription = null;
}

public void StartMapEncounter(GameEncounter encounter)
{
if (encounter == null || GCSApi.Manager == null) return;
activeEncounter = encounter;
battleRoot.SetActive(true);
mapRoot.SetActive(false);
GCSApi.StartBattle(encounter);
}

private void OnBattleStarted()
{
EncounterStarted?.Invoke(activeEncounter);
}

private void OnBattleEnded(BattleEndedEventArgs args)
{
battleRoot.SetActive(false);
mapRoot.SetActive(true);

EncounterFinished?.Invoke(activeEncounter, args.Won);
activeEncounter = null;
}
}

转场或统计系统可以订阅 EncounterStarted,存档、地图节点、奖励或进度系统则订阅 EncounterFinished。边界很清楚:GCS 负责这一场战斗,你的协调器负责战斗前后的完整游戏流程

玩家放弃尚未结束的战斗,或提前离开战斗场景时,先调用 GCSApi.DisposeBattle()。这个调用只清理当前战斗,不会发出 OnBattleEnded,取消战斗后的地图处理需要由你的代码完成

进入 Play Mode 前检查

  1. 场景中只有一个 GameCardManager,并启用了这场战斗需要的数据库
  2. 自动启动:已指定 Default Encounter,并开启 Auto Start On Play
  3. 流程控制:关闭 Auto Start On Play,由协调器调用 GCSApi.StartBattle
  4. 生命周期订阅早于启动调用,并在对象禁用时释放
  5. 胜利流程完成奖励选择或跳过后,再等待 OnBattleEnded