PART 4 - 강의 2/8

AbilitySystemComponent 설정

GAS의 핵심 허브 컴포넌트 구현 및 초기화

01

ASC 기본 구현

캐릭터에 AbilitySystemComponent 추가

MyCharacter.h #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "AbilitySystemInterface.h" #include "MyCharacter.generated.h" class UAbilitySystemComponent; class UMyAttributeSet; class UGameplayAbility; class UGameplayEffect; UCLASS() class MYGAME_API AMyCharacter : public ACharacter, public IAbilitySystemInterface { GENERATED_BODY() public: AMyCharacter(); // IAbilitySystemInterface 구현 virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override; // AttributeSet 접근자 UMyAttributeSet* GetAttributeSet() const { return AttributeSet; } protected: virtual void BeginPlay() override; virtual void PossessedBy(AController* NewController) override; virtual void OnRep_PlayerState() override; // GAS 컴포넌트 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Abilities") UAbilitySystemComponent* AbilitySystemComponent; UPROPERTY() UMyAttributeSet* AttributeSet; // 초기 어빌리티 목록 UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Abilities") TArray<TSubclassOf<UGameplayAbility>> DefaultAbilities; // 초기 이펙트 목록 (스탯 초기화 등) UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Abilities") TArray<TSubclassOf<UGameplayEffect>> DefaultEffects; // 초기화 함수 void InitializeAbilitySystem(); void GiveDefaultAbilities(); void ApplyDefaultEffects(); private: bool bAbilitiesInitialized = false; };
MyCharacter.cpp #include "MyCharacter.h" #include "AbilitySystemComponent.h" #include "MyAttributeSet.h" #include "GameplayAbilitySpec.h" AMyCharacter::AMyCharacter() { // ASC 생성 AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>( TEXT("AbilitySystemComponent")); // 리플리케이션 설정 AbilitySystemComponent->SetIsReplicated(true); // 리플리케이션 모드 // Full: 모든 GameplayEffects 복제 (싱글플레이어/AI) // Mixed: GameplayEffects는 소유 클라이언트만, Cues/Tags는 모두에게 (멀티플레이어) // Minimal: 최소 복제 (AI에 적합) AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed); // AttributeSet 생성 - ASC에 자동 등록됨 AttributeSet = CreateDefaultSubobject<UMyAttributeSet>(TEXT("AttributeSet")); } UAbilitySystemComponent* AMyCharacter::GetAbilitySystemComponent() const { return AbilitySystemComponent; } void AMyCharacter::BeginPlay() { Super::BeginPlay(); // ASC ActorInfo 초기화 (싱글플레이어 또는 AI) if (AbilitySystemComponent) { AbilitySystemComponent->InitAbilityActorInfo(this, this); } } void AMyCharacter::PossessedBy(AController* NewController) { Super::PossessedBy(NewController); // 서버에서 초기화 if (HasAuthority()) { InitializeAbilitySystem(); } } void AMyCharacter::OnRep_PlayerState() { Super::OnRep_PlayerState(); // 클라이언트에서 ASC 재초기화 if (AbilitySystemComponent) { AbilitySystemComponent->InitAbilityActorInfo(this, this); } } void AMyCharacter::InitializeAbilitySystem() { if (bAbilitiesInitialized || !AbilitySystemComponent) { return; } // ActorInfo 초기화 (Owner와 Avatar) AbilitySystemComponent->InitAbilityActorInfo(this, this); // 초기 어빌리티 부여 GiveDefaultAbilities(); // 초기 이펙트 적용 ApplyDefaultEffects(); bAbilitiesInitialized = true; } void AMyCharacter::GiveDefaultAbilities() { if (!HasAuthority() || !AbilitySystemComponent) { return; } for (TSubclassOf<UGameplayAbility>& AbilityClass : DefaultAbilities) { if (AbilityClass) { // 어빌리티 스펙 생성 및 부여 FGameplayAbilitySpec Spec( AbilityClass, // 어빌리티 클래스 1, // 레벨 INDEX_NONE, // 입력 ID (나중에 바인딩) this // 소스 오브젝트 ); AbilitySystemComponent->GiveAbility(Spec); } } } void AMyCharacter::ApplyDefaultEffects() { if (!HasAuthority() || !AbilitySystemComponent) { return; } for (TSubclassOf<UGameplayEffect>& EffectClass : DefaultEffects) { if (EffectClass) { // 이펙트 컨텍스트 생성 FGameplayEffectContextHandle Context = AbilitySystemComponent->MakeEffectContext(); Context.AddSourceObject(this); // 이펙트 스펙 생성 FGameplayEffectSpecHandle Spec = AbilitySystemComponent->MakeOutgoingSpec( EffectClass, 1, // 레벨 Context ); if (Spec.IsValid()) { // 자신에게 이펙트 적용 AbilitySystemComponent->ApplyGameplayEffectSpecToSelf( *Spec.Data.Get()); } } } }
02

리플리케이션 모드

멀티플레이어에서의 ASC 동기화 전략

모드 GameplayEffects GameplayCues 사용 사례
Full 모든 클라이언트에 복제 복제됨 싱글플레이어, AI
Mixed 소유 클라이언트만 모든 클라이언트 멀티플레이어 플레이어
Minimal 복제 안 함 복제됨 AI 전용
C++ // 리플리케이션 모드 설정 AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed); // 플레이어용 권장 설정 // - Mixed 모드 사용 // - GameplayEffects는 소유 클라이언트만 받음 // - GameplayCues는 모든 클라이언트가 받아 VFX 재생 // AI용 권장 설정 // - Minimal 모드 사용 // - 서버에서만 처리, Cues만 전파
03

PlayerState 소유 패턴

Pawn 변경 시에도 ASC 유지

플레이어 캐릭터의 경우, Pawn이 변경되어도 ASC를 유지하기 위해 PlayerState에 ASC를 배치하는 패턴을 사용합니다.

MyPlayerState.h #pragma once #include "CoreMinimal.h" #include "GameFramework/PlayerState.h" #include "AbilitySystemInterface.h" #include "MyPlayerState.generated.h" UCLASS() class MYGAME_API AMyPlayerState : public APlayerState, public IAbilitySystemInterface { GENERATED_BODY() public: AMyPlayerState(); virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override; UMyAttributeSet* GetAttributeSet() const { return AttributeSet; } protected: UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Abilities") UAbilitySystemComponent* AbilitySystemComponent; UPROPERTY() UMyAttributeSet* AttributeSet; };
MyPlayerState.cpp #include "MyPlayerState.h" #include "AbilitySystemComponent.h" #include "MyAttributeSet.h" AMyPlayerState::AMyPlayerState() { AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>( TEXT("AbilitySystemComponent")); AbilitySystemComponent->SetIsReplicated(true); AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed); AttributeSet = CreateDefaultSubobject<UMyAttributeSet>(TEXT("AttributeSet")); // PlayerState의 NetUpdateFrequency 조정 NetUpdateFrequency = 100.0f; } UAbilitySystemComponent* AMyPlayerState::GetAbilitySystemComponent() const { return AbilitySystemComponent; }
PlayerState 소유 시 Character 구현 // MyPlayerCharacter.cpp void AMyPlayerCharacter::PossessedBy(AController* NewController) { Super::PossessedBy(NewController); // PlayerState에서 ASC 가져오기 if (AMyPlayerState* PS = GetPlayerState<AMyPlayerState>()) { // ASC 캐싱 AbilitySystemComponent = PS->GetAbilitySystemComponent(); AttributeSet = PS->GetAttributeSet(); // ActorInfo 초기화 - Owner는 PlayerState, Avatar는 Character AbilitySystemComponent->InitAbilityActorInfo(PS, this); // 초기 어빌리티/이펙트 적용 if (HasAuthority()) { InitializeAbilitySystem(); } } } void AMyPlayerCharacter::OnRep_PlayerState() { Super::OnRep_PlayerState(); // 클라이언트에서 ASC 재초기화 if (AMyPlayerState* PS = GetPlayerState<AMyPlayerState>()) { AbilitySystemComponent = PS->GetAbilitySystemComponent(); AttributeSet = PS->GetAttributeSet(); AbilitySystemComponent->InitAbilityActorInfo(PS, this); } }
04

어빌리티 활성화

다양한 어빌리티 트리거 방법

C++ // 1. 태그로 어빌리티 활성화 void AMyCharacter::ActivateAbilityByTag(FGameplayTag AbilityTag) { if (AbilitySystemComponent) { FGameplayTagContainer TagContainer; TagContainer.AddTag(AbilityTag); AbilitySystemComponent->TryActivateAbilitiesByTag(TagContainer); } } // 2. 클래스로 어빌리티 활성화 void AMyCharacter::ActivateAbilityByClass( TSubclassOf<UGameplayAbility> AbilityClass) { if (AbilitySystemComponent) { AbilitySystemComponent->TryActivateAbilityByClass(AbilityClass); } } // 3. 스펙 핸들로 어빌리티 활성화 void AMyCharacter::ActivateAbilityByHandle(FGameplayAbilitySpecHandle Handle) { if (AbilitySystemComponent) { AbilitySystemComponent->TryActivateAbility(Handle); } } // 4. 이벤트로 어빌리티 트리거 void AMyCharacter::SendGameplayEvent(FGameplayTag EventTag, FGameplayEventData EventData) { if (AbilitySystemComponent) { AbilitySystemComponent->HandleGameplayEvent(EventTag, &EventData); } }
SUMMARY

핵심 요약

  • CreateDefaultSubobject로 ASC와 AttributeSet 생성
  • InitAbilityActorInfo()로 Owner와 Avatar 설정 필수
  • 멀티플레이어는 Mixed 모드, AI는 Minimal 모드 권장
  • 플레이어는 PlayerState 소유 패턴으로 Pawn 변경에 대응
  • PossessedBy(서버)와 OnRep_PlayerState(클라이언트)에서 초기화
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: ASC 기본 설정

ARPGCharacter에 UAbilitySystemComponent를 CreateDefaultSubobject로 추가하고, IAbilitySystemInterface를 구현하여 GetAbilitySystemComponent()를 오버라이드하세요.

실습 2: ASC 초기 어빌리티 부여

캐릭터의 BeginPlay/PossessedBy에서 GiveAbility()로 기본 어빌리티(공격, 점프, 대시)를 부여하세요. FGameplayAbilitySpec을 사용하여 어빌리티 레벨과 소스 오브젝트를 설정하세요.

심화 과제: NPC와 플레이어의 ASC 아키텍처 분리

플레이어는 PlayerState에, NPC는 Pawn에 ASC를 배치하는 아키텍처를 구현하세요. OwnerActor와 AvatarActor의 차이를 이해하고, 캐릭터 전환(빙의 변경) 시 ASC가 올바르게 작동하도록 구현하세요.