Gameplay Attribute를 설정하는 데 사용할 수 있는 Curve Table을 CSV(Comma-Seperated Value) 또는 Json 파일로 Ex/Improt하여 사용하면 편리하다.
먼저 Content Drawer에서 Miscellaneous -> Curve Table을 클릭해서 커브테이블을 생성한 후, 우클릭 -> Export as CSV/JSON
을 클릭해서 간단히 export 할 수 있다.
만들어진 CSV/JSON 파일을 이용해서 커브 테이블을 만들고 싶다면 커브 테이블을 연 후 'Reimport' 버튼을 클릭해서 쉽게 연동할 수 있다.
이제 어트리뷰트들을 적용하는 GameplayEffect를 만든다.
Modifier Magnitude에서 Use Curve Table
을 선택해서 커브를 어트리뷰트 모디파이어와 연결한다.
확장성을 고려해서 캐릭터 종류별 Enum Class를 만들고, 각각의 클래스가 어떤 GE를 사용하는지에 대한 정보를 하나의 Data Asset으로 만들어서 관리한다.
먼저 캐릭터 클래스 Enum을 정의해 주고,
// 캐릭터 타입을 정의
UENUM(BlueprintType)
enum class ECharacterClass : uint8
{
Elementalist,
Warrior,
Ranger
};
사용할 데이터 구조체를 만든다.
USTRUCT(BlueprintType)
struct FCharacterClassDefaultInfo
{
GENERATED_BODY()
UPROPERTY(EditDefaultsOnly, Category = "Class Defaults")
TSubclassOf<UGameplayEffect> PrimaryAttributes;
};
이를 가지고 CharacterClass
와 FCharacterClassDefaultInfo
의 맵을 가지는 Data Asset 클래스를 만든다.
UCLASS()
class AURA_API UCharacterClassInfo : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, Category = "Character Class Defaults")
TMap<ECharacterClass, FCharacterClassDefaultInfo> CharacterClassInformation;
FCharacterClassDefaultInfo GetClassDefaultInfo(ECharacterClass CharacterClass);
};
이후에는 에디터에서 GE와 클래스를 서로 연결.
이제 마지막으로 GameplayEffect를 적용하는 것만 남았다.
그 전에 한가지 생각해야 할 것은 방금 만들었던 CharacterClass를 저장하는 위치이다.
게임의 전체적인 정책을 중앙에서 관리하는 game mode가 좋은 장소가 될 수 있을 것이며 여기에 CharacterClassInfo TObjectPtr을 생성하기로 한다.
AbilitySystemLibrary
에다 함수를 정의하기로 하고, 상위 Character 클래스의 InitializeDefaultAttribute
함수를 가상 함수로 만들어 몬스터클래스에서 오버라이드한다.
void UAuraAbilitySystemLibrary::InitializeDefaultAttributes(const UObject* WorldContextObject, ECharacterClass CharacterClass, float Level, UAbilitySystemComponent* ASC)
{
if (AAuraGameModeBase* AuraGameMode = Cast<AAuraGameModeBase>(UGameplayStatics::GetGameMode(WorldContextObject)))
{
AActor* AvatarActor = ASC->GetAvatarActor();
check(AuraGameMode->CharacterClassInfo);
const FCharacterClassDefaultInfo ClassDefaultInfo = AuraGameMode->CharacterClassInfo->GetClassDefaultInfo(CharacterClass);
// Set Primary Attribute
FGameplayEffectContextHandle PrimaryAttributesContextHandle = ASC->MakeEffectContext();
PrimaryAttributesContextHandle.AddSourceObject(AvatarActor);
const FGameplayEffectSpecHandle PrimaryAttributesSpecHandle = ASC->MakeOutgoingSpec(ClassDefaultInfo.PrimaryAttributes, Level, PrimaryAttributesContextHandle);
ASC->ApplyGameplayEffectSpecToSelf(*PrimaryAttributesSpecHandle.Data.Get());
}
}
ASC에서 ApplyGameplayEffect를 수행할 수 있고 여기서 필요한 건 GameplayEffectSpec
. GameplayEffectSpec
은 MakeOutgoingSpec
로부터 얻은 SpecHandle에서 .Data.Get()
을 통해 얻을 수 있다.
그리고 MakeOutgoingSpec
의 파라미터로써 필요한 FGameplayEffectContextHandle
은 MakeEffectContext()
를 통해 얻을 수 있으며 여기서 AddSourceObject
를 설정해서 이 effect의 출처를 명시한다.
(개인적으로는 이런 식으로 거슬러 거슬러 올라가는 방식이 좋은 것 같다)
그 결과, 디버깅해 보았을 때 Ranger
타입으로 설정된 Goblin_SlingShot
몬스터에서 ClassDefaultInfo가 GE_PrimaryAttributes_Ranger
로 잘 매핑되어 적용되고 있음을 확인할 수 있다.