Unreal GAS (22) - GameplayAbility - Mapping Input

wnsduf0000·2025년 12월 1일

Unreal_GAS

목록 보기
23/34
  • Input Config / Aura Input Component / Callbacks for Ability Input / Activating Abilities

    • GameplayAbility를 커스터마이징 가능한 키 입력과 연결해서 사용하기 위한 설계

    • 전체적인 구조는 다음과 같음.
      키 입력을 통해 GameplayAbility를 작동시키되, 키 설정을 바꿀 수 있도록 하기 위한 구조임.

      • AuraGameplayAbility
        - GameplayAbility가 스스로 어떤 키에 바인딩 되었는지 알 수 있어야 하므로, FGameplayTag형의 변수 StartupInputTag를 생성해둠.
        (최초의 키값이 존재해야 하기 때문에 생성하는 것)

            #include "CoreMinimal.h"
            #include "Abilities/GameplayAbility.h"
            #include "AuraGameplayAbility.generated.h"
            
            UCLASS()
            class AURA_API UAuraGameplayAbility : public UGameplayAbility
            {
            	GENERATED_BODY()
            	
            
            public:
            	UPROPERTY(EditDefaultsOnly, Category = "Input")
            	FGameplayTag StartupInputTag;
            };
      • Input Config

        • EnhancedInputComponent와 GameplayAbility를 연결하기 위한 데이터 에셋 및 구조체.
        • FAuraInputAction이라는 이름의 구조체를 생성하여, 이 구조체가 UInputAction 및 FGameplayTag를 멤버 변수로 갖게 함. 변수들은 모두 EditDefaultsOnly로 설정.
          #include "CoreMinimal.h"
          #include "Engine/DataAsset.h"
          #include "GameplayTagContainer.h"
          #include "AuraInputConfig.generated.h"
          
          USTRUCT(BlueprintType)
          struct FAuraInputAction
          {
          	GENERATED_BODY()
          
          	UPROPERTY(EditDefaultsOnly)
          	const class UInputAction* InputAction = nullptr;
          
          	UPROPERTY(EditDefaultsOnly)
          	FGameplayTag InputTag = FGameplayTag::EmptyTag;
          };
          
          UCLASS()
          class AURA_API UAuraInputConfig : public UDataAsset
          {
          	GENERATED_BODY()
          	
          public:
          	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
          	TArray<FAuraInputAction> AbilityInputActions;
          
          	const UInputAction* FindAbilityInputActionForTag(const FGameplayTag& InputTag, bool bLogNotFound) const;
          };
          
          // AuraInputConfig.h
          #include "Input/AuraInputConfig.h"
          #include "InputAction.h"
          
          const UInputAction* UAuraInputConfig::FindAbilityInputActionForTag(const FGameplayTag& InputTag, bool bLogNotFound) const
          {
          	for (auto& AbilityInputAction : AbilityInputActions)
          	{
          		if (AbilityInputAction.InputTag == InputTag)
          		{
          			return AbilityInputAction.InputAction;
          		}
          	}
          
          	if (bLogNotFound)
          	{
          		UE_LOG(LogTemp, Error, TEXT("UAuraInputConfig::FindAbilityInputActionForTag: No input action found for tag %s"), *InputTag.ToString());
          	}
          
          	return nullptr;
          }
          struct FAuraGameplayTags
          {
          
          public:
          	static const FAuraGameplayTags& Get() { return GameplayTags; }
          
          	static void InitializeNativeGameplayTags();
          
          	// ...
          
          	/* Input Tags */
          	FGameplayTag InputTag_LMB;
          	FGameplayTag InputTag_RMB;
          	FGameplayTag InputTag_1;
          	FGameplayTag InputTag_2;
          	FGameplayTag InputTag_3;
          	FGameplayTag InputTag_4;
          	
          
          private:
          	static FAuraGameplayTags GameplayTags;
          
          };
        • UInputAction (입력 액션)을 에디터에서 원하는 만큼 생성한 후, 이를 생성한 UAuraInputConfig 데이터 에셋에 짝을 지을 FGameplayTag와 함께 설정해주면 됨.
          FGameplayTag들은 FAuraGameplayTags에 미리 설정해두어야 함.
          • UAuraInputConfig 데이터 에셋의 FindAbilityInputActionForTag() 함수는 전달받은 FGameplayTag를 통해 자신이 보유한 FAuraInputAction의 배열에서 일치하는 FGameplayTag가 있는지 탐색하여, 일치하는 것이 있다면 해당 FAuraInputAction의 UInputAction을 반환하는 함수임.
        • 생성한 UInputAction은 InputMappingContext에 설정하고, 원하는 대로 키 입력 등을 설정해주면 됨.

        • Aura Input Component
          • EnhancedInputComponent를 상속하는 AuraInputComponent 클래스를 생성.
            AuraInputComponent는 기존의 EnhancedInputComponent의 기능을 그대로 사용하되, 템플릿을 통해 GameplayTag를 통해 GameplayAbility를 사용하기 위한 함수 바인딩을 실시하는 함수 BindAbilityAction()을 추가한 클래스임.

            #include "CoreMinimal.h"
            #include "EnhancedInputComponent.h"
            #include "Input/AuraInputConfig.h"
            #include "AuraInputComponent.generated.h"
            
            
            UCLASS()
            class AURA_API UAuraInputComponent : public UEnhancedInputComponent
            {
            	GENERATED_BODY()
            	
            public:
            	template<class UserClass, typename PressedFuncType, typename ReleasedFuncType, typename HeldFuncType>
            	void BindAbilityAction(const UAuraInputConfig* InputConfig, UserClass* Object, PressedFuncType PressedFunc, ReleasedFuncType ReleasedFunc, HeldFuncType HeldFunc);
            };
            
            // PressedFuncType, ReleasedFuncType, HeldFuncType은 바인딩하는 함수
            template<class UserClass, typename PressedFuncType, typename ReleasedFuncType, typename HeldFuncType>
            inline void UAuraInputComponent::BindAbilityAction(const UAuraInputConfig* InputConfig, UserClass* Object, PressedFuncType PressedFunc, ReleasedFuncType ReleasedFunc, HeldFuncType HeldFunc)
            {
            	check(InputConfig);
            
            	// AuraInputConfig 데이터 에셋에 설정한 FAuraInputAction들을 순회하며
            	// FAuraInputAction의 UInputAction에 입력 함수들(PressedFuncType, ReleasedFuncType, HeldFuncType)을 바인딩함.
            	// BindAction() 함수는 마지막 매개변수로 임의 개수의 바인딩 함수 매개변수를 받으므로, FAuraInputAction의 FGameplayTag를 넘겨줌.
            	// 이를 통해, UInputAction에 매핑된 키가 입력되면, 바인딩 함수에 FGameplayTag를 매개변수로 넘겨 호출되게끔 할 수 있음.
            	for (const FAuraInputAction& Action : InputConfig->AbilityInputActions)
            	{
            		if (Action.InputAction && Action.InputTag.IsValid())
            		{
            			if (PressedFunc)
            			{
            				BindAction(Action.InputAction, ETriggerEvent::Started, Object, PressedFunc, Action.InputTag);
            			}
            
            			if (ReleasedFunc)
            			{
            				BindAction(Action.InputAction, ETriggerEvent::Completed, Object, ReleasedFunc, Action.InputTag);
            			}
            
            			if (HeldFunc)
            			{
            				BindAction(Action.InputAction, ETriggerEvent::Triggered, Object, HeldFunc, Action.InputTag);
            			}
            		}
            	}
            };
            
            // EnhancedInputComponent.h - BindAction
            template<class UserClass, typename... VarTypes>																																					\
            FEnhancedInputActionEventBinding& BindAction(const UInputAction* Action, ETriggerEvent TriggerEvent, UserClass* Object, typename HANDLER_SIG::template TMethodPtr< UserClass, VarTypes... > Func, VarTypes... Vars) \
            {																																											\
            	TUniquePtr<FEnhancedInputActionEventDelegateBinding<HANDLER_SIG>> AB = MakeUnique<FEnhancedInputActionEventDelegateBinding<HANDLER_SIG>>(Action, TriggerEvent);			\
            	AB->Delegate.BindDelegate<UserClass>(Object, Func, Vars...);																														\
            	AB->Delegate.SetShouldFireWithEditorScriptGuard(bShouldFireDelegatesInEditor);																							\
            	return *EnhancedActionEventBindings.Add_GetRef(MoveTemp(AB));																											\
            }
          • AuraInputComponent를 기존의 EnhancedInputComponent 대신 사용해야 하기 때문에, 프로젝트 설정에서 Input Component를 AuraInputComponent로 설정하는 것을 잊어선 안 됨.

        • Callbacks for Ability Input
          • 키 입력을 통해 GameplayAbility가 실제로 작동하도록 함.

            • EnhancedInputComponent에 입력 액션을 바인딩 하는 곳은 AuraPlayerController였으므로, AuraPlayerController에서 바인딩 할 함수를 선언 및 정의하고, 입력 액션들에 바인딩해주어야 함.
            • 현재 플레이어가 사용 가능한 GameplayAbility들은 AbilitySystemComponent가 FGameplayAbilitySpecContainer 변수 내에 보유하고 있음.
              또한, AbilitySystemComponent는 TryActivateAbility() 등, GameplayAbility를 사용하는 함수들 또한 제공하고 있음.
          • 따라서, AuraPlayerController는 AuraAbilitySystemComponent에 대한 참조, AuraInputConfig에 대한 참조 (에디터 내에서 설정 가능하도록)를 지녀야 하며, 입력 액션 바인딩을 통해 AbilitySystemComponent가 입력 액션과 쌍을 이루는 FGameplayTag을 지닌 GameplayAbility를 사용하는 함수를 정의해야 함.

            #include "Player/AuraPlayerController.h"
            #include "Interaction/EnemyInterface.h"
            #include "Input/AuraInputComponent.h"
            #include "EnhancedInputSubsystems.h"
            #include "EnhancedInputComponent.h"
            #include "AbilitySystemBlueprintLibrary.h"
            #include "AbilitySystem/AuraAbilitySystemComponent.h"
            
            // AuraInputComponent에 정의한 InputAction 바인딩용 함수를 호출함.
            void AAuraPlayerController::SetupInputComponent()
            {
            	Super::SetupInputComponent();
            
            	UAuraInputComponent* AuraInputComponent = CastChecked<UAuraInputComponent>(InputComponent);
            	AuraInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AAuraPlayerController::Move);
            	AuraInputComponent->BindAbilityAction(InputConfig, this, &ThisClass::AbilityInputTagPressed, &ThisClass::AbilityInputTagReleased, &ThisClass::AbilityInputTagHeld);
            }
            
            UAuraAbilitySystemComponent* AAuraPlayerController::GetASC()
            {
            	if (AuraAbilitySystemComponent == nullptr)
            	{
            		AuraAbilitySystemComponent = Cast<UAuraAbilitySystemComponent>(UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(GetPawn<APawn>()));
            	}
            
            	return AuraAbilitySystemComponent;
            }
            
            // InputAction 바인딩용 함수들
            // AuraAbilitySystemComponent의 GameplayAbility를 탐색 및 작동시키는 함수로 연결하는 역할
            void AAuraPlayerController::AbilityInputTagPressed(FGameplayTag InputTag)
            {
            	// GEngine->AddOnScreenDebugMessage(1, 5.f, FColor::Red, FString::Printf(TEXT("Pressed Tag: %s"), *InputTag.ToString()));
            }
            
            void AAuraPlayerController::AbilityInputTagReleased(FGameplayTag InputTag)
            {
            	if (GetASC() == nullptr) return;
            	GetASC()->AbilityInputTagReleased(InputTag);
            }
            
            void AAuraPlayerController::AbilityInputTagHeld(FGameplayTag InputTag)
            {
            	if (GetASC() == nullptr) return;
            	GetASC()->AbilityInputTagHeld(InputTag);
            }
          • AuraAbilitySystemComponent는 AuraPlayerController가 호출하는 함수를 통해 실제 GameplayAbility를 작동시켜야 함.

            • AbilitySystemComponent는 현재 보유한 GameplayAbility들을 보유하는 변수 FGameplayAbilityContainer를 반환하는 GetActivatableAbilities() 함수를 제공함.

            • 또한 GameplayAbility를 작동하도록 시도하는 함수 TryActivateAbility()도 제공함.

            • 따라서 AuraAbilitySystemComponent는 AuraPlayerController로부터 전달받을 수 있는 FGameplayTag를 통해 현재 보유한 AuraGameplayAbility 중 전달받은 Tag와 일치하는 StartupTag(FGameplayTag)를 지닌 GameplayAbility를 작동시키면 됨.

              // AuraAbilitySystemComponent.cpp
              // 입력이 유지될 때 호출되는 함수. 입력 시작을 포함함.
              void UAuraAbilitySystemComponent::AbilityInputTagHeld(const FGameplayTag& InputTag)
              {
              	if (!InputTag.IsValid()) return;
              
              	for (FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities())
              	{
              		if (AbilitySpec.DynamicAbilityTags.HasTagExact(InputTag))
              		{
              			AbilitySpecInputPressed(AbilitySpec);
              			if (!AbilitySpec.IsActive())
              			{
              				TryActivateAbility(AbilitySpec.Handle);
              			}
              		}
              	}
              }
              
              // 입력이 중단(Release)될 때 호출되는 함수
              void UAuraAbilitySystemComponent::AbilityInputTagReleased(const FGameplayTag& InputTag)
              {
              	if (!InputTag.IsValid()) return;
              
              	for (FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities())
              	{
              		if (AbilitySpec.DynamicAbilityTags.HasTagExact(InputTag))
              		{
              			AbilitySpecInputReleased(AbilitySpec);
              		}
              	}
              }
profile
저는 게임 개발자로 일하고 싶어요

0개의 댓글