前言
UE5 GAS能够直接支持引擎常规输入, 在GA中能触发Wait Input Press、Wait Input Release等Task, 从而制作一些蓄力能力。
问题:项目使用的增强输入插件来完成更多的输入控制,但GAS本身对增强输入插件没有做支持,官方文档也没有做明确的描述,如何将GAS接入增强输入?
接入常规输入(参考官网文档4.6.2)
常规输入设置
接入步骤
- 声明InputID枚举
UENUM(BlueprintType)
enum class EUpCoAbilityInputID : uint8
{
// 0 None
None UMETA(DisplayName = "None"),
Confirm UMETA(DisplayName = "Confirm"),
Cancel UMETA(DisplayName = "Cancel"),
NormalAttack UMETA(DisplayName = "NormalAttack"),
};
- 在GameplayAbility的子类中声明两个变量,AbilityInputID与AbilityID,用于此GA对应的Input枚举,并在GA中设置对应的枚举
UCLASS()
class UPGRADECOLLECTOR_API UUpCoGameplayAbility : public UGameplayAbility
{
GENERATED_BODY()
public:
// Abilities with this set will automatically activate when the input is pressed
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Ability")
EUpCoAbilityInputID AbilityInputID = EUpCoAbilityInputID::None;
// Value to associate an ability with an slot without tying it to an automatically activated input.
// Passive abilities won't be tied to an input so we need a way to generically associate abilities with slots.
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Ability")
EUpCoAbilityInputID AbilityID = EUpCoAbilityInputID::None;
};
- 在GiveAbility的时候,将GA与InputID Bind
void AUpCoCharacterBase::AddCharacterAbilities()
{
// Grant abilities, but only on the server
if (GetLocalRole() != ROLE_Authority || !AbilitySystemComponent.IsValid() || AbilitySystemComponent->
bCharacterAbilitiesGiven)
{
return;
}
for (TSubclassOf<UUpCoGameplayAbility>& StartupAbility : CharacterAbilities)
{
AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(StartupAbility,1, static_cast<int32>(StartupAbility.GetDefaultObject()->AbilityInputID),this));
}
AbilitySystemComponent->bCharacterAbilitiesGiven = true;
}
- 在Character SetupPlayerInputComponent中调用AbilitySystemComponent的BindAbilityActivationToInputComponent方法完成Bind
void AUpCoCharacterBase::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
BindASCInput();
}
void AUpCoCharacterBase::BindASCInput()
{
AbilitySystemComponent->BindAbilityActivationToInputComponent(InputComponent, FGameplayAbilityInputBinds(
FString("ConfirmTarget"), // 对应着 ProjectSettings里面的Action名称
FString("CancelTarget"), // 对应着 ProjectSettings里面的Action名称
FTopLevelAssetPath("/Script/UpgradeCollector", TEXT("EUpCoAbilityInputID")), // 对应着枚举名称
static_cast<int32>(EUpCoAbilityInputID::Confirm), // Confirm
static_cast<int32>(EUpCoAbilityInputID::Cancel) // Cancel
)); ));
}
- 测试
增强输入
增强输入设置
接入思路
分析常规输入的接入bind,关键是AbilitySystemComponent中的BindAbilityActivationToInputComponent方法
源码
void UAbilitySystemComponent::BindAbilityActivationToInputComponent(UInputComponent* InputComponent, FGameplayAbilityInputBinds BindInfo)
{
UEnum* EnumBinds = BindInfo.GetBindEnum();
SetBlockAbilityBindingsArray(BindInfo);
for(int32 idx=0; idx < EnumBinds->NumEnums(); ++idx)
{
const FString FullStr = EnumBinds->GetNameStringByIndex(idx);
// Pressed event
{
FInputActionBinding AB(FName(*FullStr), IE_Pressed);
AB.ActionDelegate.GetDelegateForManualSet().BindUObject(this, &UAbilitySystemComponent::AbilityLocalInputPressed, idx);
InputComponent->AddActionBinding(AB);
}
// Released event
{
FInputActionBinding AB(FName(*FullStr), IE_Released);
AB.ActionDelegate.GetDelegateForManualSet().BindUObject(this, &UAbilitySystemComponent::AbilityLocalInputReleased, idx);
InputComponent->AddActionBinding(AB);
}
}
// Bind Confirm/Cancel. Note: these have to come last!
if (BindInfo.ConfirmTargetCommand.IsEmpty() == false)
{
FInputActionBinding AB(FName(*BindInfo.ConfirmTargetCommand), IE_Pressed);
AB.ActionDelegate.GetDelegateForManualSet().BindUObject(this, &UAbilitySystemComponent::LocalInputConfirm);
InputComponent->AddActionBinding(AB);
}
if (BindInfo.CancelTargetCommand.IsEmpty() == false)
{
FInputActionBinding AB(FName(*BindInfo.CancelTargetCommand), IE_Pressed);
AB.ActionDelegate.GetDelegateForManualSet().BindUObject(this, &UAbilitySystemComponent::LocalInputCancel);
InputComponent->AddActionBinding(AB);
}
if (BindInfo.CancelTargetInputID >= 0)
{
GenericCancelInputID = BindInfo.CancelTargetInputID;
}
if (BindInfo.ConfirmTargetInputID >= 0)
{
GenericConfirmInputID = BindInfo.ConfirmTargetInputID;
}
}
其实发现在常规输入的情况下,InputComponent Bind几个Action,分别对应
- AbilityLocalInputPressed
- AbilityLocalInputReleased
- LocalInputConfirm
- LocalInputCancel
然后通过能力中的枚举ID关联起来了。
于是我们提出新的针对增强输入的解决方案
- 使用EnhancedInputComponent的方法进行bind
// Pressed event
{
EnhancedInputComponent->BindAction(InputAction, ETriggerEvent::Started,this, &UAbilitySystemComponent::AbilityLocalInputPressed, idx);
}
// Released event
{
EnhancedInputComponent->BindAction(InputAction, ETriggerEvent::Completed,this, &UAbilitySystemComponent::AbilityLocalInputReleased, idx);
}
- InputID枚举值映射InputAction
具体接入步骤
- 继承AbilitySystemComponent,在子类中重写BindAbilityActivationToInputComponent方法
.h文件
UCLASS()
class UPGRADECOLLECTOR_API UUpCoAbilitySystemComponent : public UAbilitySystemComponent
{
GENERATED_BODY()
public:
bool bCharacterAbilitiesGiven = false;
bool bStartupEffectsApplied = false;
virtual void BindAbilityActivationToInputComponent(UInputComponent* InputComponent, FGameplayAbilityInputBinds BindInfo) override;
};
.cpp文件
void UUpCoAbilitySystemComponent::BindAbilityActivationToInputComponent(UInputComponent* InputComponent,
FGameplayAbilityInputBinds BindInfo)
{
Super::BindAbilityActivationToInputComponent(InputComponent, BindInfo);
UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(InputComponent);
AUpCoCharacterBase* UpCoCharacter = Cast<AUpCoCharacterBase>(GetAvatarActor());
if(!UpCoCharacter)
{
return;
}
TMap<EUpCoAbilityInputID, UInputAction*> CurrentMap = UpCoCharacter->GetEnumMap();
if(EnhancedInputComponent)
{
UEnum* EnumBinds = BindInfo.GetBindEnum();
SetBlockAbilityBindingsArray(BindInfo);
for(int32 idx=0; idx < EnumBinds->NumEnums(); ++idx)
{
// const FString FullStr = EnumBinds->GetNameStringByIndex(idx)
UInputAction* InputAction = CurrentMap.FindRef(static_cast<EUpCoAbilityInputID>(idx));
if(InputAction)
{
// Pressed event
{
EnhancedInputComponent->BindAction(InputAction, ETriggerEvent::Started,this, &UAbilitySystemComponent::AbilityLocalInputPressed, idx);
}
// Released event
{
EnhancedInputComponent->BindAction(InputAction, ETriggerEvent::Completed,this, &UAbilitySystemComponent::AbilityLocalInputReleased, idx);
}
}
}
}
}
- InputID枚举值映射InputAction,在CharacterBase.h中声明变量,用于映射,并在Character蓝图中设置映射
- 声明变量和Get方法
- 设置映射
- 后续同常规输入接入步骤一致
在Character SetupPlayerInputComponent中调用AbilitySystemComponent的BindAbilityActivationToInputComponent方法完成Bind
总结
GAS是UE5官方提供的一套高度灵活的能力框架,已经被验证于多个在线商业项目使用,但其中还是有不少的细节需要在基础框架上进行完善和封装,也要求开发者对框架源代码有更多的理解,本文针对GAS集成增强输入提供了一种思路。
参考链接
https://github.com/tranek/GASDocumentation#462-binding-input-to-the-asc