데이터 애셋 생성

<.h>

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

#pragma once

#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "ABItemData.generated.h"

//아이템 종류 열거형.
//블루프린트와 호환되도록 BlueprintType 지정.
UENUM(BlueprintType)
enum class EItemType : uint8
{
	Weapon = 0,
	Potion,
	Scroll
};

/**
 * 
 */
UCLASS()
class ARENABATTLEDEMO_API UABItemData : public UPrimaryDataAsset
{
	GENERATED_BODY()

public:
	//아이템 타입을 지정하는 열거형 변수.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Type)
	EItemType Type;
	
};

ItemData를 상속 받는 WeaponItemData 애셋 생성

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

#pragma once

#include "CoreMinimal.h"
#include "Item/ABItemData.h"
#include "ABWeaponItemData.generated.h"

/**
 * 
 */
UCLASS()
class ARENABATTLEDEMO_API UABWeaponItemData : public UABItemData
{
	GENERATED_BODY()

public:
	//제공할 무기에 대한 스켈레탈 메시.
	UPROPERTY(EditAnywhere, Category = Weapon)
	TSoftObjectPtr<class USkeletalMesh> WeaponMesh;
	
};

 

아이템 박스 객체 클래스 생성

<.h>

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ABItemBox.generated.h"

UCLASS()
class ARENABATTLEDEMO_API AABItemBox : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AABItemBox();

protected:
	//박스 컴포넌트의 오버랩 델리게이트에 등록할 함수.
	//OnComponentBeginOverlap 델리게이트는 다이나믹으로 지정되어 있기대문에
	//UFUNCTION() 매크로를 지정해야 한다.
	UFUNCTION()
	void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent,
		AActor* OtherActor,
		UPrimitiveComponent* OtherComp,
		int32 OtherBodyIndex,
		bool bFromSweep,
		const FHitResult& SweepResult);

	//파티클 재생 종료 시 발행되는 델리게이트에 등록 할 함수.
	UFUNCTION()
	void OnEffectFinished(class UParticleSystemComponent* PSystem );

protected:
	//액터의 충돌을 담당할 박스 컴포넌트.
	UPROPERTY(VisibleAnywhere, Category = Box)
	TObjectPtr<class UBoxComponent> TriggerBox;

	//아이템 박스를 보여줄 메시 컴포넌트.
	UPROPERTY(VisibleAnywhere, Category = Box)
	TObjectPtr<class UStaticMeshComponent> Mesh;

	//박스와 상호작용 했을때 보여줄 파티클 효과 컴포넌트.(나이아가라 X)
	UPROPERTY(VisibleAnywhere, Category = Box)
	TObjectPtr<class UParticleSystemComponent> Effect;

	//아이템 정보.
	UPROPERTY(EditAnywhere, Category = Item)
	TObjectPtr<class UABItemData> Item;
	
};

<.cpp>

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


#include "Item/ABItemBox.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Particles/ParticleSystemComponent.h"
#include "Physics/ABCollision.h"
#include "Interface/ABCharacterItemInterface.h"
#include "ABWeaponItemData.h"

// Sets default values
AABItemBox::AABItemBox()
{
	TriggerBox = CreateDefaultSubobject<UBoxComponent>(TEXT("TriggerBox"));
	Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
	Effect = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("Effect"));

	//계층설정.
	RootComponent = TriggerBox;
	Mesh->SetupAttachment(TriggerBox);
	Effect->SetupAttachment(TriggerBox);

	//콜리전 프로파일 설정.
	TriggerBox->SetCollisionProfileName(CPROFILE_ABTRIGGER);
	TriggerBox->SetBoxExtent(FVector(40.0f, 42.0f, 30.0f));
	//트리거가 발생하는 다이나믹 델리게이트에 함수 등록.
	TriggerBox->OnComponentBeginOverlap.AddDynamic(this, &AABItemBox::OnOverlapBegin);
	
	Mesh->SetCollisionProfileName(TEXT("NoCollision"));

	//애셋 로드.
	static ConstructorHelpers::FObjectFinder<UStaticMesh> BoxMeshRef(TEXT("/Game/ArenaBattle/Environment/Props/SM_Env_Breakables_Box1.SM_Env_Breakables_Box1"));
	if (BoxMeshRef.Object)
	{
		Mesh->SetStaticMesh(BoxMeshRef.Object);
	}
	//메시 컴포넌트의 위치 조정.
	Mesh->AddRelativeLocation(FVector(0.0f, -3.5f, -30.0f));

	//파티클 애셋 로드.
	static ConstructorHelpers::FObjectFinder<UParticleSystem> EffectRef(TEXT("/Game/ArenaBattle/Effect/P_TreasureChest_Open_Mesh.P_TreasureChest_Open_Mesh"));
	if (EffectRef.Object)
	{
		//파티클 애셋 설정.
		Effect->SetTemplate(EffectRef.Object);

		//바로 재생되지 않도록 설정.
		Effect->bAutoActivate = false;
	}

	//static ConstructorHelpers::FObjectFinder<UABItemData> ItemDataRef(TEXT("/Game/ArenaBattle/Item/Weapon/ABIW_Weapon.ABIW_Weapon"));
	static ConstructorHelpers::FObjectFinder<UABItemData> ItemDataRef(TEXT("/Game/ArenaBattle/Item/Weapon/ABIW_Weapon.ABIW_Weapon"));
	if (ItemDataRef.Object)
	{
		Item = ItemDataRef.Object;
	}
	
}

void AABItemBox::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
	UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	//꽝 상자도 있다고 가정.
	//Item이 Nullptr이면 꽝.
	if (!Item)
	{
		Destroy();
		return;
	}

	//아이템이 있으면 캐릭터에 아이템 획득 메시지 전달.
	IABCharacterItemInterface* OverlappedPawn = Cast<IABCharacterItemInterface>(OtherActor);
	if (OverlappedPawn)
	{
		OverlappedPawn->TakeItem(Item);
	}
	
	//파티클 재생.
	Effect->Activate();

	//매시는 안보이도록 처리(비활성화).
	Mesh->SetHiddenInGame(true);

	//액터의 콜리전 끄기.
	SetActorEnableCollision(false);

	//파티클 재생 종료시 발행되는 델리게이트에 함수 등록.
	Effect->OnSystemFinished.AddDynamic(this, &AABItemBox::OnEffectFinished);
}

void AABItemBox::OnEffectFinished(class UParticleSystemComponent* PSystem)
{
	//파티클 재생이 완료되면 액터 삭제.
	Destroy();
}

캐릭터Base클래스에 Item에 관한 코드 추가

	//ItemSection.
protected:

	//래퍼 구조체를 관리할수 있는 배열.
	UPROPERTY()
	TArray<FTakeItemDelegateWrapper> TakeItemActions;

	//아이템 획득시 호출될 함수.
	virtual void TakeItem(class UABItemData* InItemData) override;

	//아이템 종류마다 처리될 함수 선언.
	virtual void DrinkPortion(class UABItemData* InItemData);
	virtual void EquipWeapon(class UABItemData* InItemData);
	virtual void ReadScroll(class UABItemData* InItemData);

	//무기 아이템을 획득했을 때 사용할 스켈레탈 메시 컴포넌트.
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Equipent, meta = (AllowPrivateAccess = "true"))
	TObjectPtr<class USkeletalMeshComponent> Weapon;

생성자에서 델리게이트 함수등록 및 스켈레탈 메시 생성.

	//Item Section.
	TakeItemActions.Add(FTakeItemDelegateWrapper(FOnTakeItemDelegate::CreateUObject(this, &AABCharacterBase::EquipWeapon)));
	TakeItemActions.Add(FTakeItemDelegateWrapper(FOnTakeItemDelegate::CreateUObject(this, &AABCharacterBase::DrinkPortion)));
	TakeItemActions.Add(FTakeItemDelegateWrapper(FOnTakeItemDelegate::CreateUObject(this, &AABCharacterBase::ReadScroll)));

	//무기를 보여줄 스켈레탈 메쉬 생성.
	Weapon = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Weapon"));
	//메시 컴포넌트 하위로 계층을 설정하고, 이때 hand_rSocket 소켓에 부착.
	Weapon->SetupAttachment(GetMesh(), TEXT("hand_rSocket"));

아이템의 트리거 충돌시 TakeItem()함수 호출.

void AABCharacterBase::TakeItem(class UABItemData* InItemData)
{
	//아이템 정보가 넘어오면 처리.
	if (InItemData)
	{
		TakeItemActions[(uint8)InItemData->Type].ItemDelegate.ExecuteIfBound(InItemData);//C# : ?.Invoke()랑 같은 함수이다.
	}
}

void AABCharacterBase::DrinkPortion(class UABItemData* InItemData)
{
	UE_LOG(LogTemp, Log, TEXT("DrinkPortion"));
}

void AABCharacterBase::EquipWeapon(class UABItemData* InItemData)
{
	//UE_LOG(LogTemp, Log, TEXT("EquipWeapon"));
	//함수에 전달된 아이템 데이터 애셋을 무기 데이터로 변환.
	UABWeaponItemData* WeaponItemData = Cast<UABWeaponItemData>(InItemData);
	if (WeaponItemData)
	{
		//무기 메시가 아직 로딩 안된 경우 로드 처리.
		if (WeaponItemData->WeaponMesh.IsPending())
		{
			WeaponItemData->WeaponMesh.LoadSynchronous();	
		}
		//무기 컴포넌트에 로드가 완료된 스켈레탈 메시 설정.
		Weapon->SetSkeletalMesh(WeaponItemData->WeaponMesh.Get());
	}
}

void AABCharacterBase::ReadScroll(class UABItemData* InItemData)
{
	UE_LOG(LogTemp, Log, TEXT("ReadScroll"));
}

소프트 레퍼런싱 vs 하드 레퍼런싱

• 액터 로딩시 TObjectPtr로 선언한 언리얼 오브젝트도 따라서 메모리에 로딩됨.
• 이를 하드 레퍼런싱이라고 함.
• 게임 진행에 필수적인 언리얼 오브젝트는 이렇게 선언해도 되지만 아이템의 경우?
• 데이터 라이브러리에 1000종의 아이템 목록이 있을 때 이를 모두 다 로딩할 것인가?
• 필요한 데이터만 로딩하도록 TSoftObjectPtr로 선언하고 대신 애셋 주소 문자열을 지정함
• 필요시에 애셋을 로딩하도록 구현을 변경할 수 있으나 애셋 로딩 시간이 소요됨.
• 현재 게임에서 로딩되어 있는 스켈레탈 메시의 목록을 살펴보기

'언리얼 엔진 공부 > 언리얼C++' 카테고리의 다른 글

캐릭터 공격 판정  (0) 2025.04.18
캐릭터 애니메이션 시스템  (0) 2025.04.14
데이터 애셋  (0) 2025.04.14
언리얼 엔진 게임 제작 기초  (0) 2025.04.10
어설션(Assertion)  (0) 2025.04.08

+ Recent posts