UE5.8 Chooser 喂饭级专题

UE5.8 Chooser 专题(八):玩家编辑技能连招完整案例

把 Enhanced Input、Common UI、SaveGame、GAS、GameplayTag、Chooser 和动画串起来,讲一个可让玩家编辑轻重攻击派生的生产级设计。

总览

你提到的“支持玩家编辑技能连招”非常适合拿来讲 Chooser,但要先拆清楚:玩家不是直接编辑 Chooser 表,玩家编辑的是自己的连招档案;Chooser 用这份档案加上战斗上下文,选择下一段动作;GAS 最后做权威激活。

UE5.8 Chooser 专题(八):玩家编辑技能连招完整案例 配图
玩家编辑的是连招档案;Chooser 负责把档案和战斗上下文解析成可执行的下一段动作。

目标体验

玩家在菜单里看到技能面板:

轻攻击链:
  第 1 段:迅斩
  第 2 段:旋斩
  第 3 段:破甲斩
空中轻攻击:坠击
闪避后轻攻击:回身斩

玩家可以替换某个槽位,但只能放入已解锁、当前武器支持、槽位类型匹配的技能。进入战斗后,按轻攻击时系统按档案解析下一段;如果目标太远、蓝不够、角色被硬直,不能强行释放。

数据模型

技能定义用 DataAsset:

UCLASS(BlueprintType)
class UComboMoveData : public UPrimaryDataAsset
{
    GENERATED_BODY()

public:
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
    FGameplayTag MoveTag;

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
    FGameplayTagContainer SlotTags;

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
    FGameplayTagContainer RequiredWeaponTags;

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
    TSubclassOf<UGameplayAbility> AbilityClass;

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
    TObjectPtr<UAnimMontage> PreviewMontage;
};

玩家档案只保存轻量标识:

USTRUCT(BlueprintType)
struct FPlayerComboProfile
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    TMap<FGameplayTag, FGameplayTag> SlotToMove;
};

比如 ComboSlot.Sword.Light.02 -> Move.Sword.SpinSlash。不要保存直接对象指针,跨版本和存档迁移会麻烦。

UI 怎么列出可选技能

Common UI 面板打开某个槽位时,构造 Context:

EditingSlotTag = ComboSlot.Sword.Light.02
WeaponTag = Weapon.Sword
PlayerTags = 已解锁技能、职业、天赋
InputTag = Input.Attack.Light

调用 Chooser 多结果表 CHT_Combo_EditCandidates,返回所有合法 UComboMoveData。这张表只负责“这个槽位能放什么”。UI 显示 MoveData 的名字、图标、预览动画。玩家选中后,SaveGame 只记录 MoveTag。

战斗时怎么解析

战斗里用另一张表 CHT_Combo_NextMove。Context 包含:

USTRUCT(BlueprintType)
struct FComboRuntimeContext
{
    GENERATED_BODY()
    UPROPERTY(EditAnywhere, BlueprintReadWrite) FGameplayTag InputTag;
    UPROPERTY(EditAnywhere, BlueprintReadWrite) FGameplayTag PreviousMoveTag;
    UPROPERTY(EditAnywhere, BlueprintReadWrite) FGameplayTag CurrentSlotTag;
    UPROPERTY(EditAnywhere, BlueprintReadWrite) FGameplayTag PlayerPreferredMoveTag;
    UPROPERTY(EditAnywhere, BlueprintReadWrite) FGameplayTag WeaponTag;
    UPROPERTY(EditAnywhere, BlueprintReadWrite) FGameplayTagContainer PlayerTags;
    UPROPERTY(EditAnywhere, BlueprintReadWrite) FGameplayTagContainer TargetTags;
    UPROPERTY(EditAnywhere, BlueprintReadWrite) float TargetDistance = 0.0f;
    UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bIsInAir = false;
};

PlayerPreferredMoveTag 来自档案;如果档案里的技能已经被版本移除或未解锁,表可以自然落到 Fallback 或默认技能。

两张表的职责

调用时机Result
CHT_Combo_EditCandidatesUI 编辑槽位多个 UComboMoveData
CHT_Combo_NextMove战斗按键一个 UComboMoveData 或 Ability Class

编辑表偏“可放入什么”,战斗表偏“此刻能释放什么”。它们可以共享部分列设计,但不要硬合成一张巨表。

GAS 验证

Chooser 选中 MoveData 后,不要直接播放动画:

if (!MoveData || !MoveData->AbilityClass)
{
    return EComboResolveResult::NoMove;
}

FGameplayAbilitySpec* Spec = ASC->FindAbilitySpecFromClass(MoveData->AbilityClass);
if (!Spec || !ASC->TryActivateAbility(Spec->Handle))
{
    return EComboResolveResult::RejectedByGAS;
}

服务器权威项目里,客户端可以预测选择,但服务器仍要用同样的上下文或可验证输入重新判断。玩家档案必须由服务器认可,不能只信客户端传来的 MoveTag。

输出参数

FComboChooserOutput 可以包含:

字段用途
MontageSectionAbility 播放哪段 Montage
NextComboWindow下一段输入窗口
CostMultiplier资源消耗倍率
CameraCueTag触发 Gameplay Camera 或震屏
VfxCueTag触发 GameplayCue
bCanBeCanceled是否允许闪避取消

这些适合 Output Struct,而不是塞进 Result 名字里。

版本升级和存档修复

玩家档案存的是 Tag,就要准备修复表:

Move.Sword.OldSpin -> Move.Sword.SpinSlash
Move.Sword.RemovedHeavy -> Move.Sword.DefaultHeavy

加载 SaveGame 后先迁移,再用编辑候选 Chooser 校验。如果玩家存档里的技能不再合法,替换成默认技能并给 UI 一个提示。

使用案例:玩家改轻攻击第二段

玩家打开连招编辑页,点选 ComboSlot.Sword.Light.02。UI 调用编辑候选表,传入当前武器、已解锁技能和槽位 Tag,得到 DA_Move_SwordSpinDA_Move_SwordUppercutDA_Move_SwordCrossSlash 三个候选。玩家选择旋斩后,SaveGame 只保存 Move.Sword.SpinSlash

战斗中第一次轻攻击成功后,组件把 PreviousMoveTag 设为 Move.Sword.Light01。第二次轻攻击时,运行时 Context 带着 PlayerPreferredMoveTag=Move.Sword.SpinSlash 进入战斗解析表。表先检查输入、武器、上一段、地面状态和距离,再检查玩家偏好。命中后返回 DA_Move_SwordSpin,OutputStruct 写出 MontageSection=Spin_EntryCostMultiplier=1.15CameraCueTag=Camera.Combo.Spin。最后 GAS 激活 Ability;如果冷却或资源不足,GAS 拒绝,UI 播失败提示,连招状态回退或保持窗口。

架构分析

这套方案有三层安全边界:Common UI 限制玩家只能选择合法候选;Chooser 在战斗中按上下文解析,处理版本和状态变化;GAS 在执行前做最终权威判断。任何一层失败,系统都应该能回退,而不是崩掉或让玩家释放非法技能。

常见坑

  • UI 只按本地数组列技能,不走同一套候选规则,导致菜单能选、战斗不能出。
  • SaveGame 直接存资产路径,重命名或 DLC 移除后全坏。
  • 玩家配置影响了 Ability Class,但服务器没有重新验证。
  • Chooser 表里把冷却/资源当最终权威,绕过 GAS。
  • 连招状态只在客户端更新,服务器和动画表现不同步。

源码依据

EvaluateChooserMultiChooseMulti 支持收集多个候选,适合编辑 UI 列表。FChooserEvaluationContext 支持结构体输入和输出;OutputStruct 列可以把每行参数写回。UChooserTable::EvaluateChooser 在没有行成功时会走 FallbackResult,适合处理玩家档案过期或上下文异常。

源码路径索引

  • Engine/Plugins/Chooser/Source/Chooser/Public/Chooser.h
  • Engine/Plugins/Chooser/Source/Chooser/Public/ChooserFunctionLibrary.h
  • Engine/Plugins/Chooser/Source/Chooser/Internal/GameplayTagColumn.h
  • Engine/Plugins/Chooser/Source/Chooser/Internal/GameplayTagQueryColumn.h
  • Engine/Plugins/Chooser/Source/Chooser/Internal/OutputStructColumn.h
  • Engine/Plugins/Chooser/Source/Chooser/Private/Chooser.cpp