UE5.8 Game Features 喂饭级专题

UE5.8 Game Features 专题(七):像 Lyra 一样注入输入、UI 和 GAS

参考 Lyra 的 GameFeatureAction_AddInputContextMapping、AddInputBinding、AddWidget、AddAbilities 和 GameFeaturePolicy,讲清输入、界面和能力系统如何模块化。

总览

Game Feature 真正在项目里有用,往往不是只加一个组件,而是一起加输入、UI、技能、数据和提示。Lyra 给了很好的参考:玩法包激活后,可以给玩家加输入映射、给 HUD 加 Widget、给 Actor 授予 Ability/Attribute/AbilitySet。

UE5.8 Game Features 专题(七):像 Lyra 一样注入输入、UI 和 GAS 配图
Lyra 的价值不是照抄类名,而是学习它怎样把输入、UI、AbilitySet 做成可插拔玩法。

先分清:Lyra Action 是项目级代码

这些类不在引擎 GameFeatures 插件里,而在 Lyra 项目源码里:

  • UGameFeatureAction_AddInputContextMapping
  • UGameFeatureAction_AddInputBinding
  • UGameFeatureAction_AddWidgets
  • UGameFeatureAction_AddAbilities
  • UGameFeatureAction_WorldActionBase

你的项目可以直接学习结构,但最好写自己的版本,适配自己的 PlayerController、HUD、ASC 和输入系统。

WorldActionBase 思路

Lyra 抽了一个 GameFeatureAction_WorldActionBase:激活时遍历已有 WorldContext,并监听 GameInstanceStart;停用时解绑。你写“和世界有关”的 Action,都可以沿用这个思路。

void UMyGameFeatureAction_WorldActionBase::OnGameFeatureActivating(FGameFeatureActivatingContext& Context)
{
    FDelegateHandle Handle = FWorldDelegates::OnStartGameInstance.AddUObject(
        this,
        &ThisClass::HandleGameInstanceStart,
        FGameFeatureStateChangeContext(Context));

    for (const FWorldContext& WorldContext : GEngine->GetWorldContexts())
    {
        if (Context.ShouldApplyToWorldContext(WorldContext))
        {
            AddToWorld(WorldContext, Context);
        }
    }
}

注入 Enhanced Input

输入 Action 一般监听 PlayerController 或 Pawn 的 Extension Event。等 Controller/Pawn ready 后,找到 UEnhancedInputLocalPlayerSubsystem,添加 IMC:

Subsystem->AddMappingContext(InputMappingContext, Priority);
ActiveData.Players.Add(PlayerController, InputMappingContext);

停用时遍历 ActiveData,把 MappingContext 移除。不要只 Add 不 Remove,否则切换玩法包后输入还在。

注入 Common UI

UI Action 推荐让项目有一个统一的 HUD Layout 或 UI Extension Subsystem。Game Feature 只声明“我要把某 Widget 放到哪个槽位”:

字段示例
Slot TagUI.Layer.HUD.WeaponStatus
Widget ClassWBP_RifleAmmoPanel
Target ActorPlayerController/HUD
Visibility PolicyActive 时显示,Deactivate 时移除

停用时必须移除 Widget 或注销扩展点。否则活动结束后 HUD 还挂着入口。

注入 GAS

Lyra 的 AddAbilities 思路是:对某类 Actor 监听 Extension Event,找到 ASC,然后授予 Ability、AttributeSet、AbilitySet,并保存句柄。

struct FActorExtensions
{
    TArray<FGameplayAbilitySpecHandle> Abilities;
    TArray<UAttributeSet*> Attributes;
    TArray<FMyAbilitySet_GrantedHandles> AbilitySetHandles;
};

停用时用保存的 handle 移除 Ability 和 Attribute。不要按类名粗暴 Clear,因为多个玩法包可能授予同类 Ability。

使用案例:Rifle 武器包

Rifle 包激活后做四件事:

  1. Add Components:给 Pawn 加 URifleWeaponComponent
  2. Add Input Context:给本地玩家加 IMC_Rifle,包含开火、换弹、瞄准。
  3. Add Widget:在 HUD 武器槽显示 WBP_RifleAmmoPanel
  4. Add Abilities:授予 GA_RifleFireGA_Reload,并加 AS_RifleRuntime 属性集。

停用顺序建议反过来:先禁输入,再移除 UI,再移除 Ability,最后让组件销毁。这样玩家不会在组件半拆时还能按开火键。

架构分析

Game Feature 不应该知道具体关卡里有几个玩家,也不应该直接访问全局单例找第一个 Controller。更稳的做法是:激活时注册“对某类 Actor 的扩展处理器”,Actor ready 时回调,停用时释放处理器和已授予对象。Lyra 的 Action 大多都有 FPerContextData,里面保存 ActiveData 和 ComponentRequestHandle,这个模式非常值得学。

常见坑

  • 只给当前已存在玩家加输入,新加入本地玩家没有 IMC。
  • 停用时移除所有同类 IMC,误删别的玩法包加的输入。
  • UI Widget 创建后没保存句柄,Deactivate 找不到它。
  • AbilitySet 授予后只按 AbilityClass 移除,误删其它来源授予的 Ability。
  • 把 Lyra 的 Slot Tag、HUD 类型硬搬到自己的项目,编译过了但运行时没有对应系统。

源码依据

Lyra 的 GameFeatureAction_AddInputContextMapping 监听 GameInstance 和 LocalPlayer,给 Enhanced Input LocalPlayerSubsystem 添加/移除 Mapping Context。GameFeatureAction_AddWidget 使用 Actor Extension 和 per-context 数据记录 Widget。GameFeatureAction_AddAbilities 用 ActiveExtensions 保存 Ability、Attribute、AbilitySet handles,并在 Deactivate 重置。

源码路径索引

  • Samples/Games/Lyra/Source/LyraGame/GameFeatures/GameFeatureAction_AddInputContextMapping.h
  • Samples/Games/Lyra/Source/LyraGame/GameFeatures/GameFeatureAction_AddInputBinding.h
  • Samples/Games/Lyra/Source/LyraGame/GameFeatures/GameFeatureAction_AddWidget.h
  • Samples/Games/Lyra/Source/LyraGame/GameFeatures/GameFeatureAction_AddAbilities.h
  • Samples/Games/Lyra/Source/LyraGame/GameFeatures/LyraGameFeaturePolicy.h