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가 올바르게 작동하도록 구현하세요.