Unreal

[Unreal C++] 액터 컴포넌트 만들기

푸쿠이 2021. 5. 10. 16:06
참고 자료

'이득우의 언리얼 C++ 게임 개발의 정석' Chapter 11

&

docs.unrealengine.com/ko/ProgrammingAndScripting/ProgrammingWithCPP/UnrealArchitecture/Actors/index.html

 

액터 컴포넌트란?

계층 구조를 가지는 컴포넌트는 Scene Component이다.

기능만 가지는 컴포넌트는 Actor Component이다.

 

블루프린트 창에서 보면 이렇게 구분된다.

 

액터 컴포넌트 제작하기

이 게시글에서 제작할 액터 컴포넌트는 Character Stat을 관리하는 컴포넌트이다.

기능을 독립적으로 만들어야 관리하기에 쉽다.

 

Actor Component를 부모 클래스로 상속받아 클래스를 만든다.

클래스를 만들고 컴파일이 완료되면, 컴포넌트로써 추가할 수 있게 된다.

 

CharacterStatComponent.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "ArenaBattle.h"
#include "Components/ActorComponent.h"
#include "ABCharacterStatComponent.generated.h"

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class ARENABATTLE_API UABCharacterStatComponent : public UActorComponent
{
	GENERATED_BODY()

public:	
	UABCharacterStatComponent();

protected:
	virtual void BeginPlay() override;
	virtual void InitializeComponent() override;

public:	
	void SetNewLevel(int32 NewLevel);
	float GetAttack();
    
private:
	struct FABCharacterData* CurrentStatData = nullptr;

	UPROPERTY(EditInstanceOnly, Category = Stat, Meta = (AllowPrivateAccess = true))
	int32 Level;

	UPROPERTY(Transient, VisibleInstanceOnly, Category = Stat, Meta = (AllowPrivateAccess = true)) // Transient : 직렬화에서 제외.
	float CurrentHP;
		
};

CharacterStatComponent.cpp

이전 게시글에서 했던 GameInstance에 데이터 테이블 값을 저장해놓은 것을 사용했다.

2021.05.10 - [게임 엔진/[Unreal] Engine] - [Unreal C++] 데이터 테이블 만들고, 사용하기 (CSV 파일)

#include "ABCharacterStatComponent.h"

#include "ABGameInstance.h"
#include "CustomDataTables.h"

UABCharacterStatComponent::UABCharacterStatComponent()
{
	PrimaryComponentTick.bCanEverTick = false; // Tick 함수 안 쓰니까 false로.
	bWantsInitializeComponent = true; // 이걸 true해야 InitializeComponent 함수가 호출됨.

	Level = 1;
}

void UABCharacterStatComponent::BeginPlay()
{
	Super::BeginPlay();
}

void UABCharacterStatComponent::InitializeComponent()
{
	Super::InitializeComponent();
	SetNewLevel(Level);
}

void UABCharacterStatComponent::SetNewLevel(int32 NewLevel)
{
	UABGameInstance* ABGameInstance = Cast<UABGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));
	if (nullptr == ABGameInstance) return;

	CurrentStatData = ABGameInstance->GetABCharacterData(NewLevel);
	if (CurrentStatData)
	{
		Level = NewLevel;
		CurrentHP = CurrentStatData->MaxHP;
	}
	else
	{
		UE_LOG(LogClass, Warning, TEXT("Level %d data doesn't exist."), NewLevel);
	}
}

float UABCharacterStatComponent::GetAttack()
{
	if (nullptr == CurrentStatData) return 0.0f;

	return CurrentStatData->Attack;
}

 

이렇게 액터 컴포넌트에 기능을 구현했으면, 캐릭터에서 컴포넌트를 추가해서 사용하면 된다.

 

캐릭터 클래스

제작한 액터 컴포넌트를 추가해서 사용한다.

// 헤더
UPROPERTY(VisibleAnywhere, Category = Stat)
class UABCharacterStatComponent* CharacterStat;

// CPP
CharacterStat = CreateDefaultSubobject<UABCharacterStatComponent>(TEXT("CHARACTERSTAT"));

공격하는 곳에서 StatComponent의 Attack 함수를 통해 데미지를 받아올 수 있도록 구현한다.

if (bResult)
{
	if (HitResult.Actor.IsValid())
	{
		UE_LOG(LogClass, Warning, TEXT("Hit Actor Name : %s"), *HitResult.Actor->GetName());
			
		FDamageEvent DamageEvent;
		HitResult.Actor->TakeDamage(CharacterStat->GetAttack(), DamageEvent, GetController(), this);
	}
}