본문 바로가기
IT/언리얼_포트폴리오

2일차. GAS 입력 바인딩 구조 설계 (GameplayTag 기반 입력 추상화)

by 웃는펭귄 2026. 2. 15.

📌 작업 목적

GAS(Gameplay Ability System)를 사용하는 프로젝트에서
입력(Input)을 Ability에 직접 연결하지 않고, GameplayTag를 매개로 추상화하는 구조를 설계 및 구현.

목표:

  • 입력 시스템과 Ability 시스템을 느슨하게 분리
  • 데이터 중심 설계로 확장성 확보
  • 캐릭터별 입력 구성 분리 가능
  • GAS 철학(Tag 기반 설계)에 맞춘 구조 정립

1. 전체 아키텍처 개요

🔁 입력 → Ability 실행 흐름

[키/패드 입력]
      ↓
InputAction (Enhanced Input)
      ↓
InputTag (GameplayTag)
      ↓
AbilitySystemComponent
      ↓
해당 InputTag를 가진 Ability Activate

핵심 개념

  • InputAction은 입력 계층
  • Ability는 게임플레이 계층
  • 둘을 직접 연결하지 않음
  • 대신 GameplayTag를 중간 계층으로 사용

2. 왜 이 구조를 선택했는가?

❌ 단순 연결 방식의 문제점

IA_Attack → GA_Attack 직접 실행

문제:

  • Ability 변경 시 입력 코드 수정 필요
  • 플랫폼 변경 시 구조 수정 발생
  • 캐릭터별 입력 구성 분리 어려움
  • GAS 철학과 불일치

✅ GameplayTag 기반 구조의 장점

 
InputAction → InputTag → ASC → Ability
항목효과
느슨한 결합 입력과 Ability가 Tag로만 연결
데이터 중심 DataAsset 수정으로 입력 변경 가능
확장성 캐릭터별 InputConfig 교체 가능
GAS 친화 Ability Input Tag와 자연스럽게 연결
네트워크 안정성 Ability 실행은 ASC가 담당

3. 단계별 구현 정리


① Native Gameplay Tags 정의

목적

입력을 문자열이나 키값이 아닌 GameplayTag로 관리하기 위함.

구현

DirectGameplayTags.h

namespace DirectGameplayTags
{
    DIRECT_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_Move);
    DIRECT_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_Look);
    DIRECT_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_Roll);
}

DirectGameplayTags.cpp

UE_DEFINE_GAMEPLAY_TAG(InputTag_Move, "InputTag.Move");
UE_DEFINE_GAMEPLAY_TAG(InputTag_Look, "InputTag.Look");
UE_DEFINE_GAMEPLAY_TAG(InputTag_Roll, "InputTag.Roll");

설계 의도

  • Ability는 "InputTag.Roll"만 인지
  • 실제 키는 몰라도 됨
  • 키 변경 시 Ability 수정 불필요

② Input Config Data Asset 설계

목적

InputTag ↔ InputAction 매핑을 데이터화

구조

USTRUCT(BlueprintType)
struct FDirectInputActionConfig
{
    UPROPERTY(EditDefaultsOnly, meta = (Categories = "InputTag"))
    FGameplayTag InputTag;

    UPROPERTY(EditDefaultsOnly)
    UInputAction* InputAction;
};
UCLASS()
class UDataAsset_InputConfig : public UDataAsset
{
    UPROPERTY(EditDefaultsOnly)
    UInputMappingContext* DefaultMappingContext;

    UPROPERTY(EditDefaultsOnly)
    TArray<FDirectInputActionConfig> NativeInputActions;
};

에디터에서 설정

InputTagInputAction
Move IA_Move
Roll IA_Roll

설계 포인트

  • 코드 수정 없이 입력 교체 가능
  • 캐릭터별 DataAsset 교체 가능
  • 플랫폼 대응 유리

③ Custom Input Component 제작

목적

Tag 기반 바인딩 API 제공

구현

 
UCLASS()
class UDirectInputComponent : public UEnhancedInputComponent
{
    template<class UserObject, typename CallbackFunc>
    void BindNativeInputAction(
        const UDataAsset_InputConfig* InputConfig,
        const FGameplayTag& InputTag,
        ETriggerEvent TriggerEvent,
        UserObject* ContextObject,
        CallbackFunc Func);
};

내부 동작

  1. InputTag로 InputAction 검색
  2. EnhancedInput BindAction 호출
  3. 입력 발생 시 콜백 실행

④ Character에서 실제 바인딩

위치

ADirectBaseCharacter::SetupPlayerInputComponent

예시

 
InputComponent->BindNativeInputAction(
    InputConfig,
    DirectGameplayTags::InputTag_Roll,
    ETriggerEvent::Started,
    this,
    &ThisClass::Input_AbilityPressed);

콜백 처리

 
void ADirectBaseCharacter::Input_AbilityPressed(FGameplayTag InputTag)
{
	AbilitySystemComponent->AbilityInputTagPressed(InputTag);
}

4. Ability 쪽 연결 방식

Ability 클래스에서:

 
Ability Input Tag = InputTag.Roll

ASC 내부에서:

  • 입력 Tag와 Ability의 InputTag 비교
  • 일치 시 ActivateAbility

이로 인해 Ability는 입력 키를 전혀 모른다.


5. 에디터 설정 단계

필수 설정:

  • Input Mapping Context (IMC)
  • DataAsset_InputConfig 지정
  • Character/Controller에 적용

이 단계가 빠지면 입력이 동작하지 않음.


6. Direct 프로젝트 구조 정리

 
Source/Direct/
├── DirectGameplayTags        ← 입력 태그 정의
├── DataAsset_InputConfig     ← Tag ↔ Action 매핑
├── DirectInputComponent      ← Tag 기반 바인딩
├── DirectBaseCharacter       ← 실제 입력 바인딩
└── Ability 클래스들          ← Ability Input Tag 지정

7. 이번 작업의 핵심 성과

  • GAS 친화적인 입력 아키텍처 설계 완료
  • 입력-Ability 완전 분리 구조 확립
  • 캐릭터별 입력 구성 교체 가능 구조 확보
  • 유지보수성과 확장성 고려한 데이터 중심 설계 적용