내배캠/GAS(Gameplay Ability System)

GAS_03_무기 스폰 및 캐릭터 초기 능력 설정

동그래님 2025. 5. 4. 15:46

📌 GAS 도입 이유

구르기, 공격, 방어 등의 능력 사이의 우선순위 및 취소 관계를 간단하게 태그 기반으로 처리하기 위함이다.
또한 Gameplay Effect와 Attribute, Gameplay Cue를 통합적으로 관리할 수 있고 조건 분기 없이 Tag 기반으로 로직을 처리해 코드 간결화 및 확장성을 향상시킬 수 있다.

✅ GAS 구성 요소

  • UAbilitySystemComponent: 능력 부여 및 처리를 담당하는 GAS의 핵심
  • UAttributeSet: 체력, 공격력 등 게임 내 수치 정의
  • GameplayEffect: 속성 변화, 상태 변화 등 처리
  • GameplayTag: 조건 및 상태를 정의하는 키워드
  • GameplayCue: VFX/SFX 등 시각/청각 연출

 

📌 캐릭터에 ASC(Ability System Component)와 AS(Attribute Set) 적용

✅ 플러그인 세팅 및 클래스 생성

"Gameplay Abilites" 플러그인 활성화 및 프로젝트 재시작

 

"AbilitySystemComponent" 와 "AttributeSet" 을 상속받는 새 C++ 클래스 생성

 

클래스를 생성하고 컴파일을 시도했을 때, 위와 같이 에러가 발생하는 것을 확인할 수 있다.
GameplayTask 관련 모듈이 Build.cs에 포함되어 있지 않아서 발생한 문제이므로 추가해준다.

 

// Copyright Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class RPG : ModuleRules
{
	public RPG(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
	
		PublicDependencyModuleNames.AddRange(new string[] { 
			"Core",
			"CoreUObject",
			"Engine",
			"InputCore",
			"EnhancedInput",
            "GameplayTags",
            "GameplayTasks"
        });

		PrivateDependencyModuleNames.AddRange(new string[] {  });
	}
}
"GameplayTasks" 모듈 추가

 

✅ Base 캐릭터 클래스에 ASC과 AS 적용

GAS를 플레이어와 AI 모두 적용시킬 것이기 때문에, Base캐릭터 클래스에 ACS와 AS를 적용

🔹 RPGBaseCharacter.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "AbilitySystemInterface.h" // ASInterface 헤더 추가
#include "RPGBaseCharacter.generated.h"

class URPGAbilitySystemComponent;
class URPGAttributeSet;

UCLASS()
class RPG_API ARPGBaseCharacter : public ACharacter, public IAbilitySystemInterface
{
	GENERATED_BODY()

public:
	ARPGBaseCharacter();

	//~ Begin IAbilitySystemInterface Interface
	virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
	//~ End IAbilitySystemInterface Interface.

protected:
	//~ Begin APawn Interface
	virtual void PossessedBy(AController* NewController) override;
	//~ End APawn Interface

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AbilitySystem")
	URPGAbilitySystemComponent* RPGAbilitySystemComponent;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AbilitySystem")
	URPGAttributeSet* RPGAttributeSet;

public:
	FORCEINLINE URPGAbilitySystemComponent* GetRPGAbilitySystemComponent() const { return RPGAbilitySystemComponent; }
	FORCEINLINE URPGAttributeSet* GetRPGAttributeSet() const { return RPGAttributeSet; }

};

 

🔹 RPGBaseCharacter.cpp

#include "Characters/RPGBaseCharacter.h"
#include "AbilitySystem/RPGAbilitySystemComponent.h"
#include "AbilitySystem/RPGAttributeSet.h"

ARPGBaseCharacter::ARPGBaseCharacter()
{
	PrimaryActorTick.bCanEverTick = false;
	PrimaryActorTick.bStartWithTickEnabled = false;

	GetMesh()->bReceivesDecals = false;

	RPGAbilitySystemComponent = CreateDefaultSubobject<URPGAbilitySystemComponent>(TEXT("RPGAbilitySystemComponent"));
	RPGAttributeSet = CreateDefaultSubobject<URPGAttributeSet>(TEXT("RPGAttributeSet"));
}

UAbilitySystemComponent* ARPGBaseCharacter::GetAbilitySystemComponent() const
{
	return GetRPGAbilitySystemComponent();
}

void ARPGBaseCharacter::PossessedBy(AController* NewController)
{
	Super::PossessedBy(NewController);

	if (RPGAbilitySystemComponent)
	{
    	// 액터의 ASC가 정상적으로 동작하도록 초기화 작업
		RPGAbilitySystemComponent->InitAbilityActorInfo(this, this); 
	}
}

🔎InitAbilityActorInfo(UObject* OwnerActor, AActor* AvatarActor)

GAS 내부에서 사용하는 FGameplayAbilityActorInfo 구조체를 초기화한다.
이 구조체는 Ability, Attribute, Tag, Montage 등을 작동시키는데 필수적으로 필요하며, 초기화 되지 않으면 능력을 부여해도 실행되지 않거나 속성이 적용되지 않는 오류가 발생한다.
OwnerActor는 ASC의 소유자를 의미하며, 보통 PlayerState 혹은 Character가 된다. 멀티플레이에서는 보통 Owner를 PlayerState로 설정해야 GAS가 클라이언트/서버 모두 동작한다. 
현재는 GAS 시스템을 빠르게 적용해보고 이해하는데 초점이 있어, 멀티플레이를 고려하지 않고 있기 때문에 this로 설정하였다.
AvatarActor는 게임 월드 상의 실제 액터를 의미한다.

 

🔎IAbilitySystemInterface를 상속 받는 이유

언리얼 엔진의 GAS가 이 Actor에게 능력을 부여하고 처리할 수 있도록 하기 위해, 해당 액터가 어떤 UAbilitySystemComponent를 가지고 있는지 GAS시스템이 알 수 있도록 IAbilitySystemInterface를 상속 받야한다.
이로 인해 IAbilitySystemInterface::GetAbilitySystemComponent() 함수를 강제로 요구하여 위와 같이 작성해줘야한다.
AbilitySystemComponent = Cast<IAbilitySystemInterface>(Actor)->GetAbilitySystemComponent();
GAS는 능력을 부여하거나 발동할 때, 대상 액터에게 UAbilitySystemComponent가 있는지 확인해야 하는데, 위와 같이 액터가 IAbilitySystemInterface를 구현하고 있기에 안전하게 ASC를 가져올 수 있게 된다.
GAS는 해당 ASC를 통해 능력을 실행하거나 속성을 변경하는 등의 작업을 할 수 있다.

 

🔔 ASC와 AS 적용되었는지 확인

Base 캐릭터 클래스에 적용된 ASC와 AS가 플레이어 캐릭터 클래스에도 적용되었는지 확인

🔹 WarriorHeroCharacter.h

UCLASS()
class RPG_API AWarriorHeroCharacter : public ARPGBaseCharacter
{
	GENERATED_BODY()
	
public:
	AWarriorHeroCharacter();

protected:
	//~ Begin APawn Interface
	virtual void PossessedBy(AController* NewController) override;
	//~ End APawn Interface
    
    /** 생략 */
}

🔹 WarriorHeroCharacter.cpp

#include "EnhancedInputSubsystems.h"
#include "DataAssets/Input/DataAsset_InputConfig.h"
#include "Components/Input/RPGInputComponent.h"
#include "RPGGameplayTags.h"
#include "AbilitySystem/RPGAbilitySystemComponent.h"

#include "WarriorDebugHelper.h"

void AWarriorHeroCharacter::PossessedBy(AController* NewController)
{
	Super::PossessedBy(NewController);

	if (RPGAbilitySystemComponent && RPGAttributeSet)
	{
		const FString ASCText = FString::Printf(TEXT("Owner Actor: %s, AvatarActor: %s"),
			*RPGAbilitySystemComponent->GetOwnerActor()->GetActorLabel(),
			*RPGAbilitySystemComponent->GetAvatarActor()->GetActorLabel());
		Debug::Print(TEXT("Ability system component valid ") + ASCText, FColor::Green);
		Debug::Print(TEXT("Attribute set valid "), FColor::Green);
	}
}

 

플레이어 캐릭터 클래스에서도 정상적으로 ASC와 AC가 존재하는 것을 확인

 

📌 GAS 기반 무기 스폰 기능 Gameplay Ability구현

GAS기반으로 무기를 스폰하는 기능을 구현할 것이다.
GAS는 캐릭터가 능력을 가지는 것이 아닌, AbilitySystemComponent(ASC)가 능력을 갖는 구조이다.
능력은 ASC에 부여되며, 입력 혹은 시스템 트리거에 의해 ASC를 통해 활성화된다.

✅ Custom Gameplay Ability 클래스 생성

커스텀 Gameplay Ability 클래스를 생성하고, 자동으로 실행되는 정책 설정(Policy)을 진행

"GameplayAbility"를 상속받는 새 C++클래스 생성

 

🔹 RPGGameplayAbility.h

#pragma once

#include "CoreMinimal.h"
#include "Abilities/GameplayAbility.h"
#include "RPGGameplayAbility.generated.h"

UENUM(BlueprintType)
enum class ERPGAbilityActivationPolicy : uint8
{
	OnTriggered,
	OnGiven
};

UCLASS()
class RPG_API URPGGameplayAbility : public UGameplayAbility
{
	GENERATED_BODY()
	
protected:
	//~ Begin UGameplayAbility Interface

	virtual void OnGiveAbility( // AbilitySystemComponent에 Ability가 부여된 직후에 호출
		const FGameplayAbilityActorInfo* ActorInfo,
		const FGameplayAbilitySpec& Spec) override;

	virtual void EndAbility( // Ability가 종료된 직후에 호출
		const FGameplayAbilitySpecHandle Handle,
		const FGameplayAbilityActorInfo* ActorInfo,
		const FGameplayAbilityActivationInfo ActivationInfo,
		bool bReplicateEndAbility,
		bool bWasCancelled) override;

	//~ End UGameplayAbility Interface

	UPROPERTY(EditDefaultsOnly, Category = "RPGAbility")
	ERPGAbilityActivationPolicy AbilityActivaionpolicy = ERPGAbilityActivationPolicy::OnTriggered;

};

 

🔹 RPGGameplayAbility.cpp

#include "AbilitySystem/Abilities/RPGGameplayAbility.h"
#include "AbilitySystem/RPGAbilitySystemComponent.h"

void URPGGameplayAbility::OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec)
{
	Super::OnGiveAbility(ActorInfo, Spec);

	if (AbilityActivaionpolicy == ERPGAbilityActivationPolicy::OnGiven)
	{
		if (ActorInfo && !Spec.IsActive())
		{
			ActorInfo->AbilitySystemComponent->TryActivateAbility(Spec.Handle);
		}
	}
}

void URPGGameplayAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled)
{
	Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);

	if (AbilityActivaionpolicy == ERPGAbilityActivationPolicy::OnGiven)
	{
		if (ActorInfo)
		{
			ActorInfo->AbilitySystemComponent->ClearAbility(Handle);
		}
	}
}
  • ActivationPolicy의 의미:
    • OnTriggered: 능력을 부여받더라도 수동으로 능력 실행(입력, 이벤트, 코드로 명시적 TryActivateAbility( ) 호출 시점
    • OnGiven: 능력을 부여받는 시점에 자동으로 능력 실행(OnGiveAbility( ) 호출되며 자동으로 실행)

 

컴파일 후 에디터로 돌아와  방금 만든 커스텀 "RPGGameplayAbility"클래스를 상속 받는 "Gameplay Ability Blueprint" 클래스를 생성해준다.
게임을 시작하면 자동으로 무기를 스폰해 장착하는 Ability를 만들고 있기 때문에, 이름은 "GA_Shared_SpawnWeapon"으로 명명하였다.

 

"AbilityActivaionpolicy"를 "OnGiven" 으로 설정하면, Gameplay Ability 기초 설정을 완료되었다.

 

✅ 무기 클래스 생성

Actor를 상속받은 기본 무기가 될 "RPGWeaponBase" 클래스를 생성하고, 이를 상속 받는 "WarriorHeroWeapon" 클래스를 생성한다.

🔹 RPGWeaponBase.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "RPGWeaponBase.generated.h"

class UBoxComponent;

UCLASS()
class RPG_API ARPGWeaponBase : public AActor
{
	GENERATED_BODY()
	
public:	
	ARPGWeaponBase();

protected:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapons")
	UStaticMeshComponent* WeaponMesh;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapons")
	UBoxComponent* WeaponCollisionBox;

public:
	FORCEINLINE UBoxComponent* GetWeaponCollisionBox() const { return WeaponCollisionBox; }
};

🔹 RPGWeaponBase.cpp

#include "Items/Weapons/RPGWeaponBase.h"
#include "Components/BoxComponent.h"

ARPGWeaponBase::ARPGWeaponBase()
{
	PrimaryActorTick.bCanEverTick = false;

	WeaponMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("WeaponMesh"));
	SetRootComponent(WeaponMesh);

	WeaponCollisionBox = CreateDefaultSubobject<UBoxComponent>(TEXT("WeaponCollisionBox"));
	WeaponCollisionBox->SetupAttachment(GetRootComponent());
	WeaponCollisionBox->SetBoxExtent(FVector(20.0f));
	WeaponCollisionBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
"RPGWeaponBase" 클래스에서 StaticMeshComponent와 BoxComponent를 생성해 부착시켜주었고, "WarriorHeroWeapon" 클래스는 이를 상속받고 다른 구현은 하지 않았다.

 

에디터에서 "WarriorHeroWeapon" C++ 클래스를 상속 받는 "BP_WarriorHeroWeaponBase"를 생성해주고, 이 "BP_WarriorHeroWeaponBase"를 상속 받는 "BP_HeroAxe" 클래스를 생성해주었다.
Mesh와 Collision을 적절하게 설정해준다.

 

✅ GameplayAbility 무기 스폰 로직 구현

🔹 GA_Shared_SpawnWeapon

앞서 생성했던 GA_Shared_SpawnWeapon에 무기를 스폰하고 SkeletalMesh에 Attach하는 로직을 구현한다.
SpawnActor에 연결할 Class의 경우 앞서 생성했던 BP_WarriorHeroWeaponBase Class Reference 변수로 설정하고,
Attach Actor To Component 노드에 연결할 Socket의 경우 따로 변수로 생성해준다.

🔹 Skeletal Mesh에 소켓 추가

에디터에서 캐릭터의 Skeletal Mesh를 열어 Spine_02에 Add Socket으로 소켓을 추가하였다.
추가한 소켓에 우클릭 후, Add Preview Asset으로 장착할 무기 Mesh를 선택해 실제로 무기를 장착했을 때 자연스러운 소켓 위치를 잡아주었다.

 

Preview Animation에서 뒤에 있는 장비를 손으로 잡는 애니메이션으로 설정한 뒤, 등에 있는 무기를 장착하는 애니메이션의 손 위치가 무기를 잡는 지점에 애니메이션을 스탑 시켜놓고, 무기의 위치가 자연스러워 보이도록 추가적으로 디테일을 잡아나갔다.

 

🔹 Skeletal Mesh에 소켓 추가

앞서 무기 스폰 로직을 구현했던 "GA_Shared_SpawnWeapon"를 상속 받는 GameplayAbility 클래스를 생성 한 뒤,
도끼 무기의 이름에 맞춰 "GA_Hero_SpawnAxe"로 명명하였다.
디테일 패널에서 스폰할 무기 종류와, 앞서 설정한 소켓 이름을 할당해주었다.

 

📌 Gameplay Ability 캐릭터에 부여

✅ 초기 능력 데이터 베이스 설계

위와 같이 캐릭터의 초기 능력 구성을 위한 Data Asset을 설계한다.

 

 

"DataAsset"을 상속 받는 "DataAsset_StartUpDataBase" C++클래스 생성하고,
이 "DataAsset_StartUpDataBase"를 상속 받는 "DataAsset_HeroStartUpData" C++클래스 생성한다.

🔹 DataAsset_StartUpDataBase.h

#pragma once

#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "DataAsset_StartUpDataBase.generated.h"

class URPGGameplayAbility;
class URPGAbilitySystemComponent;

UCLASS()
class RPG_API UDataAsset_StartUpDataBase : public UDataAsset
{
	GENERATED_BODY()
	
public:
	// 캐릭터(플레이어/적)의 ASC에 초기 능력을 부여
	virtual void GiveToAbilitySystemComponent(URPGAbilitySystemComponent* InRPG_ASC, int32 ApplyLevel = 1);

protected:
	// 캐릭터가 생성되자마자 즉시 부여하고 활성화할 능력들
	UPROPERTY(EditDefaultsOnly, Category = "StartUpData")
	TArray<TSubclassOf<URPGGameplayAbility>> ActivateOnGivenAbilites;

	// 피격/사망 등 조건부로 발동되는 능력들
	UPROPERTY(EditDefaultsOnly, Category = "StartUpData")
	TArray<TSubclassOf<URPGGameplayAbility>> ReactiveAbilites;

	// ASC에 Ability들을 등록
	void GrantAbilites(
		const TArray<TSubclassOf<URPGGameplayAbility>>& InAbilitesToGive,
		URPGAbilitySystemComponent* InRPG_ASC, int32 ApplyLevel = 1
	);
};

 

🔹 DataAsset_StartUpDataBase.cpp

#include "DataAssets/StartUpData/DataAsset_StartUpDataBase.h"
#include "AbilitySystem/RPGAbilitySystemComponent.h"
#include "AbilitySystem/Abilities/RPGGameplayAbility.h"

void UDataAsset_StartUpDataBase::GiveToAbilitySystemComponent(URPGAbilitySystemComponent* InRPG_ASC, int32 ApplyLevel)
{
	check(InRPG_ASC);

	GrantAbilites(ActivateOnGivenAbilites, InRPG_ASC, ApplyLevel);
	GrantAbilites(ReactiveAbilites, InRPG_ASC, ApplyLevel);
}

void UDataAsset_StartUpDataBase::GrantAbilites(const TArray<TSubclassOf<URPGGameplayAbility>>& InAbilitesToGive, URPGAbilitySystemComponent* InRPG_ASC, int32 ApplyLevel)
{
	if (InAbilitesToGive.IsEmpty())
	{
		return;
	}

	for (const TSubclassOf<URPGGameplayAbility>& Ability : InAbilitesToGive)
	{
		if (!Ability) continue;

		FGameplayAbilitySpec AbilitySpec(Ability); // 어빌리티 클래스 지정
		AbilitySpec.SourceObject = InRPG_ASC->GetAvatarActor(); // 출처 지정
		AbilitySpec.Level = ApplyLevel; // 레벨 설정

		InRPG_ASC->GiveAbility(AbilitySpec); // ASC에 등록
	}
}
Ability를 부여하는 로직과 Ability List를 DataAsset 내부에서 관리하여 캡슐화시킨다.
  •  TSubclassOf<URPGGameplayAbility>:
    • GAS 기반 능력 클래스의 Blueprint 클래스 참조
  • FGameplayAbilitySpec:
    • ASC에 부여할 실제 능력 인스턴스 정보(Class + Source + Level)
  • GiveAbility( ):
    • ASC 내부 능력 리스트에 추가

 

에디터에서 "DataAsset_HeroStartUpData" C++클래스를 상속받는 "DA_Hero" Data Asset 블루프린트 클래스 생성

 

무기 스폰 및 장착 Ability인 "GA_Hero_SpawnAxe"를 할당

 

✅ 캐릭터 클래스에 StartUpData 선언 및 Ability 활성화

🔹 RPGBaseCharacter.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "AbilitySystemInterface.h"
#include "RPGBaseCharacter.generated.h"

class URPGAbilitySystemComponent;
class URPGAttributeSet;
class UDataAsset_StartUpDataBase;

UCLASS()
class RPG_API ARPGBaseCharacter : public ACharacter, public IAbilitySystemInterface
{
	GENERATED_BODY()

public:
	ARPGBaseCharacter();

	//~ Begin IAbilitySystemInterface Interface
	virtual UAbilitySystemComponent* GetAbilitySystemComponent() const;
	//~ End IAbilitySystemInterface Interface.

protected:
	//~ Begin APawn Interface
	virtual void PossessedBy(AController* NewController) override;
	//~ End APawn Interface

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AbilitySystem")
	URPGAbilitySystemComponent* RPGAbilitySystemComponent;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AbilitySystem")
	URPGAttributeSet* RPGAttributeSet;
	
    // StartUpDataBase를 소프트 레퍼런스로 참조
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "CharacterData")
	TSoftObjectPtr<UDataAsset_StartUpDataBase> CharacterStartUpData;

public:
	FORCEINLINE URPGAbilitySystemComponent* GetRPGAbilitySystemComponent() const { return RPGAbilitySystemComponent; }
	FORCEINLINE URPGAttributeSet* GetRPGAttributeSet() const { return RPGAttributeSet; }

};
초기 Ability 설정이기 때문에 플레이어 뿐만 아니라 적 캐릭터 모두 적용되어야 하기 때문에, BaseCharacter 클래스에 선언해주었다.
DataAsset에서는 하드 레퍼런스로 Ability(BP클래스)를 보관하지만, 캐릭터 클래스에서는 소프트 레퍼런스로 해당 DataAsset을 참조하도록 하였다.

 

🔹 WarriorHeroCharacter.cpp

void AWarriorHeroCharacter::PossessedBy(AController* NewController)
{
	Super::PossessedBy(NewController);

	if (!CharacterStartUpData.IsNull())
	{
		if (UDataAsset_StartUpDataBase* LoadedData = CharacterStartUpData.LoadSynchronous())
		{
			LoadedData->GiveToAbilitySystemComponent(RPGAbilitySystemComponent);
		}
	}
}
BaseCharacter에서 소프트 레퍼런스로 저장되어 있는 DataAsset을 동기 로딩하여 가져와, DataAsset에 정의된 GiveToAbilitySystemComponent 함수를 호출해 등록되어있는 모든 Ability를 활성화 해주었다.
플레이어 캐릭터는 동기식, Enemy 캐릭터는 비동기식 로딩으로 진행하고자 한다. 자세한 이유는 아래 소프트 레퍼런스 로딩 방식 참고.

 

🔎소프트 레퍼런스?

더보기
실제 객체나 클래스의 인스턴스를 직접 들고 있지 않고, 해당 에셋의 경로(Path)만 문자열로 보관된다.
하드 레퍼런스처럼 불필요한 의존성이나 로딩 비용을 가제하지 않기 때문에 언리얼의 패키지 로딩 죄적화에 유리하다.
하지만 사용 전에 반드시 로딩이 필요하고, 로딩하지 않으면 nullptr이다.

로딩하는 방식에는 동기 로딩(LoadSynchronous)와 비동기 로딩(FStreamableManager + RequestAsyncload)로 나뉜다.
const UMyDataAsset* LoadedData = SoftRef.LoadSynchronous();
  • 동기 로딩:
    • LoadSynchronous() 사용
    • 즉시 객체를 반환해서 바로 사용 가능
    • 게임 스레드를 블로킹 하므로, 많은 양의 데이터를 동시에 로딩하면 프레임 멈춤 현상 발생
    • 게임 시작 직후나 플레이어 스폰 직후 등 이미 로딩 지연이 있는 상황일 때 사용
FStreamableManager& Manager = UAssetManager::GetStreamableManager();
Manager.RequestAsyncLoad(SoftRef.ToSoftObjectPath(), FStreamableDelegate::CreateUObject(this, &MyClass::OnLoaded));
  • 비동기 로딩:
    • Streamable.RequestAsyncLoad() 등 사용
    • 백그라운드 로딩
    • 로딩 완료 타이밍에 따른 콜백 함수 실행으로 인한 로직 분기 필요
    • 프레임 멈춤 없기 때문에, 게임 중 로딩이 자연스럽게 필요할 때 사용

 

에디터로 돌아와 플레이어 캐릭터에 DataAsset_Hero를 할당해준다.

 

🔔 결과

  1. 플레이어 캐릭터는 DA_Hero 시작 데이터 에셋을 TSoftObjectPtr로 소프트 레퍼런스로 참조
  2. PossessedBy() 시점에서 DA_Hero를 동기 로딩하여 메모리에 불러옴
  3. DA_Hero의 GiveToAbilitySystemComponent() 함수로 가지고 있는 Ability를 모두 ASC에 등록
  4. DA_Hero에는 하드 레퍼런스로 "GA_HeroSpawnAxe" Ability가 포함되어 있음
  5. GA_HeroSpawnAxe Ability는 RPGGameplayAbility를 상속 받음
    • ActivationPolicy = OnGiven으로 활성화 정책이 설정되어있으면 Ability가 등록될 때, 자동으로 실행
  6. GA_HeroSpawnAxe Abiltiy 실행
    • 내부적으로 무기 액터인 BP_Axe를 하드 레퍼런스로 참조
    • 활성화 정책이 OnGiven 상태
    • BP_Axe를 SpawnActor로 생성하고 이를 캐릭터의 지정된 소캣 위치에 부착
  7. 게임이 시작되면 캐릭터가 자동으로 무기를 등에 착용하고 등장하는 로직 완성