Unreal Engine, Item Fragment 코드 작성

Item 코드 살펴보기 전 cpp 코드들을 작성하도록 한다.


D1Define.h 전체 코드

해당 경로의 D1Defind.h 파일을 열고 편집하도록 한다.

Project
└─Source
   └─LyraGame
      └─D1Defind.h (*)

이전 시간에 작성한 D1Define.h 에서 사용한 스킨, 아머타입 이외의 코드를 이어서 작성해주도록 한다.

#pragma once

UENUM(BlueprintType)
enum class ESlotState : uint8
{
	Default,
	Invalid,
	Valid
};

UENUM(BlueprintType)
enum class EEquipmentSlotType : uint8
{
	Unarmed_LeftHand,
	Unarmed_RightHand,

	Primary_LeftHand,
	Primary_RightHand,
	Primary_TwoHand,

	Secondary_LeftHand,
	Secondary_RightHand,
	Secondary_TwoHand,

	Utility_Primary,
	Utility_Secondary,
	Utility_Tertiary,
	Utility_Quaternary,

	Helmet,
	Chest,
	Legs,
	Hands,
	Foot,

	Count UMETA(Hidden)
};

UENUM(BlueprintType)
enum class EEquipState : uint8
{
	Unarmed,

	Weapon_Primary,
	Weapon_Secondary,

	Utility_Primary,
	Utility_Secondary,
	Utility_Tertiary,
	Utility_Quaternary,

	Count UMETA(Hidden)
};

UENUM(BlueprintType)
enum class EEquipmentType : uint8
{
	Armor, 
	Weapon,
	Utility,

	Count UMETA(Hidden)
};

UENUM(BlueprintType)
enum class EArmorType : uint8
{
	Helmet,
	Chest,
	Legs,
	Hands,
	Foot,

	Count UMETA(Hidden)
};

UENUM(BlueprintType)
enum class EWeaponType : uint8
{
	Unarmed,
	OneHandSword,
	TwoHandSword,
	GreatSword,
	Shield,
	Staff,
	Bow,

	Count UMETA(Hidden)
};

UENUM(BlueprintType)
enum class EWeaponSlotType : uint8
{
	Primary,
	Secondary,

	Count UMETA(Hidden)
};

UENUM(BlueprintType)
enum class EWeaponHandType : uint8
{
	LeftHand,
	RightHand,
	TwoHand,

	Count UMETA(Hidden)
};

UENUM(BlueprintType)
enum class EUtilityType : uint8
{
	Drink,
	LightSource,

	Count UMETA(Hidden)
};

UENUM(BlueprintType)
enum class EUtilitySlotType : uint8
{
	Primary,
	Secondary,
	Tertiary,
	Quaternary,

	Count UMETA(Hidden)
};

UENUM(BlueprintType)
enum class EItemRarity : uint8
{
	Poor,
	Common,
	Uncommon,
	Rare,
	Legendary,

	Count UMETA(Hidden)
};

UENUM(BlueprintType)
enum class ESpellType : uint8
{
	None,
	Projectile,
	AOE,

	Count UMETA(Hidden)
};

UENUM(BlueprintType)
enum class EOverlayTargetType : uint8
{
	None,
	Weapon,
	Character,
	ALL,
};

UENUM(BlueprintType)
enum class ECharacterSkinType : uint8
{
	Asian,
	Black,

	Count UMETA(Hidden)
};

UENUM(BlueprintType, meta=(Bitflags))
enum class ECharacterClassType : uint8
{
	Fighter,
	Swordmaster,
	Barbarian,
	Wizard,
	Archer,

	Count UMETA(Hidden)
};

UENUM(BlueprintType)
enum class ED1TeamID : uint8
{
	NoTeam,
	Monster = 1,
};

LyraAssetManager.h 파일 편집하기

해당 경로의 LyraAssetManager 파일을 수정하도록 한다.

Project
└─Source
   └─LyraGames
      └─System
         ├─LyraAssetManager.cpp (*)
         └─LyraAssetManager.h (*)

.h 헤더 코드에서 글로벌 클래스와 class ULyraAssetManager : public UAssetManager 함수 아래의 UD1ItemInstance 를 추가하고, 환경 변수도 가르키도록 Config 를 코드도 추가한다.

LyraAssetManager.h

class UD1ItemData;

class ULyraAssetManager : public UAssetManager
{
public:
    ...
    const UD1ItemData& GetItemData();
    ...
    ...
    UPROPERTY(Config)
    TSoftObjectPtr<UD1ItemData> ItemDataPath;
    ...    
}

코드 .cpp 에서는 DD1ItemData 에셋 경로를 가르키도록하고 GetItemInstance 코드도 추가한다.

LyraAssetManager.cpp

#include "Data/D1ItemData.h"
...
...
const UD1ItemData& ULyraAssetManager::GetItemData()
{
	return GetOrLoadTypedGameData<UD1ItemData>(ItemDataPath);
}


GamePlayTag 추가하기

기존 GamePlayTagStack 대신 사용할 D1GamePlayTagStack를 추가하도록 한다.

D1GameplayTagStack 경로

Project
└─Source
   └─D1Games
      └─System
         ├─D1GameplayTagStack.cpp (+)
         └─D1GameplayTagStack.h (+)

D1GameplayTagStack.h

#pragma once

#include "GameplayTagContainer.h"
#include "Net/Serialization/FastArraySerializer.h"
#include "D1GameplayTagStack.generated.h"

struct FD1GameplayTagStackContainer;
struct FNetDeltaSerializeInfo;

USTRUCT(BlueprintType)
struct FD1GameplayTagStack : public FFastArraySerializerItem
{
	GENERATED_BODY()

public:
	FD1GameplayTagStack() {}

	FD1GameplayTagStack(FGameplayTag InTag, int32 InStackCount)
		: Tag(InTag)
		, StackCount(InStackCount) { }

public:
	const FGameplayTag& GetStackTag() const { return Tag; }
	int32 GetStackCount() const { return StackCount; }

	FString GetDebugString() const;

private:
	friend FD1GameplayTagStackContainer;

	UPROPERTY()
	FGameplayTag Tag;

	UPROPERTY()
	int32 StackCount = 0;
};

USTRUCT(BlueprintType)
struct FD1GameplayTagStackContainer : public FFastArraySerializer
{
	GENERATED_BODY()

public:
	FD1GameplayTagStackContainer() { }

public:
	void AddStack(FGameplayTag Tag, int32 StackCount);
	void RemoveStack(FGameplayTag Tag);

public:
	const TArray<FD1GameplayTagStack>& GetStacks() const { return Stacks; }

	int32 GetStackCount(FGameplayTag Tag) const { return TagToCountMap.FindRef(Tag); }
	bool ContainsTag(FGameplayTag Tag) const { return TagToCountMap.Contains(Tag); }

	void PreReplicatedRemove(const TArrayView<int32> RemovedIndices, int32 FinalSize);
	void PostReplicatedAdd(const TArrayView<int32> AddedIndices, int32 FinalSize);
	void PostReplicatedChange(const TArrayView<int32> ChangedIndices, int32 FinalSize);

	bool NetDeltaSerialize(FNetDeltaSerializeInfo& DeltaParams)
	{
		return FFastArraySerializer::FastArrayDeltaSerialize<FD1GameplayTagStack, FD1GameplayTagStackContainer>(Stacks, DeltaParams, *this);
	}

private:
	UPROPERTY()
	TArray<FD1GameplayTagStack> Stacks;

	UPROPERTY(NotReplicated)
	TMap<FGameplayTag, int32> TagToCountMap;
};

template<>
struct TStructOpsTypeTraits<FD1GameplayTagStackContainer> : public TStructOpsTypeTraitsBase2<FD1GameplayTagStackContainer>
{
	enum
	{
		WithNetDeltaSerializer = true,
	};
};

D1GameplayTagStack.cpp

#include "D1GameplayTagStack.h"

#include "UObject/Stack.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(D1GameplayTagStack)

FString FD1GameplayTagStack::GetDebugString() const
{
	return FString::Printf(TEXT("%sx%d"), *Tag.ToString(), StackCount);
}

void FD1GameplayTagStackContainer::AddStack(FGameplayTag Tag, int32 StackCount)
{
	if (!Tag.IsValid())
	{
		FFrame::KismetExecutionMessage(TEXT("An invalid tag was passed to AddStack"), ELogVerbosity::Warning);
		return;
	}

	for (FD1GameplayTagStack& Stack : Stacks)
	{
		if (Stack.Tag == Tag)
		{
			const int32 NewCount = Stack.StackCount + StackCount;
			Stack.StackCount = NewCount;
			TagToCountMap[Tag] = NewCount;
			MarkItemDirty(Stack);
			return;
		}
	}

	FD1GameplayTagStack& NewStack = Stacks.Emplace_GetRef(Tag, StackCount);
	MarkItemDirty(NewStack);
	TagToCountMap.Add(Tag, StackCount);
}

void FD1GameplayTagStackContainer::RemoveStack(FGameplayTag Tag)
{
	if (!Tag.IsValid())
	{
		FFrame::KismetExecutionMessage(TEXT("An invalid tag was passed to RemoveStack"), ELogVerbosity::Warning);
		return;
	}

	for (auto It = Stacks.CreateIterator(); It; ++It)
	{
		FD1GameplayTagStack& Stack = *It;
		if (Stack.Tag == Tag)
		{
			It.RemoveCurrent();
			TagToCountMap.Remove(Tag);
			MarkArrayDirty();
			return;
		}
	}
}

void FD1GameplayTagStackContainer::PreReplicatedRemove(const TArrayView<int32> RemovedIndices, int32 FinalSize)
{
	for (int32 Index : RemovedIndices)
	{
		const FGameplayTag Tag = Stacks[Index].Tag;
		TagToCountMap.Remove(Tag);
	}
}

void FD1GameplayTagStackContainer::PostReplicatedAdd(const TArrayView<int32> AddedIndices, int32 FinalSize)
{
	for (int32 Index : AddedIndices)
	{
		const FD1GameplayTagStack& Stack = Stacks[Index];
		TagToCountMap.Add(Stack.Tag, Stack.StackCount);
	}
}

void FD1GameplayTagStackContainer::PostReplicatedChange(const TArrayView<int32> ChangedIndices, int32 FinalSize)
{
	for (int32 Index : ChangedIndices)
	{
		const FD1GameplayTagStack& Stack = Stacks[Index];
		TagToCountMap[Stack.Tag] = Stack.StackCount;
	}
}

Data 폴더 관련 코드 작성하기

다음 경로의 코드를 작성하도록 한다.

Project
└─Source
  └─D1Game
    └─Data
      ├─D1ItemData.cpp (+)
      └─D1ItemData.h (+)

D1ItemData.h

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

#pragma once

#include "D1ItemData.generated.h"

class UD1ItemTemplate;

UCLASS(BlueprintType, Const, meta = (DisplayName = "D1 Item Data"))
class UD1ItemData : public UPrimaryDataAsset
{
	GENERATED_BODY()

public:
	static const UD1ItemData& Get();

public:
#if WITH_EDITORONLY_DATA
	virtual void PreSave(FObjectPreSaveContext SaveContext) override;
#endif // WITH_EDITORONLY_DATA

#if WITH_EDITOR
	virtual EDataValidationResult IsDataValid(FDataValidationContext& Context) const override;
#endif // WITH_EDITOR

public:
	const UD1ItemTemplate& FindItemTemplateByID(int32 ItemTemplateID) const;
	int32 FindItemTemplateIDByClass(TSubclassOf<UD1ItemTemplate> ItemTemplateClass) const;

	void GetAllItemTemplateClasses(TArray<TSubclassOf<UD1ItemTemplate>>& OutItemTemplateClasses) const;
	const TArray<TSubclassOf<UD1ItemTemplate>>& GetWeaponItemTemplateClasses() const { return WeaponItemTemplateClasses; }
	const TArray<TSubclassOf<UD1ItemTemplate>>& GetArmorItemTemplateClasses() const { return ArmorItemTemplateClasses;  }

private:
	UPROPERTY(EditDefaultsOnly)
	TMap<int32, TSubclassOf<UD1ItemTemplate>> ItemTemplateIDToClass;

	UPROPERTY()
	TMap<TSubclassOf<UD1ItemTemplate>, int32> ItemTemplateClassToID;

private:
	UPROPERTY()
	TArray<TSubclassOf<UD1ItemTemplate>> WeaponItemTemplateClasses;

	UPROPERTY()
	TArray<TSubclassOf<UD1ItemTemplate>> ArmorItemTemplateClasses;
};

D1ItemData.cpp

#include "D1ItemData.h"

#if WITH_EDITOR
#include "Misc/DataValidation.h"
#endif // WITH_EDITOR

#include "Item/D1ItemTemplate.h"
#include "Item/Fragments/D1ItemFragment_Equipable_Armor.h"
#include "Item/Fragments/D1ItemFragment_Equipable_Weapon.h"
#include "System/LyraAssetManager.h"
#include "UObject/ObjectSaveContext.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(D1ItemData)

const UD1ItemData& UD1ItemData::Get()
{
	return ULyraAssetManager::Get().GetItemData();
}

#if WITH_EDITORONLY_DATA
void UD1ItemData::PreSave(FObjectPreSaveContext SaveContext)
{
	Super::PreSave(SaveContext);

	ItemTemplateIDToClass.KeySort([](const int32 A, const int32 B)
		{
			return A < B;
		});

	ItemTemplateClassToID.Empty();
	WeaponItemTemplateClasses.Empty();
	ArmorItemTemplateClasses.Empty();

	for (const auto& Pair : ItemTemplateIDToClass)
	{
		ItemTemplateClassToID.Emplace(Pair.Value, Pair.Key);

		const UD1ItemTemplate* ItemTemplate = Pair.Value.GetDefaultObject();
		if (const UD1ItemFragment_Equipable_Weapon* WeaponFragment = ItemTemplate->FindFragmentByClass<UD1ItemFragment_Equipable_Weapon>())
		{
			if (WeaponFragment->WeaponType != EWeaponType::Unarmed)
			{
				WeaponItemTemplateClasses.Add(Pair.Value);
			}
		}
		else if (ItemTemplate->FindFragmentByClass<UD1ItemFragment_Equipable_Armor>())
		{
			ArmorItemTemplateClasses.Add(Pair.Value);
		}
	}
}
#endif // WITH_EDITORONLY_DATA

#if WITH_EDITOR
EDataValidationResult UD1ItemData::IsDataValid(FDataValidationContext& Context) const
{
	EDataValidationResult Result = Super::IsDataValid(Context);

	TSet<int32> ItemTemplateIDSet;
	TSet<TSubclassOf<UD1ItemTemplate>> ItemTemplateClassSet;

	for (const auto& Pair : ItemTemplateIDToClass)
	{
		// ID Check
		const int32 ItemTemplateID = Pair.Key;

		if (ItemTemplateID <= 0)
		{
			Context.AddError(FText::FromString(FString::Printf(TEXT("Invalid ID : [ID : %d]\n"), ItemTemplateID)));
			Result = EDataValidationResult::Invalid;
		}

		if (ItemTemplateIDSet.Contains(ItemTemplateID))
		{
			Context.AddError(FText::FromString(FString::Printf(TEXT("Duplicated ID : [ID : %d]\n"), ItemTemplateID)));
			Result = EDataValidationResult::Invalid;
		}

		ItemTemplateIDSet.Add(ItemTemplateID);

		// Class Check
		const TSubclassOf<UD1ItemTemplate> ItemTemplateClass = Pair.Value;

		if (ItemTemplateClass == nullptr)
		{
			Context.AddError(FText::FromString(FString::Printf(TEXT("Invalid Class : [ID : %d]\n"), ItemTemplateID)));
			Result = EDataValidationResult::Invalid;
		}

		if (ItemTemplateClassSet.Contains(ItemTemplateClass))
		{
			Context.AddError(FText::FromString(FString::Printf(TEXT("Duplicated Class : [ID : %d]\n"), ItemTemplateID)));
			Result = EDataValidationResult::Invalid;
		}

		ItemTemplateClassSet.Add(ItemTemplateClass);
	}
	return Result;
}
#endif // WITH_EDITOR

const UD1ItemTemplate& UD1ItemData::FindItemTemplateByID(int32 ItemTemplateID) const
{
	const TSubclassOf<UD1ItemTemplate>* ItemTemplateClass = ItemTemplateIDToClass.Find(ItemTemplateID);
	ensureAlwaysMsgf(ItemTemplateClass, TEXT("Can't find ItemTemplateClass from ID [%d]"), ItemTemplateID);
	return *(ItemTemplateClass->GetDefaultObject());
}

int32 UD1ItemData::FindItemTemplateIDByClass(TSubclassOf<UD1ItemTemplate> ItemTemplateClass) const
{
	const int32* ItemTemplateID = ItemTemplateClassToID.Find(ItemTemplateClass);
	ensureAlwaysMsgf(ItemTemplateID, TEXT("Can't find ItemTemplateID from Class"));
	return *ItemTemplateID;
}

void UD1ItemData::GetAllItemTemplateClasses(TArray<TSubclassOf<UD1ItemTemplate>>& OutItemTemplateClasses) const
{
	OutItemTemplateClasses.Reset();
	OutItemTemplateClasses.Reserve(ItemTemplateIDToClass.Num());

	for (auto& Pair : ItemTemplateIDToClass)
	{
		OutItemTemplateClasses.Add(Pair.Value);
	}
}

Item 폴더 관련 코드 작성하기

파일 분량이 많다. Managers 는 D1CosmeticManager 를 작성할 때 사용한 폴더이다.

Project
└─Source
  └─LyraGame
    └─Item
      ├─Fragments
      │  ├─D1ItemFragment_Equipable.cpp
      │  ├─D1ItemFragment_Equipable.h
      │  ├─D1ItemFragment_Equipable_Armor.cpp
      │  ├─D1ItemFragment_Equipable_Armor.h
      │  ├─D1ItemFragment_Equipable_Attachment.cpp
      │  ├─D1ItemFragment_Equipable_Attachment.h
      │  ├─D1ItemFragment_Equipable_Utility.cpp
      │  ├─D1ItemFragment_Equipable_Utility.h
      │  ├─D1ItemFragment_Equipable_Weapon.cpp
      │  └─D1ItemFragment_Equipable_Weapon.h
      ├─Managers
      ├─D1ItemInstance.cpp (+)
      ├─D1ItemInstance.h (+)
      ├─D1ItemTemplate.cpp (+)
      └─D1ItemTemplate.h (+)

D1ItemTemplate.h

#pragma once

#include "D1ItemTemplate.generated.h"

class UD1ItemInstance;

UCLASS(DefaultToInstanced, EditInlineNew, Abstract)
class UD1ItemFragment : public UObject
{
	GENERATED_BODY()

public:
	virtual void OnInstanceCreated(UD1ItemInstance* Instance) const { }
};

UCLASS(Blueprintable, Const, Abstract)
class UD1ItemTemplate : public UObject
{
	GENERATED_BODY()

public:
	UD1ItemTemplate(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

protected:
#if WITH_EDITOR
	virtual EDataValidationResult IsDataValid(FDataValidationContext& Context) const override;
#endif // WITH_EDITOR

public:
	UFUNCTION(BlueprintCallable, BlueprintPure="false", meta=(DeterminesOutputType="FragmentClass"))
	const UD1ItemFragment* FindFragmentByClass(TSubclassOf<UD1ItemFragment> FragmentClass) const;

	template <typename FragmentClass>
	const FragmentClass* FindFragmentByClass() const
	{
		return (FragmentClass*)FindFragmentByClass(FragmentClass::StaticClass());
	}

public:
	UPROPERTY(EditDefaultsOnly)
	FIntPoint SlotCount = FIntPoint::ZeroValue;

	UPROPERTY(EditDefaultsOnly)
	int32 MaxStackCount = 1;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	FText DisplayName;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	FText Description;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	TObjectPtr<UTexture2D> IconTexture;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	TSoftObjectPtr<UStaticMesh> PickupableMesh;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Instanced)
	TArray<TObjectPtr<UD1ItemFragment>> Fragments;
};

D1ItemTemplate.cpp

#include "D1ItemTemplate.h"

#if WITH_EDITOR
#include "Misc/DataValidation.h"
#endif // WITH_EDITOR

#include "Fragments/D1ItemFragment_Equipable.h"
#include "Fragments/D1ItemFragment_Equipable_Armor.h"
#include "Fragments/D1ItemFragment_Equipable_Utility.h"
#include "Fragments/D1ItemFragment_Equipable_Weapon.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(D1ItemTemplate)

UD1ItemTemplate::UD1ItemTemplate(const FObjectInitializer& ObjectInitializer)
	:Super(ObjectInitializer)
{

}

#if WITH_EDITOR
EDataValidationResult UD1ItemTemplate::IsDataValid(FDataValidationContext& Context) const
{
	EDataValidationResult Result = UObject::IsDataValid(Context);

	if (SlotCount.X < 1 || SlotCount.Y < 1)
	{
		Context.AddError(FText::FromString(FString::Printf(TEXT("SlotCount is less than 1"))));
		Result = EDataValidationResult::Invalid;
	}

	if (MaxStackCount < 1)
	{
		Context.AddError(FText::FromString(FString::Printf(TEXT("MaxStackCount is less than 1"))));
		Result = EDataValidationResult::Invalid;
	}

	const UD1ItemFragment_Equipable* FoundEquipable = nullptr;
	for (UD1ItemFragment* Fragment : Fragments)
	{
		if (UD1ItemFragment_Equipable* CurrentEquippable = Cast<UD1ItemFragment_Equipable>(Fragment))
		{
			if (FoundEquipable)
			{
				Context.AddError(FText::FromString(FString::Printf(TEXT("Deuplicated Equippable Fragment"))));
				return EDataValidationResult::Invalid;
			}

			FoundEquipable = CurrentEquippable;
		}
	}

	if (FoundEquipable)
	{
		if (FoundEquipable->EquipmentType == EEquipmentType::Count)
		{
			Context.AddError(FText::FromString(FString::Printf(TEXT("Eqipment Type is Invalid : [EEquipmentType::Count]"))));
			return EDataValidationResult::Invalid;
		}

		if (FoundEquipable->EquipmentType == EEquipmentType::Armor)
		{
			const UD1ItemFragment_Equipable_Armor* ArmorFragment = Cast<UD1ItemFragment_Equipable_Armor>(FoundEquipable);

			if (ArmorFragment->ArmorType == EArmorType::Count)
			{
				Context.AddError(FText::FromString(FString::Printf(TEXT("Armor Type is Invalid : [EArmorType::Count]"))));
				Result = EDataValidationResult::Invalid;
			}
		}
		else if (FoundEquipable->EquipmentType == EEquipmentType::Weapon)
		{
			const UD1ItemFragment_Equipable_Weapon* WeaponFragment = Cast<UD1ItemFragment_Equipable_Weapon>(FoundEquipable);

			if (WeaponFragment->WeaponType == EWeaponType::Count)
			{
				Context.AddError(FText::FromString(FString::Printf(TEXT("Weapon Type is Invalid : [EWeaponType::Count]"))));
				Result = EDataValidationResult::Invalid;
			}

			if (WeaponFragment->WeaponHandType == EWeaponHandType::Count)
			{
				Context.AddError(FText::FromString(FString::Printf(TEXT("Weapon Hand Type is Invalid : [EWeaponHandType::Count]"))));
				Result = EDataValidationResult::Invalid;
			}
		}
		else if (FoundEquipable->EquipmentType == EEquipmentType::Utility)
		{
			const UD1ItemFragment_Equipable_Utility* UtilityFragment = Cast<UD1ItemFragment_Equipable_Utility>(FoundEquipable);

			if (UtilityFragment->UtilityType == EUtilityType::Count)
			{
				Context.AddError(FText::FromString(FString::Printf(TEXT("UTILITY Type is Invalid : [EUtilityType::Count]"))));
				Result = EDataValidationResult::Invalid;
			}
		}

		if (FoundEquipable->EquipmentType == EEquipmentType::Armor || FoundEquipable->EquipmentType == EEquipmentType::Weapon)
		{
			if (MaxStackCount != 1)
			{
				Context.AddError(FText::FromString(FString::Printf(TEXT("Armor or Weapon Type must have MaxStackCount of 1: [MaxStackCount != 1]"))));
				Result = EDataValidationResult::Invalid;
			}
		}
	}

	return Result;
}
#endif //WITH_EDITOR


const UD1ItemFragment* UD1ItemTemplate::FindFragmentByClass(TSubclassOf<UD1ItemFragment> FragmentClass) const
{
	if (FragmentClass)
	{
		for (UD1ItemFragment* Fragment : Fragments)
		{
			if (Fragment && Fragment->IsA(FragmentClass))
			{
				return Fragment;
			}
		}
	}
	return nullptr;
}

D1ItemInstance.h

#pragma once

#include "D1Define.h"
#include "AbilitySystem/LyraAbilitySourceInterface.h"
#include "System/D1GameplayTagStack.h"
#include "D1ItemInstance.generated.h"

USTRUCT(BlueprintType)
struct FD1ItemRarityProbability
{
	GENERATED_BODY()

public:
	UPROPERTY(VisibleAnywhere)
	EItemRarity Rarity = EItemRarity::Poor;
	
	UPROPERTY(EditAnyWhere)
	float Probability = 0;
};

UCLASS(BlueprintType)
class UD1ItemInstance : public UObject, public ILyraAbilitySourceInterface
{
	GENERATED_BODY()

public:
	UD1ItemInstance(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

public:
	virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
	virtual bool IsSupportedForNetworking() const override { return true; };
	virtual float GetDistanceAttenuation(float Distance, const FGameplayTagContainer* SourceTags = nullptr, const FGameplayTagContainer* TargetTags = nullptr) const override;
	virtual float GetPhysicalMaterialAttenuation(const UPhysicalMaterial* PhysicalMaterial, const FGameplayTagContainer* SourceTags = nullptr, const FGameplayTagContainer* TargetTags = nullptr) const override;

public:
	void Init(int32 InItemTemplateID, EItemRarity InItemRarity);

public:
	UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly)
	void AddStatTagStack(FGameplayTag StatTag, int32 StackCount);

	UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly)
	void RemoveStatTagStack(FGameplayTag StatTag);

public:
	static EItemRarity DetermineItemRarity(const TArray<FD1ItemRarityProbability>& ItemProbabilities);
	
public:
	UFUNCTION(BlueprintCallable, BlueprintPure)
	int32 GetItemTemplateID() const { return ItemTemplateID; }

	UFUNCTION(BlueprintCallable, BlueprintPure)
	EItemRarity GetItemRarity() const { return ItemRarity;  }

	UFUNCTION(BlueprintCallable, BlueprintPure)
	bool HasStatTag(FGameplayTag StatTag) const;

	UFUNCTION(BlueprintCallable, BlueprintPure)
	int32 GetStackCountByTag(FGameplayTag StatTag) const;

	UFUNCTION(BlueprintCallable, BlueprintPure)
	const FD1GameplayTagStackContainer& GetStatContainer() const { return StatContainer;  }

public:
	UFUNCTION(BlueprintCallable, BlueprintPure="false", meta=(DeterminesOutputType="FragmentClass"))
	const UD1ItemFragment* FindFragmentByClass(TSubclassOf<UD1ItemFragment> FragmentClass) const;

	template <typename FragmentClass>
	const FragmentClass* FindFragmentByClass() const
	{
		return (FragmentClass*)FindFragmentByClass(FragmentClass::StaticClass());
	}

private:
#if UE_WITH_IRIS
	virtual void RegisterReplicationFragments(UE::Net::FFragmentRegistrationContext& Context, UE::Net::EFragmentRegistrationFlags RegistrationFlags) override;
#endif // UE_WITH_IRIS

private:
	UPROPERTY(Replicated)
	int32 ItemTemplateID = INDEX_NONE;

	UPROPERTY(Replicated)
	EItemRarity ItemRarity = EItemRarity::Poor;

	UPROPERTY(Replicated)
	FD1GameplayTagStackContainer StatContainer;
};

D1ItemInstance.cpp

#include "D1ItemInstance.h"

#if UE_WITH_IRIS
#include "iris/ReplicationSystem/ReplicationFragmentUtil.h"
#endif // UE_WITH_IRIS

#include "D1ItemTemplate.h"
#include "LyraLogChannels.h"
#include "Data/D1ItemData.h"
#include "Net/UnrealNetwork.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(D1ItemInstance)

UD1ItemInstance::UD1ItemInstance(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{

}

void UD1ItemInstance::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(ThisClass, ItemTemplateID);
	DOREPLIFETIME(ThisClass, ItemRarity);
	DOREPLIFETIME(ThisClass, StatContainer);
}

float UD1ItemInstance::GetDistanceAttenuation(float Distance, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags) const
{
	return 0;
}

float UD1ItemInstance::GetPhysicalMaterialAttenuation(const UPhysicalMaterial* PhysicalMaterial, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags) const
{
	return 0;
}

void UD1ItemInstance::Init(int32 InItemTemplateID, EItemRarity InItemRarity)
{
	if (InItemTemplateID <= INDEX_NONE || InItemRarity == EItemRarity::Count)
	{
		return;
	}

	ItemTemplateID = InItemTemplateID;
	ItemRarity = InItemRarity;

	const UD1ItemTemplate& ItemTemplate = UD1ItemData::Get().FindItemTemplateByID(ItemTemplateID);
	for (const UD1ItemFragment* Fragment : ItemTemplate.Fragments)
	{
		if (Fragment)
		{
			Fragment->OnInstanceCreated(this);
		}
	}
}

#if UE_WITH_IRIS
void UD1ItemInstance::RegisterReplicationFragments(UE::Net::FFragmentRegistrationContext& Context, UE::Net::EFragmentRegistrationFlags RegistrationFlags)
{
	using namespace UE::Net;
	FReplicationFragmentUtil::CreateAndRegisterFragmentsForObject(this, Context, RegistrationFlags);
}
#endif // UE_WITH_IRIS

void UD1ItemInstance::AddStatTagStack(FGameplayTag StatTag, int32 StackCount)
{
	StatContainer.AddStack(StatTag, StackCount);
}

void UD1ItemInstance::RemoveStatTagStack(FGameplayTag StatTag)
{
	StatContainer.RemoveStack(StatTag);
}

EItemRarity UD1ItemInstance::DetermineItemRarity(const TArray<FD1ItemRarityProbability>& ItemProbabilities)
{
	float TotalProbability = 0.f;
	for (const FD1ItemRarityProbability& ItemProbability : ItemProbabilities)
	{
		TotalProbability += ItemProbability.Probability;
	}

	if (TotalProbability > 100.f)
	{
		return EItemRarity::Count;
	}

	float SumProbability = 0.f;
	float RandomValue = FMath::RandRange(0.f, 100.f);

	for (const FD1ItemRarityProbability& ItemProbability : ItemProbabilities)
	{
		SumProbability += ItemProbability.Probability;
		if (RandomValue < SumProbability)
		{
			return ItemProbability.Rarity;
		}
	}

	return EItemRarity::Count;
}

bool UD1ItemInstance::HasStatTag(FGameplayTag StatTag) const
{
	return StatContainer.ContainsTag(StatTag);
}

int32 UD1ItemInstance::GetStackCountByTag(FGameplayTag StatTag) const
{
	return StatContainer.GetStackCount(StatTag);
}

const UD1ItemFragment* UD1ItemInstance::FindFragmentByClass(TSubclassOf<UD1ItemFragment> FragmentClass) const
{
	if (ItemTemplateID > INDEX_NONE && FragmentClass)
	{
		const UD1ItemTemplate& ItemTemplate = UD1ItemData::Get().FindItemTemplateByID(ItemTemplateID);
		return ItemTemplate.FindFragmentByClass(FragmentClass);
	}
	return nullptr;
}

Fragment 폴더 편집하기

Project
└─Source
  └─LyraGame
    └─Item
      ├─Fragments (+)
      │  ├─D1ItemFragment_Equipable.cpp (+)
      │  ├─D1ItemFragment_Equipable.h (+)
      │  ├─D1ItemFragment_Equipable_Armor.cpp (+)
      │  ├─D1ItemFragment_Equipable_Armor.h (+)
      │  ├─D1ItemFragment_Equipable_Attachment.cpp (+)
      │  ├─D1ItemFragment_Equipable_Attachment.h (+)
      │  ├─D1ItemFragment_Equipable_Utility.cpp (+)
      │  ├─D1ItemFragment_Equipable_Utility.h (+)
      │  ├─D1ItemFragment_Equipable_Weapon.cpp (+)
      │  └─D1ItemFragment_Equipable_Weapon.h (+)
      ├─Managers
      ├─D1ItemInstance.cpp
      ├─D1ItemInstance.h
      ├─D1ItemTemplate.cpp
      └─D1ItemTemplate.h

D1ItemFragment_Equipable.h

#pragma once

#include "D1Define.h"
#include "GameplayTags.h"
#include "Item/D1ItemTemplate.h"
#include "D1ItemFragment_Equipable.generated.h"

class ULyraAbilitySet;
class UD1ItemInstance;

USTRUCT(BlueprintType)
struct FRarityStat
{
	GENERATED_BODY()

public:
	UPROPERTY(VisibleDefaultsOnly)
	EItemRarity Rarity = EItemRarity::Poor;

	UPROPERTY(EditDefaultsOnly)
	int32 Value = 0;
};

USTRUCT(BlueprintType)
struct FRarityStatSet
{
	GENERATED_BODY()

public:
	FRarityStatSet();

public:
	UPROPERTY(EditDefaultsOnly, meta=(Categories="SetByCaller"))
	FGameplayTag StatTag;

	UPROPERTY(EditDefaultsOnly, EditFixedSize)
	TArray<FRarityStat> RarityStats;
};

USTRUCT(BlueprintType)
struct FRarityStatRange
{
	GENERATED_BODY()

public:
	UPROPERTY(VisibleDefaultsOnly)
	EItemRarity Rarity = EItemRarity::Poor;

	UPROPERTY(EditDefaultsOnly)
	int32 MinValue = 0;

	UPROPERTY(EditDefaultsOnly)
	int32 MaxValue = 0;
};

USTRUCT(BlueprintType)
struct FRarityStatRangeSet
{
	GENERATED_BODY()

public:
	FRarityStatRangeSet();

public:
	UPROPERTY(EditDefaultsOnly, meta=(Categories="SetByCaller"))
	FGameplayTag StatTag;

	UPROPERTY(EditDefaultsOnly, EditFixedSize)
	TArray<FRarityStatRange> RarityStatRanges;
};

UCLASS(Abstract, Const)
class UD1ItemFragment_Equipable : public UD1ItemFragment
{
	GENERATED_BODY()

public:
	UD1ItemFragment_Equipable(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

protected:
	void AddStatTagStack(UD1ItemInstance* ItemInstance, const TArray<FRarityStatSet>& RarityStatSets) const;
	void AddStatTagStack(UD1ItemInstance* ItemInstance, const TArray<FRarityStatRangeSet>& RarityStatRangeSets) const;

public:
	bool IsEquipableClassType(ECharacterClassType ClassType) const;

public:
	EEquipmentType EquipmentType = EEquipmentType::Count;

	UPROPERTY(EditDefaultsOnly, meta=(Bitmask, BitmaskEnum="ECharacterClassType"))
	uint32 EquipableClassFlags = ((1 << (uint32)ECharacterClassType::Count) - 1);

	UPROPERTY(EditDefaultsOnly)
	TObjectPtr<const ULyraAbilitySet> BaseAbilitySet;
};

D1ItemFragment_Equipable.cpp

#include "D1ItemFragment_Equipable.h"

#include "Item/D1ItemInstance.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(D1ItemFragment_Equipable)

FRarityStatSet::FRarityStatSet()
{
	RarityStats.SetNum((int32)EItemRarity::Count);
	for (int32 i = 0; i < RarityStats.Num(); i++)
	{
		RarityStats[i].Rarity = (EItemRarity)i;
	}
}

FRarityStatRangeSet::FRarityStatRangeSet()
{
	RarityStatRanges.SetNum((int32)EItemRarity::Count);
	for (int32 i = 0; i < RarityStatRanges.Num(); i++)
	{
		RarityStatRanges[i].Rarity = (EItemRarity)i;
	}
}

UD1ItemFragment_Equipable::UD1ItemFragment_Equipable(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{

}

void UD1ItemFragment_Equipable::AddStatTagStack(UD1ItemInstance* ItemInstance, const TArray<FRarityStatSet>& RarityStatSets) const
{
	if (ItemInstance == nullptr) {
		return;
	}

	for (const FRarityStatSet& RarityStatSet : RarityStatSets)
	{
		const FGameplayTag& StatTag = RarityStatSet.StatTag;
		const FRarityStat& Stat = RarityStatSet.RarityStats[(int32)ItemInstance->GetItemRarity()];
		ItemInstance->AddStatTagStack(StatTag, Stat.Value);
	}
}

void UD1ItemFragment_Equipable::AddStatTagStack(UD1ItemInstance* ItemInstance, const TArray<FRarityStatRangeSet>& RarityStatRangeSets) const
{
	if (ItemInstance == nullptr) {
		return;
	}

	for (const FRarityStatRangeSet& RarityStatRangeSet : RarityStatRangeSets)
	{
		const FGameplayTag& StatTag = RarityStatRangeSet.StatTag;
		const FRarityStatRange& StatRange = RarityStatRangeSet.RarityStatRanges[(int32)ItemInstance->GetItemRarity()];
		const int32 StatValue = FMath::RandRange(StatRange.MinValue, StatRange.MaxValue);
		ItemInstance->AddStatTagStack(StatTag, StatValue);
	}
}

bool UD1ItemFragment_Equipable::IsEquipableClassType(ECharacterClassType ClassType) const
{
	return (EquipableClassFlags & (1 << (uint32)ClassType)) != 0;
}

D1ItemFragment_Equipable_Armor.h

#pragma once

#include "D1Define.h"
#include "D1ItemFragment_Equipable.h"
#include "D1ItemFragment_Equipable_Armor.generated.h"

UCLASS()
class UD1ItemFragment_Equipable_Armor : public UD1ItemFragment_Equipable
{
	GENERATED_BODY()

public:
	UD1ItemFragment_Equipable_Armor(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

protected:
#if WITH_EDITORONLY_DATA
	virtual void PreSave(FObjectPreSaveContext SaveContext) override;
#endif //WITH_EDITORONLY_DATA

public:
	virtual void OnInstanceCreated(UD1ItemInstance* ItemInstance) const override;

public:
	UPROPERTY(EditDefaultsOnly)
	EArmorType ArmorType = EArmorType::Count;

	UPROPERTY(EditDefaultsOnly, meta=(EditCondition="ArmorType == EArmorType::Chest", EditConditionHides))
	bool bIsFullBody = false;

	UPROPERTY(EditDefaultsOnly)
	TSoftObjectPtr<USkeletalMesh> ArmorMesh;

	UPROPERTY(EditDefaultsOnly, meta=(ForceInlineRow))
	TArray<FRarityStatRangeSet> RarityStatRangeSets;
};

D1ItemFragment_Equipable_Armor.cpp

#include "D1ItemFragment_Equipable_Armor.h"

#include "Item/D1ItemInstance.h"
#include "UObject/ObjectSaveContext.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(D1ItemFragment_Equipable_Armor)

UD1ItemFragment_Equipable_Armor::UD1ItemFragment_Equipable_Armor(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	EquipmentType = EEquipmentType::Armor;
}

#if WITH_EDITORONLY_DATA
void UD1ItemFragment_Equipable_Armor::PreSave(FObjectPreSaveContext SaveContext)
{
	Super::PreSave(SaveContext);

	if (ArmorType != EArmorType::Chest)
	{
		bIsFullBody = false;
	}

	for (int i = 0; i < RarityStatRangeSets.Num(); i++)
	{
		TArray<FRarityStatRange>& RarityStatRanges = RarityStatRangeSets[i].RarityStatRanges;
		RarityStatRanges.SetNum((int32)EItemRarity::Count);
		for (int32 j = 0; j < RarityStatRanges.Num(); j++)
		{
			RarityStatRanges[j].Rarity = (EItemRarity)j;
		}
	}
}
#endif // WITH_EDITORONLY_DATA

void UD1ItemFragment_Equipable_Armor::OnInstanceCreated(UD1ItemInstance* ItemInstance) const
{
	Super::OnInstanceCreated(ItemInstance);

	AddStatTagStack(ItemInstance, RarityStatRangeSets);
}

D1ItemFragment_Equipable_Attachment.h

#pragma once

#include "D1ItemFragment_Equipable.h"
#include "D1ItemFragment_Equipable_Attachment.generated.h"

 class AD1EquipmentBase;

USTRUCT(BlueprintType)
struct FD1WeaponAttachInfo
{
	GENERATED_BODY()

public:
	//UPROPERTY(EditDefaultsOnly)
	//TSubclassOf<AD1EquipmentBase> SpawnWeaponClass;

	UPROPERTY(EditDefaultsOnly)
	FName AttachSocket;

	UPROPERTY(EditDefaultsOnly)
	FTransform AttachTransform;
};

UCLASS(Abstract, Const)
class UD1ItemFragment_Equipable_Attachment : public UD1ItemFragment_Equipable
{
	GENERATED_BODY()

public:
	UD1ItemFragment_Equipable_Attachment(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

public:
	UPROPERTY(EditDefaultsOnly)
	EWeaponHandType WeaponHandType = EWeaponHandType::Count;

	UPROPERTY(EditDefaultsOnly)
	FD1WeaponAttachInfo WeaponAttachInfo;

	UPROPERTY(EditDefaultsOnly)
	TSoftObjectPtr<UAnimMontage> EquipMontage;

	UPROPERTY(EditDefaultsOnly)
	TSoftObjectPtr<UAnimMontage> FrontHitMontage;

	UPROPERTY(EditDefaultsOnly)
	TSoftObjectPtr<UAnimMontage> BackHitMontage;

	UPROPERTY(EditDefaultsOnly)
	TSoftObjectPtr<UAnimMontage> LeftHitMontage;

	UPROPERTY(EditDefaultsOnly)
	TSoftObjectPtr<UAnimMontage> RightHitMontage;

	UPROPERTY(EditDefaultsOnly)
	TSoftObjectPtr<UAnimMontage> BlockHitMontage;

	UPROPERTY(EditDefaultsOnly)
	TSoftObjectPtr<UAnimMontage> PocketWorldIdleMontage;

	UPROPERTY(EditDefaultsOnly)
	TSubclassOf<UAnimInstance> AnimInstanceClass;
};

D1ItemFragment_Equipable_Attachment.cpp

#include "D1ItemFragment_Equipable_Attachment.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(D1ItemFragment_Equipable_Attachment)

UD1ItemFragment_Equipable_Attachment::UD1ItemFragment_Equipable_Attachment(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{

}

D1ItemFragment_Equipable_Utility.h

#pragma once

#include "D1ItemFragment_Equipable_Attachment.h"
#include "D1ItemFragment_Equipable_Utility.generated.h"

class UGameplayEffect;

UCLASS()
class UD1ItemFragment_Equipable_Utility : public UD1ItemFragment_Equipable_Attachment
{
	GENERATED_BODY()

public:
	UD1ItemFragment_Equipable_Utility(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

protected:
#if WITH_EDITORONLY_DATA
	virtual void PreSave(FObjectPreSaveContext SaveContext) override;
#endif // WITH_EDITORONLY_DATA

public:
	virtual void OnInstanceCreated(UD1ItemInstance* ItemInstance) const override;

public:
	UPROPERTY(EditDefaultsOnly)
	EUtilityType UtilityType = EUtilityType::Count;

	UPROPERTY(EditDefaultsOnly)
	TSubclassOf<UGameplayEffect> UtilityEffectClass;

	UPROPERTY(EditDefaultsOnly, meta=(ForceInlineRow))
	TArray<FRarityStatSet> RarityStatSets;
};

D1ItemFragment_Equipable_Utility.cpp

#include "D1ItemFragment_Equipable_Utility.h"

#include "UObject/ObjectSaveContext.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(D1ItemFragment_Equipable_Utility)

UD1ItemFragment_Equipable_Utility::UD1ItemFragment_Equipable_Utility(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	EquipmentType = EEquipmentType::Utility;
}

#if WITH_EDITORONLY_DATA
void UD1ItemFragment_Equipable_Utility::PreSave(FObjectPreSaveContext SaveContext)
{
	Super::PreSave(SaveContext);

	for (int i = 0; i < RarityStatSets.Num(); i++)
	{
		TArray<FRarityStat>& RarityStats = RarityStatSets[i].RarityStats;
		RarityStats.SetNum((int32)EItemRarity::Count);
		for (int32 j = 0; j < RarityStats.Num(); j++)
		{
			RarityStats[j].Rarity = (EItemRarity)j;
		}
	}
}
#endif // WITH_EDITORONLY_DATA

void UD1ItemFragment_Equipable_Utility::OnInstanceCreated(UD1ItemInstance* ItemInstance) const
{
	Super::OnInstanceCreated(ItemInstance);

	AddStatTagStack(ItemInstance, RarityStatSets);
}

D1ItemFragment_Equipable_Weapon.h

#pragma once

#include "D1Define.h"
#include "D1ItemFragment_Equipable_Attachment.h"
#include "D1ItemFragment_Equipable_Weapon.generated.h"

UCLASS()
class UD1ItemFragment_Equipable_Weapon : public UD1ItemFragment_Equipable_Attachment
{
	GENERATED_BODY()

public:
	UD1ItemFragment_Equipable_Weapon(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

protected:
#if WITH_EDITORONLY_DATA
	virtual void PreSave(FObjectPreSaveContext SaveContext) override;
#endif // WITH_EDITORONLY_DATA

public:
	virtual void OnInstanceCreated(UD1ItemInstance* ItemInstance) const override;

public:
	UPROPERTY(EditDefaultsOnly)
	EWeaponType WeaponType = EWeaponType::Count;

	UPROPERTY(EditDefaultsOnly)
	TObjectPtr<USoundBase> AttackSwingSound;

	UPROPERTY(EditDefaultsOnly)
	TArray<TObjectPtr<const ULyraAbilitySet>> SkillAbilitySets;

	UPROPERTY(EditDefaultsOnly)
	TArray<FRarityStatRangeSet> RarityStatRangeSets;
};

D1ItemFragment_Equipable_Weapon.cpp

#include "D1ItemFragment_Equipable_Weapon.h"

#include "Item/D1ItemInstance.h"
#include "UObject/ObjectSaveContext.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(D1ItemFragment_Equipable_Weapon)

UD1ItemFragment_Equipable_Weapon::UD1ItemFragment_Equipable_Weapon(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	EquipmentType = EEquipmentType::Weapon;
}

#if WITH_EDITORONLY_DATA
void UD1ItemFragment_Equipable_Weapon::PreSave(FObjectPreSaveContext SaveContext)
{
	Super::PreSave(SaveContext);

	for (int i = 0; i < RarityStatRangeSets.Num(); i++)
	{
		TArray<FRarityStatRange>& RarityStatRanges = RarityStatRangeSets[i].RarityStatRanges;
		RarityStatRanges.SetNum((int32)EItemRarity::Count);
		for (int32 j = 0; j < RarityStatRanges.Num(); j++)
		{
			RarityStatRanges[j].Rarity = (EItemRarity)j;
		}
	}
}
#endif // WITH_EDITORONLY_DATA

void UD1ItemFragment_Equipable_Weapon::OnInstanceCreated(UD1ItemInstance* ItemInstance) const
{
	Super::OnInstanceCreated(ItemInstance);

	AddStatTagStack(ItemInstance, RarityStatRangeSets);
}

ItemData 블루프린트 추가

Gladiator Content
└─Data (*)

작성한 CPP Item Data 를 불려올 수 있도록 다음 경로의 블루프린트 추가하도록 한다.

cpp 에서 정상적으로 작성에 성공하였다면 블루프린트 클래스 D1 Item Data 를 생성할 수 있게 될 것이다. 해당 클래스로 선택하고 블루프린트를 추가한 다음 ItemData_GladiatorGame 이름으로 지어주도록 하였다.


DefaultConfig.ini 파일 편집하기

아이템들도 에셋 매니저를 통해 로딩이 가능하게 해야 한다.
해당 경로의 DefaultGame.ini 파일을 열어 데이터 에셋 경로를 추가하도록 한다.

Sources
└─Config
   └DefaultGame.ini

추가할 문구는 다음과 같이 ItemDataPath 경로와 함께 넣어주도록 한다.

[/Script/LyraGame.LyraAssetManager]
...
ItemDataPath=/GladiatorCore/Data/ItemData_GladiatorGame.ItemData_GladiatorGame
...