들어가기전에..
현재 작업중인 프로젝트의 몬스터클래스를 예시로 설명합니다.
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;
}
}
우리가 의도한대로 랜덤하게 나오는 것을 확인할 수 있습니다!
'언리얼엔진 > UE4' 카테고리의 다른 글
UE4 C++ 비헤이비어 트리를 활용한 몬스터 AI 구현 (1) (2) | 2021.10.01 |
---|---|
UE4 C++ 비헤이비어 트리를 활용한 몬스터 AI 구현 (0) (0) | 2021.10.01 |