多角色队伍
配置最多三名玩家角色,在共享回合、牌堆和能量的战斗中选择行动角色与目标
GameEncounter 可以配置一至三名玩家角色,每名角色都有独立的生命、护甲、状态、Hook 和场景表现,全队默认共享玩家回合、牌堆和能量,现有单角色配置仍可直接使用
上图展示了多角色战斗的配置与执行流程,Encounter 创建队伍并分配角色槽位,成员共用牌堆和能量,出牌时由指定角色执行 FlowGraph,事件和场景表现通过角色 ID 对应到实际来源与目标
多角色队伍用于本地战斗,支持自定义行动与出牌策略,不包含网络连接、状态同步、回滚、房间或账号系统
用 Encounter 建立初始队伍
在 Player Units 中配置初始队伍,第 1 行是主角色,第 2、3 行是追加成员,编辑器和运行时均最多支持三名玩家角色
| 配置 | 运行结果 |
|---|---|
Player Units[0] | 创建槽位 0 的主角色,旧版 PlayerUnit 和 GCSApi.Player() 仍解析该成员 |
Player Units[1..2] | 按行顺序创建额外的 PlayerUnitState |
| 行顺序 | 直接映射场景槽位 0、1、2,不需要额外配置槽位 |
Workbench 会报告空成员、超过三人、越界槽位与重复槽位,Encounter 预览会在 VS 左侧显示玩家队伍,进入 Play Mode 前可在此核对阵容
角色身份贯穿整场战斗
战斗中的每名单位都有独立身份,使用 UnitId 查找实例,使用 Team 和 TeamId 判断阵营,使用 SlotIndex 安排位置,使用 JoinOrder 记录加入顺序,角色资产名和列表下标不能代替这些字段
| 身份 | 作用 |
|---|---|
UnitId | 唯一标识本场战斗中的一个单位实例,同一 Player Unit 资产被创建两次时会得到两个不同值 |
Team / TeamId | 决定友军与敌军关系,内置玩家和敌人分别使用 Player 与 Enemy |
SlotIndex | 把运行时单位映射到玩家或敌人侧的可见槽位 |
JoinOrder | 保留成员进入队伍的先后顺序,增援与主角色变更不会打乱其余成员 |
GCSApi.Player() 返回主角色,GCSApi.ActivePlayer() 返回当前行动角色,GCSApi.PlayerParty() 返回全部队伍成员,包括死亡成员,需要只显示存活或死亡角色时,分别使用 AlivePlayers() 或 DeadPlayers()
一次玩家回合由整支队伍共同完成
默认每回合只有一次玩家阶段,回合开始时处理各成员的护甲、状态与回合触发,再为全队刷新一次能量和抽牌,玩家可在此阶段切换角色并出牌,点击 End Turn 后结束全队回合并进入敌人阶段
SkipNextTurns 只影响对应成员,其他成员仍可行动,全部存活成员都被跳过时,本次队伍回合直接结束
默认只有全队成员都死亡时才判定失败,主角色死亡后其余成员可以继续战斗,胜利检查要求 HasEncounteredEnemy 为 true,因此 Encounter 可以先不配置敌人,再在运行时生成首名敌人
需要按顺序行动时,将 BattlePolicySet.ActivationPolicy 设为 OrderedPartyActivationPolicy,通过 EndActivePlayerActivation() 结束当前成员的行动并切换到下一名成员
把 BattlePolicySet 传给 GCSApi.StartBattle 后,GCS 会在创建单位和卡牌前补齐默认策略,本场战斗的 ActivePolicies 为只读配置,修改规则需要为下一场战斗传入新的策略集
| 策略 | 决定的规则 |
|---|---|
IPartyActivationPolicy | 自由选择或按顺序激活成员 |
IAutomaticPartyActivationPolicy | 在顺序回合中自动执行同伴行动 |
IActionWindowPolicy | 哪些阶段接受玩家出牌请求 |
IReactionWindowPolicy | 敌方行动前是否打开可暂停的反应窗口 |
ICardPlayPolicy | 项目自己的行动来源或卡牌许可 |
IUnitTargetPolicy | 不依赖画面选中对象,为行动者解析默认敌对目标 |
IBattleResultPolicy | 当前状态是否进入胜利或失败阶段 |
全队共享卡牌与能量
全队共用 Hand、Draw、Discard、Exhaust 和当前能量,起手、回合抽牌、洗牌、手牌上限、能量变化与卡牌移动均按队伍结算一次
持久 Master Deck 为空时,使用 Player Unit 1 的 Starting Deck 创建共享 Draw 牌堆,其他成员的 Starting Deck 不会合并进当前战斗,在其他 Encounter 中成为主角色时仍可使用自己的卡组
卡牌的 OwnerUnitId 记录创建时的原始所有者,切换行动角色不会改变该值,符合出牌条件的其他成员也可以打出共享卡牌
指定卡牌的行动来源
TryPlayCard(card, target) 使用当前 ActivePlayer() 作为行动来源,FlowGraph 的 Self、来源 Hook 和出牌事件均使用该角色,卡牌的 OwnerUnitId 保持不变
需要指定行动角色时,使用 CardPlayRequest 提交 SourceUnit 和目标,按索引选择第二名存活成员前,需确认队伍至少有两名存活角色
PlayerUnitState member = GCSApi.AlivePlayers()[1] as PlayerUnitState;
GCSApi.TrySetActivePlayerUnit(member);
foreach (CardInstance card in GCSApi.Hand(member))
{
var request = new CardPlayRequest(card, member, GCSApi.FirstAliveEnemy());
int cost = GCSApi.GetEffectiveEnergyCost(card);
int energy = GCSApi.Energy(member);
if (cost <= energy && GCSApi.CanPlayCard(request))
{
GCSApi.TryPlayCard(request);
}
}
出牌成功后,CardInstance.LastPlayContext 保存只读的出牌记录,包括出牌前区域、原始所有者、实际行动来源和目标列表,OnCardPlayed 也提供这些身份信息,自定义 UI 和统计可直接读取实际执行者
卡牌可以选择友军并按相对阵营结算
Card Target 除了原有敌方目标外,还提供 Single Ally、Other Ally、All Allies、Random Ally、Random Opponent 与 Any Unit,单体友军和任意单位输入会在 Demo 中使用同一套拖拽箭头与合法目标高亮,范围和随机目标则由运行时自动解析
| Card Target | 玩家输入 | Behavior 的对应来源 |
|---|---|---|
Single Ally | 选择任意一名存活友军,允许选择来源角色 | Opponent 不适用,使用已提交 Target 或目标端口 |
Other Ally | 选择来源角色以外的存活友军 | 使用已提交 Target,运行时会拒绝来源角色本身 |
All Allies | 不需要单体输入 | All Allies |
Random Ally | 不需要单体输入 | 运行时从全部存活友军中选择一名,并通过 Target 交给 Behavior |
Random Opponent | 不需要单体输入 | 运行时从来源角色的对方阵营选择一名存活单位,并通过 Target 交给 Behavior |
Any Unit | 选择任意一名存活玩家或敌人 | 使用已提交 Target,不限制双方阵营 |
FlowGraph 的 All Allies 和 All Opponents 分别读取当前宿主的存活友军与对手,可用于玩家卡牌、敌人和状态,Active Player 返回当前选中的存活玩家,All Dead Allies 返回可供复活的死亡友军,完整规则见目标选择
在战斗中增援、替换与复活成员
Spawn Unit 按 Team 创建玩家或敌人,玩家队伍最多三人,增援成功后分配槽位并发布 OnPlayerUnitAdded,沿用全队共享的牌堆和能量
ReplacePlayer 在原槽位替换成员,Despawn Unit 移除单位并发布 OnUnitRemoved,普通死亡发布 OnUnitDied 并保留成员与槽位,Revive Unit 复活后发布 OnUnitRevived
PlayerUnitState joined = GCSApi.SummonPlayer(playerDefinition, 2, GCSApi.ActivePlayer());
if (joined != null)
{
GCSApi.SetEnergy(joined, GCSApi.MaxEnergyFor(joined));
}
GCSApi.RemoveUnit(joined);
移除的成员不能再复活,同一次死亡只发布一次死亡事件,移除或替换成员不会改变共享牌堆和能量,使用 OnUnitRemoved 区分离队,使用 OnUnitDied 区分留在队伍中的死亡成员
Demo BattleBoard 会订阅成员加入、单位移除、死亡、复活与当前角色变化,单角色场景只显示一个玩家槽位,第二或第三名玩家加入时,运行时在同一个 Player Area 根对象下展开子槽位,Party Showcase 直接保存三个子槽位,Dashboard 的 Initialize System 也会创建这套层级
在 Party Showcase 中验证完整链路
打开 Demo/Scenes/Multi-Character/Party Showcase 后进入 Play Mode,棋盘应同时显示 Scout、Squire、Apprentice、Plague Stalker、Spore Brute 与 Mire Reaper,底部只保留一组共享手牌和能量显示

Player Unit 1 的 Scout 提供 Party Tactics,运行时据此创建全队共享的八张卡牌
- 确认三个玩家槽位分别显示生命、护甲与状态区域
- 点击不同成员,确认当前角色与能量显示同步变化
- 切换成员后打出共享
Party Tactics中的攻击牌,确认费用由同一份队伍能量支付、当前成员成为SourceUnit,卡牌原始OwnerUnitId保持不变 - 依次使用
Field Mend、Interpose、Shared Resolve与Emergency Aid,确认指定队友、排除自身、全体队友与随机队友四条目标路径 - 结束队伍回合,确认所有成员完成回合生命周期后敌人才开始行动
- 打开 Game Card Monitor,核对每名成员的
UnitId、槽位、当前行动状态、共享能量与状态
Monitor 中的行动来源与目标应对应场景中的实际角色,单角色 Encounter 只需保留一行 Player Units