엑셀에 저장돼 있는 캐릭터의 스탯데이터 테이블을 언리얼 엔진에 불러들이는 기능을 구현한다
사용할라면 .xml -> .csv로 저장
파일을 불러들이기 위해 테이블 데이터의 각 열의 이름과 유형이 동일한 구조체를 선언해야 한다.
언리언 엔진에서 제공하는 FTableRowBase 구조체를 상속받은 FABCharacterData라는 이름의 구조체를 만든다
#include "KGame.h"
#include "Engine/DataTable.h"
#include "Engine/GameInstance.h"
#include "ABGameInstance.generated.h"
USTRUCT(BlueprintType)
struct FABCharacterData : public FTableRowBase
{
GENERATED_BODY()
public:
FABCharacterData() : Level(1),MaxHp(100), Attack(10.0f),DropExp(10),NextExp(30) {}
UPROPERTY(EditAnyWhere, BlueprintReadWrite, Category = "Data")
int32 Level;
UPROPERTY(EditAnyWhere, BlueprintReadWrite, Category = "Data")
float MaxHp;
UPROPERTY(EditAnyWhere, BlueprintReadWrite, Category = "Data")
float Attack;
UPROPERTY(EditAnyWhere, BlueprintReadWrite, Category = "Data")
int32 DropExp;
UPROPERTY(EditAnyWhere, BlueprintReadWrite, Category = "Data")
int32 NextExp;
};
UCLASS()
class KGAME_API UABGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UABGameInstance();
virtual void Init() override;
};
추가하고 임포트 후 데이터 테이블에 ABCharacterData를 설정할수 있다.
이제 DataTable을 게임 인스턴스의 멤버 변수로 선언하고, 데이터 에셋의 레퍼런스를 복사한 후 데이터를 불러들이는 기능을 구현한다.
//.h
class KGAME_API UABGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UABGameInstance();
virtual void Init() override;
FABCharacterData* GetABCharacterData(int32 Level);
private:
UPROPERTY()
class UDataTable* ABCharacterTable;
};
//.cpp
UABGameInstance::UABGameInstance()
{
FString CharacterDataPath = TEXT("/Game/Book/GameData/ABCharacterData.ABCharacterData");
static ConstructorHelpers::FObjectFinder<UDataTable> DT_ABCHARACTER(*CharacterDataPath);
ABCharacterTable = DT_ABCHARACTER.Object;
}
FABCharacterData* UABGameInstance::GetABCharacterData(int32 Level)
{
return ABCharacterTable->FindRow<FABCharacterData>(*FString::FromInt(Level), TEXT(""));
}
게임인스턴스를 사용하기 위해서 반드시 프로젝트 설정을 해주어야 한다. [7시간 뻘짓함]
게임인스턴스를 사용하기 위해서 반드시 프로젝트 설정을 해주어야 한다. [7시간 뻘짓함]
액터컴포넌트의 클래스를 생성한다
캐릭터에 부착해 캐릭터 스탯에 대한 관리를 액터 컴포넌트가 일임하도록 기능을 구현
//.h
class KGAME_API AABCharacter : public ACharacter
{
///
UPROPERTY(VisibleAnyWhere, Category = Stat)
class UABCharacterStatComponent* CharacterStat;
};
//.cpp
#include "ABCharacter.h"
#include "ABAnimInstance.h"
#include "ABWeapon.h"
#include "ABCharacterStatComponent.h"
#include "DrawDebugHelpers.h"
AABCharacter::AABCharacter()
{
...
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SPRINGARM"));
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("CAMERA"));
CharacterStat = CreateDefaultSubobject<UABCharacterStatComponent>(TEXT("CHARACTERSTAT"));
스탯에 변경이 일어날 떄만 작업을 할예정이라 tick 작업은 할 필요가 없다
그래서 액터 컴포넌트의 설정을 변경해야 한다.
액터의 PostInitializeComponents에 대응하는 컴포넌트의 함수는 InitializeComponent 함수다. 이 함수는 액터의 PostInitalizeComponents 함수가 호출되기 전에 바로 호출된다.
이 함수를 사용해 컴포넌트의 초기화 로직을 구현해주는데, 호출이 되려면 생성자에서 bWantsInitializeCOmpent 값을 true로 설정행 줘야 한다.
/.h
class KGAME_API UABCharacterStatComponent : public UActorComponent
{
protected:
// Called when the game starts
virtual void BeginPlay() override;
virtual void InitializeComponent() override;
};
/.cpp
UABCharacterStatComponent::UABCharacterStatComponent()
{
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = false;
bWantsInitializeComponent = true;
}
void UABCharacterStatComponent::InitializeComponent()
{
Super::InitializeComponent();
}
데이터를 관리하는 변수들은 private으로 한정해 선언하고 레벨은 SetNewLevel 함수를 통해서만 변경 할 수 잇도록 설정한다.
언리얼 오브젝트에는 직렬화 기능이 있어서 오브젝트의 UPROPERTY 속성을 저장하고 로딩할 수 있다.
컴포넌트의 스탯 중 CurrentHp 값은 게임을 시작할 때마다 변경되므로 이 값을 보관하는 것은 의미가 없고 오히려 오브젝트를 저장할 떄 필요 없는 디크슼 공간만 차지한다.
이러한 속성에는 Transient 키워드를 추가해 해당 속성을 직렬화에서 제외시키는 것이 좋다
//.h
class KGAME_API UABCharacterStatComponent : public UActorComponent
{
...
public:
void SetNewLevel(int32 NewLevel);
private:
struct FABCharacterData* CurrentStatData = nullptr;
UPROPERTY(EditInstanceOnly, Category = Stat, Meta = (AllowPrivateAccess = true))
int32 Level;
UPROPERTY(Transient, VisibleInstanceOnly, Category = Stat, Meta = (AllowPrivateAccess = true))
float CurrentHP;
};
//.cpp
#include "ABCharacterStatComponent.h"
#include "ABGameInstance.h"
UABCharacterStatComponent::UABCharacterStatComponent()
{
...
Level = 1;
}
void UABCharacterStatComponent::InitializeComponent()
{
Super::InitializeComponent();
SetNewLevel(Level);
}
void UABCharacterStatComponent::SetNewLevel(int32 NewLevel)
{
auto ABGameInstance = Cast<UABGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));
CurrentStatData = ABGameInstance->GetABCharacterData(NewLevel);
if (nullptr != CurrentStatData)
{
Level = NewLevel;
CurrentHP = CurrentStatData->MaxHp;
}
else
{
//레벨존재 X
}
}//.h
릭터가 대미지를 받으면 받은 대미지만큼 CurrentHp에서 차감하고 그결과로 0이 보다 같거나 작으면 캐릭터가 죽도록 기능을 추가한다
액터 컴포넌트가 캐릭터에 의존성을 가지지 않도록, 액터 컴포넌트에 델리게이트를 선언하고 캐릭터에서 이를 바인딩을 시킨다
/.h
DECLARE_MULTICAST_DELEGATE(FOnHPIsZeroDelegete);
class KGAME_API UABCharacterStatComponent : public UActorComponent
{
public:
void SetDamage(float NewDamage);
float GetAttack();
FOnHPIsZeroDelegete OnHpIsZero;
};
//.cpp
void UABCharacterStatComponent::SetDamage(float NewDamage)
{
CurrentHP = FMath::Clamp<float>(CurrentHP - NewDamage, 0.0f, CurrentStatData->MaxHp);
if (CurrentHP <= 0.0f)
{
OnHpIsZero.Broadcast();
}
}
float UABCharacterStatComponent::GetAttack()
{
return CurrentStatData->Attack;
}
////////////////////////////////////////////////////
//Character
void AABCharacter::PostInitializeComponents()
{
...
CharacterStat->OnHpIsZero.AddLambda([this]()->
void {ABAnim->SetDeadAnim();
SetActorEnableCollision(false);
});
}
void AABCharacter::AttackCheck()
{
if (bResult)
{
if (HitResult.Actor.IsValid())
{
FDamageEvent DamageEvnet;
HitResult.Actor->TakeDamage(CharacterStat->GetAttack(), DamageEvnet,GetController(),this);
}
}
}
float AABCharacter::TakeDamage(float DamageAmout, struct FDamageEvent const& DamageEvent,
class AController* EventInstigator, AActor* DamageCauser)
{
CharacterStat->SetDamage(FinalDamage);
return FinalDamage;
}
'Unreal > Game 1 (C++)' 카테고리의 다른 글
18.UI와 데이터의 연동 (1) | 2019.05.08 |
---|---|
17.모듈과 빌드 설정 (UI작업중) (0) | 2019.05.08 |
15.아이템 습득 + 흭득이펙트 (0) | 2019.05.02 |
14.아이템 상자의 제작 (0) | 2019.05.02 |
13.소켓 + 무기액터 (0) | 2019.05.02 |