본문 바로가기

언리얼엔진/UE4

UE4 C++ Datatable을 이용한 몬스터 정보 초기화

들어가기전에..

현재 작업중인 프로젝트의 몬스터클래스를 예시로 설명합니다.

Hulk와 Parasite라는 2종류의 몬스터가 있고 데이터 테이블에 각각의 정보를 저장합니다.

json으로 관리하며 이 데이터테이블을 적용시킬 몬스터 캐릭터C++클래스가 미리 있다고 가정합니다.

 

우리는 이 몬스터의 이름에 따라 데이터 테이블에서 정보를 불러온 후 에디터 내에 랜덤하게 스폰을 하려고 합니다.

 

 

DataTable을 만들기 위해 먼저 기본 C++클래스를 생성합니다. 

 

#pragma once

#include "CoreMinimal.h"
#include "Engine/DataTable.h"


#include "DoubleEnemyDataTable.generated.h"

USTRUCT(BlueprintType)
struct FDoubleEnemyDataTable : public FTableRowBase
{
	GENERATED_USTRUCT_BODY()

	//이름
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	FString D_DoubleHitEnemyName;
	//HP
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	float D_CurrentHP;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	float D_MaxHP;
	//Damage
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	float D_Damage;

	//메쉬
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	class USkeletalMesh* D_Mesh;
	//본체,양손 구 컴포넌트
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	float D_BodyX;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	float D_BodyY;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	float D_HandRdius;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	FVector D_LHandFLocation;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	FVector D_RHandFLocation;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	FVector D_WidgetFLocation;
	//위치
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	FVector D_Location;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	FRotator D_Rotation;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	FVector D_Scale;


};

헤더파일에 DataTable을 추가하고 몬스터의 정보를 저장할 변수를 설정합니다.

 

 

 

 

 

에디터에서 마우스 우측 버튼 -> 기타 -> 데이터테이블을 선택합니다.

그리고 위에서 만든 데이터테이블 이름의 C++클래스를 선택합니다.

 

C++클래스에서 저장했던 변수들이 잘 들어가 있는것을 볼 수 있습니다.

상단의 메뉴에서 추가버튼을 눌러 여러분만의 몬스터 데이터를 저장해봅니다.

 

그 다음 이렇게 JSON으로 익스포트합니다.

 

우리는 이제 코드로 데이터를 쉽게 관리할 수 있습니다.

 

 

 

 

 

여기까지 되었다면 이제 남은 것은 이 저장한 데이터를 

몬스터 C++클래스에 초기화하고 에디터에서 불러오는 것입니다.

이미 여러분들은 자기만의 몬스터C++클래스가 있을것입니다.

 

 

[DoubleHitEnemy.h]

#include "Engine/DataTable.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/Character.h"
#include "DoubleHitEnemy.generated.h"



class PORTFOLIOPROJECT_API ADoubleHitEnemy : public ACharacter
{
	GENERATED_BODY()

	

public:

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;
	//몬스터 데이터 초기화 함수
	void SetEnemy(FName EnemyName);
	//데이터 테이블 행
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	USkeletalMesh* SMesh;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	AMeleeEnemyAIController* EnemyAI;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	ADoubleHitEnemy* EnemyCharacter;
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float CyrrentHealth;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float MaxHealth;
	UPROPERTY(EditAnyWhere, BlueprintReadWrite)
	FName DefaultEnemyName = "Hulk";
	UPROPERTY(EditAnyWhere, BlueprintReadWrite)
	FName RandomName;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	float BodyX;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	float BodyY;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	float HandRdius;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	float Damage;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	FVector LHandFLocation;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	FVector RHandFLocation;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	FVector WidgetFLocation;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	FVector EnemyLocation;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	FRotator EnemyRotation;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	FVector EnemyScale;


	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	FString AnimName;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	TSubclassOf<class UAnimInstance> HulkAnimInstance;
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	TSubclassOf<class UAnimInstance> ParasiteAnimInstance;

	//에디터에서 만든 데이터 테이블을 저장할 변수
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	class UDataTable* EnemyDataTable;
	
};

데이터 테이블에서 받아온 변수를 몬스터 클래스의 변수에 저장해야합니다.

그러기 위해 우리도 몬스터 클래스에 변수를 만들어 줍니다.

저는 헷갈리지 않도록 데이터 테이블의 변수는 'D_'를 붙였습니다.

 

 

 

 

 

 

 

[DoubleHitEnemy.cpp]


#include "DoubleHitEnemy.h"
#include "DoubleEnemyDataTable.h"   //우리가 만든 데이터 테이블C++클래스를 추가합니다.
#include "UObject/ConstructorHelpers.h"


// Sets default values
ADoubleHitEnemy::ADoubleHitEnemy()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
	
	//DataTable 초기화
	static ConstructorHelpers::FObjectFinder<UDataTable> DoubleHitEnemyDataObject(TEXT("/Game/Movable/DataTable/DoubleHitEnemyDataTable"));
	if(DoubleHitEnemyDataObject.Succeeded())
	{
		UE_LOG(LogTemp, Warning, TEXT("DataTable Succeed!"));
		EnemyDataTable = DoubleHitEnemyDataObject.Object;
	}
}

우리의 몬스터 클래스 생성자에 에디터상의 데이터 테이블을 초기화합니다.

위 코드처럼 ConstructorHelper를 통해 참조위치를 설정하면 됩니다.

 

 

 

 

 

 

[DoubleHitEnemy.cpp]


// Called when the game starts or when spawned
void ADoubleHitEnemy::BeginPlay()
{
	Super::BeginPlay();
	
	//좀비 랜덤으로 이름을 선택
	auto List = EnemyDataTable->GetRowNames();
	RandomName = List[FMath::RandRange(0,1)];
	FDoubleEnemyDataTable* EnemyData = EnemyDataTable->FindRow<FDoubleEnemyDataTable>(RandomName,FString(""));

	
	//이름에 의한 좀비 초기화
	SetEnemy(RandomName);
	UE_LOG(LogTemp, Warning, TEXT("BeginplayRandomName: %s"),*RandomName.ToString());
	//데이터테이블 적용
	SkeletalMesh->SetSkeletalMesh(SMesh);
	SkeletalMesh->SetRelativeLocation(EnemyLocation);
	SkeletalMesh->SetRelativeRotation(EnemyRotation);
	SkeletalMesh->SetRelativeScale3D(EnemyScale);
	GetCapsuleComponent()->InitCapsuleSize(BodyX, BodyY);
	LHand->InitSphereRadius(HandRdius);
	LHand->SetRelativeLocation(LHandFLocation);
	RHand->InitSphereRadius(HandRdius);
	RHand->SetRelativeLocation(RHandFLocation);
	EnemyWidget->SetRelativeLocation(WidgetFLocation);
	EnemyWidget->SetRelativeRotation(FRotator(0.0f,0.0f,0.0f));
	EnemyWidget->SetRelativeScale3D(FVector(1.0f,1.0f,1.0f));

이렇게 생성자가 아닌 BeginPlay()에서 초기화를 하는 것이 좋습니다.

우선 EnemyDataTable의 이름을 List에 저장합니다.

그리고 RandRange함수를 사용해 임의의 인덱스를 받아옵니다.

[0.Hulk , 1.Parasite]

그리고 그 인덱스의 값 즉, Hulk 또는 Parasite를 통해 해당하는 데이터를 EnemyData에 저장하고 

그 값을 현재 몬스터클래스에 초기화합니다. (SetEnemy함수)

마지막으로 이렇게 얻어온 값으로 몬스터에 적용합니다.

 

 

[DoubleHitEnemy.cpp]

void ADoubleHitEnemy::SetEnemy(FName EnemyName)
 {
 	FDoubleEnemyDataTable* EnemyData = EnemyDataTable->FindRow<FDoubleEnemyDataTable>(EnemyName,FString(""));
 	if(EnemyData)
 	{
 		UE_LOG(LogTemp, Warning, TEXT("EnemyData Succeed!"));
 		DefaultEnemyName = EnemyName;
 		MaxHealth = EnemyData->D_MaxHP;
 		SMesh = EnemyData->D_Mesh;
 		BodyX = EnemyData->D_BodyX;
 		BodyY = EnemyData->D_BodyY;
 		HandRdius = EnemyData->D_HandRdius;
 		LHandFLocation = EnemyData->D_LHandFLocation;
 		RHandFLocation = EnemyData->D_RHandFLocation;
 		WidgetFLocation = EnemyData->D_WidgetFLocation;
 		EnemyLocation = EnemyData->D_Location;
 		EnemyRotation = EnemyData->D_Rotation;
 		EnemyScale = EnemyData->D_Scale;
 		Damage = EnemyData->D_Damage;
 		AnimName= EnemyData->D_AnimName;
 		
 	}



 
 }

 

 

 

 

 

 

 

실행결과

 

우리가 의도한대로 랜덤하게 나오는 것을 확인할 수 있습니다!