엑셀에 저장돼 있는 캐릭터의 스탯데이터 테이블을 언리얼 엔진에 불러들이는 기능을 구현한다
사용할라면 .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

+ Recent posts