728x90
반응형
GameInstance라는 클래스를 이용해서 데이터를 담는 클래스로 사용해보자.
// MyGameInstance.h
// DataTable 헤더 필요
#include "Engine/DataTable.h"
#include "MyGameInstance.generated.h"
USTRUCT()
struct FMyCharacterData : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 Level;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 Attack;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MaxHp;
};
정수형 프로퍼티를 struct안에 선언한다.
엔진에서 콘텐츠 브라우저 우클릭 → 기타 → 데이터 테이블로 데이터 테이블을 생성할 수 있다.
// MyGameInstance.cpp
#include "MyGameInstance.h"
UMyGameInstance::UMyGameInstance()
{
// DataTable 선언
static ConstructorHelpers::FObjectFinder<UDataTable> DATA(TEXT("DataTable경로"));
MyStats = DATA.Object;
}
void UMyGameInstance::Init()
{
Super::Init();
// 1번 row의 Attack값을 출력해본다
UE_LOG(LogTemp, Warning, TEXT("MyGameInstance %d"), GetStatData(1)->Attack);
}
// Data를 반환하는 함수
FMyCharacterData* UMyGameInstance::GetStatData(int32 Level)
{
return MyStats->FindRow<FMyCharacterData>(*FString::FromInt(Level), TEXT(""));
}
cpp파일에서는 DataTable경로를 통해 Data를 가져와서 Object를 UDataTable변수에 대입하고, get함수를 통해 Data를 반환할 수 있도록 한다.
결과를 확인하기 위해서는 GameInstance클래스 설정이 필요한데,
프로젝트 세팅 → 맵 & 모드 → 게임 인스턴스 클래스를 생성한 GameInstance 클래스로 변경해준다.
ActorComponent 클래스를 이용해서 다른 클래스에 연결하는 용도의 Component를 만들 수 있다.
// MyStatComponent.h
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "MyStatComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class UNREALINTRODUCTION_API UMyStatComponent : public UActorComponent
{
/.../
protected:
// Called when the game starts
virtual void BeginPlay() override;
// Component를 초기화하는 함수
virtual void InitializeComponent() override;
public:
// 레벨 정하는 함수
void SetLevel(int32 Level);
// 공격당했을 때 호출 함수
void OnAttacked(float DamageAmount);
// 능력치들을 get하는 함수 선언
int32 GetLevel() { return Level; }
int32 GetHp() { return Hp; }
int32 GetAttack() { return Attack; }
private:
// 각각 능력치 프로퍼티 선언
UPROPERTY(EditAnywhere, Category = Stat, Meta = (AllowPrivateAccess = true))
int32 Level;
UPROPERTY(EditAnywhere, Category = Stat, Meta = (AllowPrivateAccess = true))
int32 Hp;
UPROPERTY(EditAnywhere, Category = Stat, Meta = (AllowPrivateAccess = true))
int32 Attack;
};
// MyStatComponent.cpp
#include "MyStatComponent.h"
#include "MyGameInstance.h"
#include "Kismet/GameplayStatics.h"
UMyStatComponent::UMyStatComponent()
{
PrimaryComponentTick.bCanEverTick = true;
// InitializeComponent를 실행하기 위해 true로 설정
bWantsInitializeComponent = true;
Level = 1;
}
/ ... /
// 컴포넌트 초기화 함수
void UMyStatComponent::InitializeComponent()
{
Super::InitializeComponent();
SetLevel(Level);
}
void UMyStatComponent::SetLevel(int32 NewLevel)
{
auto MyGameInstance = Cast<UMyGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));
if(MyGameInstance)
{
// GameInstance의 data structor값을 가져와 statdata에 세팅한다.
auto StatData = MyGameInstance->GetStatData(NewLevel);
if (StatData)
{
Level = StatData->Level;
Hp = StatData->MaxHp;
Attack = StatData->Attack;
}
}
}
// Attack (충돌) 되었을 때 실행되는 함수
void UMyStatComponent::OnAttacked(float DamageAmount)
{
Hp -= DamageAmount;
if (Hp < 0)
Hp = 0;
UE_LOG(LogTemp, Warning, TEXT("OnAttacked %d"), Hp);
}
특별히 주목할 점이 있다면, InitializeComponent를 실행하기 위해서 bWantsInitializeComponent이라는 값을 true로 설정해주어야한다는 점이다.
// MyCharacter.h
UCLASS()
class UNREALINTRODUCTION_API AMyCharacter : public ACharacter
{
/.../
public:
// TakeDamage 함수 선언
virtual float TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override;
// Stat 컴포넌트 선언
UPROPERTY(VisibleAnywhere)
class UMyStatComponent* Stat;
}
Character header에서는 TakeDamage를 선언하는데 이미 언리얼 Actor 라이브러리에 포함되어있는 함수이다. 이 가상화되어있는 TakeDamage를 사용하기 나름으로 바꾸기 위해 재선언하는 것이다.
// MyCharacter.cpp
/.../
AMyCharacter::AMyCharacter()
{
/.../
// stat 컴포넌트를 찾아서 할당
Stat = CreateDefaultSubobject<UMyStatComponent>(TEXT("STAT"));
}
/.../
void AMyCharacter::AttackCheck()
{
/.../
if (bResult && HitResult.Actor.IsValid())
{
UE_LOG(LogTemp, Log, TEXT("Hit Actor : %s"), *HitResult.Actor->GetName());
// 충돌된 상대방 오브젝트로부터 TakeDamage를 실행한다.
FDamageEvent DamageEvent;
HitResult.Actor->TakeDamage(Stat->GetAttack(), DamageEvent, GetController(), this);
}
}
/.../
// 재정의한 TakeDamage
float AMyCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
Stat->OnAttacked(DamageAmount);
return DamageAmount;
}
AttackCheck함수 안의 TakeDamage는 이미 선언되어있는 Actor의 함수를 사용하는 것이지만, 밑에서 TakeDamage를 재정의하였기 때문에, 재정의된 TakeDamage의 내용으로 함수가 실행된다.
728x90
반응형
'개발 · 컴퓨터공학' 카테고리의 다른 글
Learning Unreal 4 언리얼 공부일지 - class 삭제 or 이름 바꾸기 (0) | 2022.02.05 |
---|---|
Learning Unreal 4 언리얼 공부일지 - Hp bar 만들기 (0) | 2022.02.02 |
Learning Unreal 4 언리얼 공부일지 - 충돌을 이용한 아이템 습득 (0) | 2022.01.29 |
Learning Unreal 4 언리얼 공부일지 - 언리얼의 소켓 (0) | 2022.01.28 |
Learning Unreal 4 언리얼 공부일지 - 충돌 감지 해보기 (0) | 2022.01.27 |