
데이터 애셋 생성
<.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 |

































