UE5 GAS与Enhanced Input深度整合:基于GameplayTag的声明式输入绑定架构
1. 项目概述为什么我们需要深度整合GAS与Enhanced Input在UE5项目开发中尤其是涉及复杂角色技能、状态和交互的多人游戏或动作游戏输入处理与技能系统的耦合常常成为架构的痛点。传统的输入绑定方式比如直接映射到蓝图节点或C函数在技能数量增多、技能存在冷却、消耗、条件触发等复杂逻辑时代码会迅速变得难以维护。你可能会遇到“技能键按下后需要检查蓝量、检查状态、再触发技能”这种分散在各处的逻辑或者为同一个技能键在不同状态下如地面、空中、驾驶载具绑定不同行为而头疼。这就是Gameplay Ability SystemGAS和Enhanced Input系统登场的原因。GAS提供了一套强大的、面向数据的技能与状态管理框架而Enhanced Input则带来了更现代、更灵活的输入处理管道。但将它们简单地“用在一起”和“深度整合”是两回事。深度整合的目标是建立一个声明式、解耦且高效的输入绑定架构即我们不是告诉引擎“按下E键执行某个函数”而是声明“一个带有Ability.Input.Skill1标签的输入动作可以触发一个带有Ability.Skill1标签的技能”。所有条件判断能否释放和具体执行如何释放都交由GAS处理。Lyra作为Epic官方发布的UE5示例项目正是这一整合理念的绝佳实践。它没有采用土法炼钢而是构建了一个基于GameplayTag的桥梁优雅地连接了Enhanced Input的输入事件与GAS的技能激活。本文将深入拆解Lyra的实现方案还原其设计思路并提供一套可直接在你项目中复用的高效输入绑定框架。无论你是正在为项目输入架构挠头的开发者还是希望学习UE5前沿架构的设计思路这篇文章都将提供从理论到实践的完整路径。2. 核心架构解析Lyra如何用GameplayTag桥接两大系统Lyra的输入绑定架构核心在于一个关键设计使用GameplayTag作为输入动作与游戏技能之间的通用语言和连接纽带。这避免了输入系统与技能系统之间的硬编码依赖实现了彻底的解耦。2.1 传统方式 vs Lyra方式为了理解其优越性我们先看一个常见的传统实现方式传统方式硬编码耦合在角色或玩家控制器中绑定输入事件如InputAction Jump。在事件回调中编写一长串逻辑检查角色是否在地面、检查体力是否足够、调用跳跃函数、播放动画、扣除体力等。如果跳跃技能升级为二段跳需要修改这里的回调逻辑增加状态判断。另一个技能也需要检测按键时可能又得复制一份类似的检测逻辑。这种方式将输入检测、资源判断、技能执行逻辑全部耦合在一处违反了单一职责原则难以复用和扩展。Lyra方式基于标签的声明式绑定定义阶段创建一个Enhanced Input的InputAction例如命名为IA_Jump。为其添加一个InputTag值为InputTag.Jump这是一个GameplayTag。配置阶段在角色的技能蓝图或ASCAbility System Component上配置一个GameplayAbility例如GA_Jump。在该技能的Activation Owned Tags或Activation Required Tags中添加相同的InputTag.Jump。连接阶段通过一个专门的系统如Lyra中的LyraInputComponent或AbilitySystemGlobals的配置建立规则“当检测到标签为InputTag.X的输入被触发时尝试激活所有需要InputTag.X标签的技能”。执行阶段玩家按下跳跃键 - Enhanced Input系统触发IA_Jump动作 - 系统读取其InputTag.Jump- 将此标签传递给关联角色的ASC - ASC查找所有可以被InputTag.Jump激活的技能即GA_Jump- GAS内部按顺序执行该技能的CanActivateAbility检查地面、体力等和ActivateAbility执行跳跃逻辑。可以看到输入系统只负责识别动作并抛出标签它完全不知道“跳跃”具体要做什么。技能系统只接收一个标签请求并执行与之匹配的技能它也不关心这个请求来自键盘、手柄还是触摸屏。两者通过GameplayTag这个轻量的、可读的字符串进行通信实现了完美的关注点分离。2.2 核心组件拆解Lyra为了实现这套架构主要依赖以下几个核心组件UGameplayAbility技能的基类。其Activation Owned Tags属性是关键这里添加的标签意味着“这个技能可以被哪个输入标签激活”。例如GA_Jump拥有InputTag.JumpGA_Attack拥有InputTag.Attack。UInputAction(Enhanced Input)输入动作资产。其Tags属性可以添加一个或多个FGameplayTag。这就是输入动作的“身份标识”。通常一个InputAction只绑定一个主要的输入标签。ULyraInputConfig(Lyra特有)这是一个数据资产DataAsset用于集中管理输入绑定。它内部定义了一个结构体数组将InputAction与InputTagGameplayTag显式地关联起来。这相当于一个“输入动作-标签”的映射表。ULyraInputComponent(Lyra特有)继承自UEnhancedInputComponent的增强组件。它被添加到玩家控制的Pawn或Character上。它的核心职责是从ULyraInputConfig中读取绑定信息。使用Enhanced Input的APIBindAction将每个InputAction绑定到自己的回调函数。在回调函数中不是直接执行业务逻辑而是将对应的InputTag提取出来并调用一个通用的技能激活接口。AbilitySystemComponent(ASC)GAS的核心组件管理所有GameplayAbility和GameplayEffect。它需要暴露一个接口通常通过IAbilitySystemInterface供输入组件调用。接口函数类似于TryActivateAbilitiesByTag(FGameplayTagContainer InputTag)。这个数据流可以概括为InputAction(标签) -LyraInputConfig(映射) -LyraInputComponent(绑定与转发) -ASC(查找并激活技能)。注意这里的关键是“转发”。LyraInputComponent的回调函数极其简单它几乎不做任何游戏逻辑判断仅仅是一个“邮差”把输入标签“送”给ASC。所有条件判断CanActivate都在GAS的技能内部完成这保证了逻辑的一致性。3. 从零实现构建你自己的GAS与Enhanced Input整合框架理解了Lyra的架构思想后我们完全可以脱离Lyra项目在自己的项目中实现一套类似的、甚至更定制化的系统。下面将分步骤详细说明。3.1 第一步项目基础设置与GAS引入首先确保你的UE5项目已启用必要的插件Gameplay Abilities Plugin这是GAS的核心。Enhanced Input PluginUE5推荐的输入系统。在C项目中需要在YourProject.Build.cs文件中添加对应的模块依赖PublicDependencyModuleNames.AddRange(new string[] { Core, CoreUObject, Engine, InputCore, EnhancedInput, // 添加EnhancedInput模块 GameplayAbilities, // 添加GameplayAbilities模块 GameplayTags, // 通常GAS需要GameplayTags模块 GameplayTasks // GAS通常需要GameplayTasks模块 });接下来创建你的基础角色类。这个角色需要拥有AbilitySystemComponent。通常我们会创建一个基类例如ABaseCharacter// ABaseCharacter.h #include AbilitySystemInterface.h #include AbilitySystemComponent.h UCLASS() class YOURPROJECT_API ABaseCharacter : public ACharacter, public IAbilitySystemInterface { GENERATED_BODY() public: ABaseCharacter(); // 实现 IAbilitySystemInterface 接口 virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override; // 声明ASC UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category GAS) TObjectPtrUAbilitySystemComponent AbilitySystemComponent; protected: virtual void BeginPlay() override; }; // ABaseCharacter.cpp ABaseCharacter::ABaseCharacter() { // 创建并初始化ASC组件 AbilitySystemComponent CreateDefaultSubobjectUAbilitySystemComponent(TEXT(AbilitySystemComponent)); // 通常ASC需要设置为Replicated用于网络同步 AbilitySystemComponent-SetIsReplicated(true); // REPLICATED模式表示只在拥有此角色的客户端和服务器上同步 AbilitySystemComponent-ReplicationMode EGameplayEffectReplicationMode::Mixed; } UAbilitySystemComponent* ABaseCharacter::GetAbilitySystemComponent() const { return AbilitySystemComponent; } void ABaseCharacter::BeginPlay() { Super::BeginPlay(); // 确保ASC在BeginPlay时被初始化这对于赋予初始技能很重要 if (AbilitySystemComponent) { AbilitySystemComponent-InitAbilityActorInfo(this, this); } }3.2 第二步创建输入标签与映射数据资产我们需要一个地方来集中定义“哪个输入动作对应哪个标签”。仿照Lyra我们创建自己的输入配置数据资产。首先创建一个结构体来描述单个绑定关系// InputConfig.h #include GameplayTagContainer.h #include Engine/DataAsset.h #include InputAction.h #include InputConfig.generated.h USTRUCT(BlueprintType) struct FTaggedInputAction { GENERATED_BODY() public: // 对应的输入动作资产 UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) TObjectPtrconst UInputAction InputAction nullptr; // 该输入动作关联的GameplayTag UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Meta (Categories InputTag)) FGameplayTag InputTag; }; // 输入配置数据资产 UCLASS(BlueprintType, Const) class YOURPROJECT_API UInputConfig : public UDataAsset { GENERATED_BODY() public: // 通过输入标签查找对应的输入动作在输入组件绑定时使用 const UInputAction* FindInputActionForTag(const FGameplayTag InputTag) const; // 绑定的列表 UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Meta (TitleProperty InputAction)) TArrayFTaggedInputAction TaggedInputActions; };// InputConfig.cpp const UInputAction* UInputConfig::FindInputActionForTag(const FGameplayTag InputTag) const { for (const FTaggedInputAction Action : TaggedInputActions) { if (Action.InputTag InputTag) { return Action.InputAction; } } return nullptr; }在编辑器中你可以创建一个InputConfig数据资产例如DA_DefaultInput然后在其中以表格形式添加映射IA_Jump映射到InputTag.JumpIA_Attack映射到InputTag.Attack。实操心得建议为输入标签建立独立的子标签树如InputTag.JumpInputTag.Attack.PrimaryInputTag.Attack.SecondaryInputTag.Interact。这有利于通过标签匹配如InputTag.Attack匹配所有攻击实现更灵活的输入绑定例如一个“攻击”键可以触发所有带有InputTag.Attack父标签的技能。3.3 第三步实现增强输入组件这是连接Enhanced Input和GAS的桥梁。我们需要创建一个自定义的输入组件其职责是加载UInputConfig并完成绑定。// EnhancedInputComponentWithGAS.h #include Components/PawnComponent.h #include EnhancedInputComponentWithGAS.generated.h class UInputConfig; struct FGameplayTag; UCLASS(ClassGroupInput, meta(BlueprintSpawnableComponent)) class YOURPROJECT_API UEnhancedInputComponentWithGAS : public UPawnComponent { GENERATED_BODY() public: // 绑定输入配置 UFUNCTION(BlueprintCallable, Category Input) void BindInputActions(const UInputConfig* InputConfig); // 绑定单个输入动作的通用回调 templateclass UserClass, typename FuncType void BindInputAction(const UInputConfig* InputConfig, const FGameplayTag InputTag, ETriggerEvent TriggerEvent, UserClass* Object, FuncType Func); protected: // 当输入动作触发时的通用处理函数 void OnInputActionTriggered(const FInputActionInstance Instance, FGameplayTag InputTag); private: // 缓存的输入配置 UPROPERTY(Transient) TObjectPtrconst UInputConfig CurrentInputConfig; };// EnhancedInputComponentWithGAS.cpp #include EnhancedInputComponentWithGAS.h #include InputConfig.h #include GameFramework/Pawn.h #include AbilitySystemGlobals.h #include AbilitySystemComponent.h void UEnhancedInputComponentWithGAS::BindInputActions(const UInputConfig* InputConfig) { if (!InputConfig || !GetPawnAPawn() || !GetOwner()-InputEnabled()) { return; } CurrentInputConfig InputConfig; // 清空现有绑定可选取决于你的逻辑 if (UEnhancedInputComponent* EnhancedInputComponent CastUEnhancedInputComponent(GetPawn()-InputComponent)) { EnhancedInputComponent-ClearBindings(); } // 为配置中的每个动作进行绑定 for (const FTaggedInputAction Action : InputConfig-TaggedInputActions) { if (Action.InputAction Action.InputTag.IsValid()) { // 使用模板函数进行绑定将InputTag作为参数捕获 BindInputAction(InputConfig, Action.InputTag, ETriggerEvent::Triggered, this, UEnhancedInputComponentWithGAS::OnInputActionTriggered); // 通常也需要绑定Started, Completed, Canceled事件取决于技能需求 BindInputAction(InputConfig, Action.InputTag, ETriggerEvent::Started, this, UEnhancedInputComponentWithGAS::OnInputActionTriggered); BindInputAction(InputConfig, Action.InputTag, ETriggerEvent::Completed, this, UEnhancedInputComponentWithGAS::OnInputActionTriggered); } } } templateclass UserClass, typename FuncType void UEnhancedInputComponentWithGAS::BindInputAction(const UInputConfig* InputConfig, const FGameplayTag InputTag, ETriggerEvent TriggerEvent, UserClass* Object, FuncType Func) { if (const UInputAction* InputAction InputConfig-FindInputActionForTag(InputTag)) { if (UEnhancedInputComponent* EnhancedInputComponent CastUEnhancedInputComponent(GetPawn()-InputComponent)) { // 使用Lambda表达式将InputTag捕获并传递给回调函数 EnhancedInputComponent-BindAction(InputAction, TriggerEvent, Object, [this, InputTag, Func](const FInputActionInstance Instance) { (this-*Func)(Instance, InputTag); } ); } } } void UEnhancedInputComponentWithGAS::OnInputActionTriggered(const FInputActionInstance Instance, FGameplayTag InputTag) { // 这是核心转发逻辑 APawn* OwningPawn GetPawnAPawn(); if (!OwningPawn) { return; } // 通过AbilitySystemGlobals获取ASC这是一种通用方式 if (UAbilitySystemComponent* ASC UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(OwningPawn)) { // 将输入标签传递给ASC尝试激活对应的技能 // 注意这里需要根据输入事件类型Started, Triggered, Completed做更精细的控制 // Lyra中可能会将Started映射为“按下”Completed映射为“松开”用于不同的技能激活策略 FGameplayTagContainer TagContainer; TagContainer.AddTag(InputTag); // 例如对于Triggered事件通常代表按住我们尝试持续激活 if (Instance.GetTriggerEvent() ETriggerEvent::Triggered) { // 处理持续输入例如蓄力技能 // ASC-AbilityLocalInputPressed(...); 对于按下 // ASC-AbilityLocalInputReleased(...); 对于松开 // 这里简单调用TryActivateAbilitiesByTag ASC-TryActivateAbilitiesByTag(TagContainer); } else if (Instance.GetTriggerEvent() ETriggerEvent::Started) { // 输入按下时通常调用 PressInputID (如果使用InputID) 或直接激活 // 更常见的做法是使用 InputID 与技能绑定这里我们用标签模拟 ASC-PressInputID(InputTag.GetTagIndex()); // 注意这需要技能配置相应的InputID更通用的做法是下面一种 // 或者直接激活适用于瞬时技能 ASC-TryActivateAbilitiesByTag(TagContainer); } else if (Instance.GetTriggerEvent() ETriggerEvent::Completed) { // 输入松开时调用 ReleaseInputID ASC-ReleaseInputID(InputTag.GetTagIndex()); } } }注意事项上述代码中的PressInputID和ReleaseInputID是GAS中另一种绑定输入的方式通过数字ID。为了与标签系统统一Lyra和更优雅的做法是重写ASC的AbilityLocalInputPressed和AbilityLocalInputReleased函数使其响应GameplayTag。或者更简单地我们可以直接使用TryActivateAbilitiesByTag并在技能蓝图的CanActivateAbility函数中根据输入事件类型通过GetCurrentActivationInfo获取来做更精细的控制。例如一个“蓄力射击”技能可能在Started时开始蓄力激活技能但不发射在Completed时执行发射。3.4 第四步在角色中集成并初始化最后一步是在你的玩家角色或Pawn中集成上述组件。在角色蓝图或C类中添加UEnhancedInputComponentWithGAS组件。在角色初始化如BeginPlay或SetupPlayerInputComponent时调用绑定函数。// 在你的角色类中 void AYourCharacter::BeginPlay() { Super::BeginPlay(); // 初始化ASC if (AbilitySystemComponent) { AbilitySystemComponent-InitAbilityActorInfo(this, this); // 这里可以授予初始技能 // GrantAbilitiesOnStart(); } // 初始化输入 SetupGASInput(); } void AYourCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent); // 确保使用的是EnhancedInputComponent if (UEnhancedInputComponent* EnhancedInputComponent CastUEnhancedInputComponent(PlayerInputComponent)) { // 你的其他输入绑定... } } void AYourCharacter::SetupGASInput() { if (UEnhancedInputComponentWithGAS* GASInputComponent FindComponentByClassUEnhancedInputComponentWithGAS()) { // 加载你的输入配置数据资产 if (UInputConfig* InputConfig LoadObjectUInputConfig(nullptr, TEXT(/Game/Path/To/Your/DA_DefaultInput.DA_DefaultInput))) { GASInputComponent-BindInputActions(InputConfig); } } }配置技能创建你的GameplayAbility蓝图例如BP_GA_Jump。在其细节面板中找到Ability Tags或Activation相关部分添加Activation Owned Tags包含InputTag.Jump。这样当输入组件转发InputTag.Jump标签时这个技能就会被尝试激活。4. 高级技巧与实战优化基础框架搭建完成后我们可以探讨一些进阶用法和优化点让你的输入绑定系统更加强大和灵活。4.1 处理输入优先级与阻塞在复杂游戏中经常需要处理输入优先级。例如当角色处于“硬直”状态时所有攻击输入应该被忽略或者“打开背包”的菜单输入应该优先于角色移动输入。利用GAS的GameplayTag和GameplayEffect可以优雅地实现这一点。使用Block Abilities Tag在ASC上可以设置BlockAbilitiesWithTag。你可以创建一个GameplayEffect当角色处于硬直状态时持续施加这个GE并为它添加一个标签例如State.HardStun。在ASC的BlockAbilitiesWithTag列表中添加InputTag.Attack。这样当角色拥有State.HardStun标签时所有需要InputTag.Attack标签的技能都无法被激活。在CanActivateAbility中检查在每个技能的CanActivateAbility事件中你可以检查角色当前是否拥有某些“禁止标签”。例如检查AbilitySystemComponent-HasMatchingGameplayTag(FGameplayTag::RequestGameplayTag(“State.Disabled”))如果为真则返回false。4.2 实现组合键与输入上下文Input ContextEnhanced Input原生支持Input Modifiers和Input Triggers可以轻松实现组合键如CtrlClick、长按、双击等。我们的标签系统可以很好地与之结合。为复杂输入创建专用InputAction例如创建一个IA_StrongAttack为其添加Hold触发器长按1秒和[Shift]修饰键。这个动作的标签设为InputTag.Attack.Strong。在技能中响应创建一个GA_StrongAttack技能其激活标签为InputTag.Attack.Strong。当玩家长按Shift攻击键时Enhanced Input识别为IA_StrongAttack动作触发转发InputTag.Attack.Strong标签从而激活重击技能。技能内部无需再判断按键组合和长按时间。4.3 网络同步考量对于多人游戏输入和技能的同步至关重要。输入事件本身不需要同步输入检测在本地客户端进行。EnhancedInputComponentWithGAS组件只在本地客户端存在并运行。技能激活需要RPC当输入组件调用ASC-TryActivateAbilitiesByTag()时如果该技能是NetExecutionPolicy为LocalPredicted、ServerInitiated或ServerOnlyGAS内部会自动处理网络RPC远程过程调用。对于LocalPredicted本地预测技能客户端会立即本地执行并播放效果同时向服务器发送激活请求服务器验证后执行并同步结果。我们的输入绑定框架对此是透明的你只需要在创建技能时正确设置其网络执行策略。确保ASC已正确初始化并设置Owner和Avatar在BeginPlay中调用AbilitySystemComponent-InitAbilityActorInfo(this, this);是关键步骤它设置了ASC的所有者Owner和化身Avatar这对于技能的目标定位和网络复制至关重要。4.4 性能优化与调试使用数据资产DataAsset将输入配置放在数据资产中便于策划或设计师在编辑器中调整无需重新编译C代码。标签匹配优化GameplayTag的匹配是高效的但避免在每帧进行大量的标签容器遍历。我们的设计只在输入事件触发时进行一次查找和匹配。调试显示在开发阶段可以在HUD上显示当前激活的输入标签和正在运行的技能列表便于调试。GAS提供了AbilitySystemComponent-GetActiveAbilities()等函数来获取信息。使用Gameplay Debugger启用GameplayAbilities插件自带的Gameplay Debugger按“撇号”键可以实时查看任何角色的ASC状态、激活的技能、拥有的标签和效果是调试GAS相关问题不可或缺的工具。5. 常见问题排查与解决方案实录在实际整合过程中你可能会遇到一些典型问题。以下是我在多个项目中踩过坑后总结的排查清单。5.1 问题输入触发后技能毫无反应。检查点1输入绑定是否成功在UEnhancedInputComponentWithGAS::BindInputActions中打日志确认循环到了你的TaggedInputActions。在OnInputActionTriggered函数开头打日志确认当按下按键时该函数被调用且InputTag正确。检查点2ASC是否有效在OnInputActionTriggered中检查UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(OwningPawn)返回的指针是否非空。确认你的角色类正确实现了IAbilitySystemInterface并且GetAbilitySystemComponent返回了有效的AbilitySystemComponent。在角色BeginPlay中确认调用了AbilitySystemComponent-InitAbilityActorInfo(this, this);。检查点3技能是否被授予Granted技能必须通过GiveAbility或GiveAbilityWithSourceObject授予给ASC后才能被激活。检查你授予技能的代码是否执行。可以在授予后打印ASC拥有的技能列表。检查点4技能的激活标签匹配吗确认技能蓝图或C类中Activation Owned Tags包含的标签与输入配置中InputAction绑定的InputTag完全一致。注意标签的大小写和层级InputTag.Jump和InputTag.jump是不同的。检查点5技能的CanActivateAbility是否返回false在技能蓝图的CanActivateAbility事件中添加一个简单的Print String节点确保它被执行并返回true。可能技能内部有冷却、资源、条件等检查阻止了激活。5.2 问题技能被激活但表现不正常如动画不播放、效果不应用。检查点1技能的ActivateAbility逻辑是否正确确保在ActivateAbility中正确调用了执行技能的蓝图节点或C代码如播放蒙太奇、应用GameplayEffect、发射投射物等。对于网络游戏确保在服务器端执行的逻辑如造成伤害被正确复制或通过RPC调用。检查点2技能的成本Cost和冷却CooldownGE是否生效检查为技能配置的GameplayEffectCost和Cooldown是否被正确创建和应用。使用Gameplay Debugger查看角色身上的GE列表。确认Cost GE的Modifiers正确修改了属性如魔法值并且Application Requirement应用要求被满足。检查点3技能的动画蒙太奇是否在角色的骨骼网格体上播放确认Play Montage节点选择的Anim Montage资产有效并且目标骨骼网格体组件是正确的。5.3 问题输入在特定状态下如UI打开时不应触发但却触发了。解决方案在输入组件中增加输入模式检查。在UEnhancedInputComponentWithGAS::OnInputActionTriggered函数开头添加对当前输入模式的判断。例如可以从Player Controller或一个自定义的Game Instance中获取当前是否处于UI模式。APlayerController* PC GetControllerAPlayerController(); if (PC PC-IsInputKeyDown(EKeys::LeftMouseButton)) // 或者你自己的UI模式标志 { // 如果处于UI交互模式则忽略游戏角色输入 return; }更优雅的方式是使用Enhanced Input的Input Mapping Context的优先级。为UI操作创建一个高优先级的Input Mapping Context当UI打开时将其添加给玩家UI关闭时移除。高优先级的上下文会覆盖低优先级的绑定。5.4 问题如何实现“按住连续触发”或“蓄力”技能方案利用Enhanced Input的Triggered事件和GAS的WaitInputPress/WaitInputRelease节点。连续触发在输入配置中为动作绑定ETriggerEvent::Triggered事件。在技能的ActivateAbility中启动一个循环任务如WaitDelay回调每次循环执行一次攻击逻辑。直到接收到输入释放事件Completed或技能被取消才结束循环。蓄力技能绑定ETriggerEvent::Started和ETriggerEvent::Completed。在Started事件触发时激活技能GA_Charging。该技能在激活后开始累积一个本地变量蓄力时间并播放蓄力动画。在Completed事件触发时输入组件会调用ReleaseInputID或转发释放标签。在GA_Charging技能中你需要监听输入释放可以使用WaitInputRelease节点当释放时根据蓄力时间计算出最终效果如伤害倍率然后执行发射逻辑最后结束技能EndAbility。也可以在Completed事件中尝试激活另一个专门处理释放的技能GA_ChargeRelease并将蓄力时间通过参数传递过去。整合GAS与Enhanced Input绝非一蹴而就它要求你对两个系统都有较深的理解。但一旦这套管道搭建完成你将获得一个清晰、强大、易于扩展的输入与技能管理框架能够轻松应对未来复杂的游戏功能需求。从Lyra中学习其设计思想再根据自己项目的实际情况进行裁剪和强化这才是高效的学习与开发之道。

相关新闻

DXVK终极指南:在Linux上流畅运行Windows游戏的完整教程

DXVK终极指南:在Linux上流畅运行Windows游戏的完整教程

DXVK终极指南:在Linux上流畅运行Windows游戏的完整教程 【免费下载链接】dxvk Vulkan-based implementation of D3D8, 9, 10 and 11 for Linux / Wine 项目地址: https://gitcode.com/gh_mirrors/dx/dxvk DXVK(Direct3D to Vulkan转换层&#xff…

2026/8/5 10:53:51 阅读更多 →
职场英语学习计划Day031

职场英语学习计划Day031

🌟计划1:📅学习时间:2026.7.29 周三内容:视频30主题:English at Work Episode 30: Two heads are better than one Language for negotiating▶ ring back 回电,回电话ring——过去式rang——过…

2026/8/5 10:53:50 阅读更多 →
Qt开发进阶:攻克图形视图、多线程与部署三大核心难点

Qt开发进阶:攻克图形视图、多线程与部署三大核心难点

在实际 C 图形界面开发中,Qt 框架以其跨平台特性和丰富的组件库成为许多桌面应用的首选。然而,从学习到精通,再到应对复杂的生产级项目,开发者会经历一个明显的难度曲线。这个曲线并非平滑上升,而是在某些关键节点上会…

2026/8/5 10:52:50 阅读更多 →

最新新闻

5分钟快速恢复QQ空间历史记录:GetQzonehistory终极指南

5分钟快速恢复QQ空间历史记录:GetQzonehistory终极指南

5分钟快速恢复QQ空间历史记录:GetQzonehistory终极指南 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否担心QQ空间里那些承载青春记忆的说说会随着时间流逝而消失&…

2026/8/5 11:44:16 阅读更多 →
终极泰拉瑞亚地图编辑器:10分钟从新手变造物主

终极泰拉瑞亚地图编辑器:10分钟从新手变造物主

终极泰拉瑞亚地图编辑器:10分钟从新手变造物主 【免费下载链接】Terraria-Map-Editor TEdit - Terraria Map Editor - TEdit is a stand alone, open source map editor for Terraria. It lets you edit maps just like (almost) paint! It also lets you change wo…

2026/8/5 11:44:16 阅读更多 →
Figma中文界面汉化终极指南:3分钟实现专业设计工具本地化

Figma中文界面汉化终极指南:3分钟实现专业设计工具本地化

Figma中文界面汉化终极指南:3分钟实现专业设计工具本地化 【免费下载链接】figmaCN 中文 Figma 插件,设计师人工翻译校验 项目地址: https://gitcode.com/gh_mirrors/fi/figmaCN 你是否在使用Figma时被英文界面困扰?专业术语难懂、菜单…

2026/8/5 11:44:16 阅读更多 →
从功能实现到工程化落地:异步任务与消息队列的实战应用

从功能实现到工程化落地:异步任务与消息队列的实战应用

1. 一个真实的项目复盘:从“功能跑通”到“工程落地”的鸿沟最近和几个刚入行的朋友聊天,发现一个挺普遍的现象:很多人,包括我自己刚工作时,都容易陷入一个误区——把“功能实现”等同于“项目成功”。比如&#xff0c…

2026/8/5 11:44:16 阅读更多 →
如何快速解决常见问题:RVC语音转换WebUI的7个终极技巧

如何快速解决常见问题:RVC语音转换WebUI的7个终极技巧

如何快速解决常见问题&#xff1a;RVC语音转换WebUI的7个终极技巧 【免费下载链接】Retrieval-based-Voice-Conversion-WebUI Easily train a good VC model with voice data < 10 mins! 项目地址: https://gitcode.com/GitHub_Trending/re/Retrieval-based-Voice-Convers…

2026/8/5 11:44:16 阅读更多 →
CUDA编程入门:从.cu文件结构到并行计算实战

CUDA编程入门:从.cu文件结构到并行计算实战

1. 从“Hello, World!”到并行计算&#xff1a;CUDA编程的初体验如果你是一名C/C开发者&#xff0c;第一次接触CUDA编程&#xff0c;打开一个.cu文件时&#xff0c;可能会觉得既熟悉又陌生。它看起来就像普通的C代码&#xff0c;但里面多了些奇怪的修饰符&#xff0c;比如__glo…

2026/8/5 11:43:15 阅读更多 →

日新闻

Java缓存框架:JetCache

Java缓存框架:JetCache

TOC 一、简介 JetCache 是一个 Java 缓存抽象框架&#xff0c;为不同的缓存解决方案提供了统一的使用方式。 它提供的注解比 Spring Cache 更加强大。 JetCache 的注解支持原生 TTL、两级缓存以及在分布式环境中的自动刷新功能&#xff0c;同时你也可以通过代码直接操作 Cach…

2026/8/5 0:00:43 阅读更多 →
AD 铺铜设置十字连接,过孔全连接,新版AD的简单设置

AD 铺铜设置十字连接,过孔全连接,新版AD的简单设置

需求&#xff1a;通孔焊盘 十字花&#xff1b;过孔 Via 实心直连&#xff1b;贴片焊盘按需设置 AD 测试版本AD24 很多工程师踩坑&#xff1a;全部统一十字&#xff0c;导致接地过孔阻抗高、大电流发热&#xff01; 一、快捷键打开规则 PCB 界面按下&#xff1a;D R 展开…

2026/8/5 0:00:43 阅读更多 →
AI素描转换技术深度拆解(2024最新论文+工业级落地代码):从Stable Diffusion ControlNet到LoRA微调全链路解析

AI素描转换技术深度拆解(2024最新论文+工业级落地代码):从Stable Diffusion ControlNet到LoRA微调全链路解析

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;AI生成素描效果 AI生成素描效果是计算机视觉与风格迁移技术融合的典型应用&#xff0c;其核心在于将彩色照片或RGB图像转换为具有手绘质感、明暗对比强烈、边缘清晰的单色素描图像。该过程通常依赖于深度学习模…

2026/8/5 0:00:43 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流&#xff1a;一个核心问题的诞生想象一下&#xff0c;你是一个城市供水系统的总工程师。你的城市有多个水源&#xff08;水库&#xff09;&#xff0c;需要通过一个复杂的地下管道网络&#xff0c;将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/4 13:24:41 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/4 11:41:39 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起&#xff1a;为什么我们需要互相关几年前&#xff0c;我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号&#xff0c;理论上它们接收到的声音波形应该非常相似&#xff0c;只是由于麦克风位置不同&#xff…

2026/8/5 10:20:36 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速&#xff1a;macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/4 13:38:24 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南&#xff1a;3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗&#xff1f;ncmdump解密工具帮你轻松解决这个困…

2026/8/4 11:09:16 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片&#xff1a;为英语学习 App 打造桌面级学习助手适用平台&#xff1a;HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0&#xff08;API 26 Beta&#xff09;新增了 AgentCard 智能体卡片能力&#xff0c;这是继 HMAF&#xff08;鸿蒙智能体框架&#x…

2026/8/4 13:38:40 阅读更多 →