BlueRoseNote/03-UnrealEngine/Gameplay/GAS/GAS Ability输入绑定.md

7.6 KiB
Raw Blame History

title, date, excerpt, tags, rating
title date excerpt tags rating
Untitled 2024-01-29 13:23:24

前言

相关函数主要为:

  • AbilitySystemComponent->BindToInputComponent()只绑定名称为AbilityConfirm与AbilityCancel的InputAction。
  • AbilitySystemComponent->BindAbilityActivationToInputComponent()输入InputComponent、FGameplayAbilityInputBinds、FTopLevelAssetPath枚举类路径、枚举ID之后绑定GA与Input。
  • AbilitySystemComponent->SetBlockAbilityBindingsArray()设置Block列表。

GAS

BindAbilityActivationToInputComponent()的绑定方式如果ASC在Character中可以在SetupPlayerInputComponent()中进行绑定:

// Bind to AbilitySystemComponent
FTopLevelAssetPath AbilityEnumAssetPath = FTopLevelAssetPath(FName("/Script/GASDocumentation"), FName("EGDAbilityInputID"));
AbilitySystemComponent->BindAbilityActivationToInputComponent(PlayerInputComponent, FGameplayAbilityInputBinds(FString("ConfirmTarget"),
	FString("CancelTarget"), AbilityEnumAssetPath, static_cast<int32>(EGDAbilityInputID::Confirm), static_cast<int32>(EGDAbilityInputID::Cancel)));

其内部主要是类似这样的方式进行绑定:

// 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);  
}

AbilityLocalInputPressed()与AbilityLocalInputReleased()会执行:

  1. 触发多播委托
  2. TryActivateAbility() 或者 AbilitySpecInputPressed()。

感觉可以自己实现一个BindAbilityActivationToInputComponent()的绑定逻辑ASC自带的逻辑有点2。 GAS使用的是InputComponent->AddActionBinding,而增强输入使用PlayerEnhancedInputComponent->BindAction。本质是一样的。

FInputActionBinding& BindAction( const FName ActionName, const EInputEvent KeyEvent, UserClass* Object, typename FInputActionHandlerSignature::TMethodPtr< UserClass > Func )  
{  
    FInputActionBinding AB( ActionName, KeyEvent );  
    AB.ActionDelegate.BindDelegate(Object, Func);  
    return AddActionBinding(MoveTemp(AB));  
}

绑定输入而不激活能力

如果您不希望GameplayAbilities在按下输入时自动激活,但仍将它们绑定到输入以与 一起使用AbilityTasks,则可以向您的UGameplayAbility子类添加一个新的 bool 变量bActivateOnInput,该变量默认为true并覆盖UAbilitySystemComponent::AbilityLocalInputPressed()

void UGSAbilitySystemComponent::AbilityLocalInputPressed(int32 InputID)
{
	// Consume the input if this InputID is overloaded with GenericConfirm/Cancel and the GenericConfim/Cancel callback is bound
	if (IsGenericConfirmInputBound(InputID))
	{
		LocalInputConfirm();
		return;
	}

	if (IsGenericCancelInputBound(InputID))
	{
		LocalInputCancel();
		return;
	}

	// ---------------------------------------------------------

	ABILITYLIST_SCOPE_LOCK();
	for (FGameplayAbilitySpec& Spec : ActivatableAbilities.Items)
	{
		if (Spec.InputID == InputID)
		{
			if (Spec.Ability)
			{
				Spec.InputPressed = true;
				if (Spec.IsActive())
				{
					if (Spec.Ability->bReplicateInputDirectly && IsOwnerActorAuthoritative() == false)
					{
						ServerSetInputPressed(Spec.Handle);
					}

					AbilitySpecInputPressed(Spec);

					// Invoke the InputPressed event. This is not replicated here. If someone is listening, they may replicate the InputPressed event to the server.
					InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputPressed, Spec.Handle, Spec.ActivationInfo.GetActivationPredictionKey());
				}
				else
				{
					UGSGameplayAbility* GA = Cast<UGSGameplayAbility>(Spec.Ability);
					if (GA && GA->bActivateOnInput)
					{
						// Ability is not active, so try to activate it
						TryActivateAbility(Spec.Handle);
					}
				}
			}
		}
	}
}

暂时需要使用GAS默认的枚举绑定Input的理由

GameplayAbilitySpec.h的InputPressed

UPROPERTY(NotReplicated)  
uint8 InputPressed:1;

该变量用于本地输入判断,可以使用自己的代码代替,但涉及到以下几个函数,工作量比较大。

  • UAbilitySystemComponent
    • AbilityLocalInputReleased
    • AbilitySpecInputPressed
    • InternalServerTryActivateAbility
    • InternalTryActivateAbility
    • TryActivateAbility
    • 间接关系
      • BindAbilityActivationToInputComponent
      • SetBlockAbilityBindingsArray
      • PressInputID
      • ReleaseInputID
  • UAbilityTask_WaitInputPress
  • UAbilityTask_WaitInputRelease

理论上是可以将其修改成增强输入的版本只需要将枚举循环改成InputActionContext循环即可具体逻辑在角色类中

void AGSHeroCharacter::BindASCInput()
{
	if (!bASCInputBound && IsValid(AbilitySystemComponent) && IsValid(InputComponent))
	{
		AbilitySystemComponent->BindAbilityActivationToInputComponent(InputComponent, FGameplayAbilityInputBinds(FString("ConfirmTarget"),
			FString("CancelTarget"), FString("EGSAbilityInputID"), static_cast<int32>(EGSAbilityInputID::Confirm), static_cast<int32>(EGSAbilityInputID::Cancel)));

		bASCInputBound = true;
	}
}

FGameplayAbilityInputBinds(FString InConfirmTargetCommand, FString InCancelTargetCommand, FTopLevelAssetPath InEnumPathName, int32 InConfirmTargetInputID = INDEX_NONE, int32 InCancelTargetInputID = INDEX_NONE)  
    : ConfirmTargetCommand(InConfirmTargetCommand)  
    , CancelTargetCommand(InCancelTargetCommand)  
    , EnumPathName(InEnumPathName)  
    , ConfirmTargetInputID(InConfirmTargetInputID)  
    , CancelTargetInputID(InCancelTargetInputID)  
{  
}

ConfirmTargetCancelTarget需要在DefaultInput.ini中添加对应的配置名字需要对应EGSAbilityInputID为枚举类名称,EGSAbilityInputID::ConfirmEGSAbilityInputID::Cancel为枚举的index需要与GameplayAbilitySpec中的AbilityInputID对应。具体是在GiveAbility中进行参考#GASShooter中给Ability注册按键ID

之后还需要在URPGGameplayAbility中将枚举ERPGAbilityInputID修改成

UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "RPGGameplayAbility|Input")  
ERPGAbilityInputID AbilityInputID = ERPGAbilityInputID::None;

GASShooter中给Ability注册按键ID

void AGSCharacterBase::AddCharacterAbilities()
{
	// Grant abilities, but only on the server	
	if (GetLocalRole() != ROLE_Authority || !IsValid(AbilitySystemComponent) || AbilitySystemComponent->bCharacterAbilitiesGiven)
	{
		return;
	}

	for (TSubclassOf<UGSGameplayAbility>& StartupAbility : CharacterAbilities)
	{
		AbilitySystemComponent->GiveAbility(
			FGameplayAbilitySpec(StartupAbility, GetAbilityLevel(StartupAbility.GetDefaultObject()->AbilityID), static_cast<int32>(StartupAbility.GetDefaultObject()->AbilityInputID), this));
	}

	AbilitySystemComponent->bCharacterAbilitiesGiven = true;
}

SDHGame

李兄是在状态机每个节点的StateBegin()中调用CreateDynamicSkillInputActionMappingContext()进行动态绑定的。FSDHSkillInputActionBinding里包含FEnhancedActionKeyMapping相关数据。

void USDHSMStateIns::OutputActionKeyMapping(TArray<FEnhancedActionKeyMapping>& OutBindings, const TArray<FSDHSkillInputActionBinding>& InputSkillBinding)  
{  
    for (int32 i = 0; i < InputSkillBinding.Num(); i++)  
    {       
	   FEnhancedActionKeyMapping NewMapping;  
       NewMapping.Key = InputSkillBinding[i].Key;  
       NewMapping.Action = InputSkillBinding[i].Action;  
       OutBindings.Add(MoveTemp(NewMapping));  
    }
}