UArcItemGenerator
使用生成器将ArcItemStack
生成,其仅仅用于生成固定种类的Stack与UI数据,而动态变化的Definition和Rarity则作为函数参数传入
栈类、Tag、UIData
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Default")
TSubclassOf<UArcItemStack> ItemStackClass;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Default")
FGameplayTagContainer OwnedTags;
UPROPERTY(EditDefaultsOnly, Instanced, BlueprintReadOnly, Category = "UI", Meta = (AssetBundles = "UI"))
UArcInvUIData* UIData;
ItemStackClass
:作为一个备选项与Definition的StackClass共同决定物品的Definition
OwnedTags
:Generator自身的Tag,与生成的具体物品无关
UIData
:没有引用
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override;
virtual FPrimaryAssetId GetPrimaryAssetId() const override;
UArcItemStack* GenerateItemStack(const FArcItemGeneratorContext& Context);
GetOwnedGameplayTags与GetPrimaryAssetId之前已经提过,不表
GenerateItemStack
GenerateItemStack
为protected类型,只允许子类调用
作用:
传入Definition和Rarity,根据自身的DefaultStackClass
和Definition的StackClass生成包含了Definition和 Rarity的新UArcItemStack
并返回d
UArcItemStack* UArcItemGenerator::CreateNewItemStack(TSubclassOf<UArcItemDefinition_New> ItemDefinition, UArcItemRarity* ItemRarity)
{
//1、寻找ItemStackClass
TSubclassOf<UArcItemStack> ISC = ItemStackClass;
if (IsValid(ISC)){
//检查我们的ItemStack类是否是item def想要的子类。 如果不是,就使用该项目定义的类
if (IsValid(ItemDefinition.GetDefaultObject()->DefaultItemStackClass) && !ISC->IsChildOf(ItemDefinition.GetDefaultObject()->DefaultItemStackClass)){
ISC = ItemDefinition.GetDefaultObject()->DefaultItemStackClass;}}
if (!IsValid(ISC)){
//如果没有指定ISC,则使用DefaultItemStackClass
ISC = GetDefault<UArcInventoryDeveloperSettings>()->DefaultItemStackClass;
if (!IsValid(ISC)){
//如果依旧没有DefaultItemStackClass,则使用基类
//但我就是不使用Definition中的ItemStackClass
ISC = UArcItemStack::StaticClass();}}
//2、生成并初始化ItemStack
UArcItemStack* NewItemStack = NewObject<UArcItemStack>(GetTransientPackage(), ISC);
NewItemStack->ItemDefinition = ItemDefinition;
NewItemStack->Rarity = ItemRarity;
return NewItemStack;
}
实现:
1、传入Definition与Rarity
2、获取到StackClass
ItemStackClass
(之后简称ISC
)ISC
ISC
ISC
ISC
ISC
基类最大的问题在于没有ISC
时不去查找ArcItemDefinition
的基类而直接使用默认类,询问后作者会在之后的版本修复这个问题
修复后的优先级应是:
ISC
是Definition中ISC
的子类,则使用当前ISCArcItemDefinition
->UArcInventoryDeveloperSettings
->UArcItemStack::StaticClass()
ArcItemGenerator
创建的是ArcItemStack
而非ArcItemDefinition
,Stack中的Definition可以为空,所以需要有默认的StackClass
以及UIData
。
属性集与ArcItemDefinition
高度重合,ItemStackClass
、OwnedTags
、UIData
三者在Definition中都有对应,且其中并没有定义Definition的变量,这是对Generator中变与不变的部分并进行合理划分。
UIData
、StackClass
可以在没有Definition的情况下也能正常进行物品操作与显示,而OwnedTags
是对自身的描述。
另一方面GenerateItemStack
仅允许子类调用,证明需要用户自己创建子类生成自己的Generator类型
UArcItemGenerator_Unique
作为UArcItemGenerator的子类,可以说是对UArcItemGenerator功能的补全,其能承担一个含有Definition的Stack生成任务。
包含了Definition、Rarity、Name物品实体属性,同时通过SubItemGenerators增加拓展性
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Definition")
TSubclassOf<UArcItemDefinition_New> ItemDefinition;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Definition")
UArcItemRarity* ItemRarity;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Definition")
FText ItemDisplayName;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Instanced, Category = "Definition")
TArray<UArcItemGenerator*> SubItemGenerators;
ItemDefinition
与ItemRarity
对应ArcItemGenerator
中CreateNewItemStack
的两个变量输入
SubItemGenerators
与SubItemStack
异曲同工
ItemDisplayName
显示的名称,
GenerateItemStack_Implementation
GenerateItemStack_Implementation
作用:
外界让Generator生成Stack的接口
UArcItemStack* UArcItemGenerator_Unique::GenerateItemStack_Implementation(const FArcItemGeneratorContext& Context)
{
//1、调用父类函数生成并填充Stack
UArcItemStack* ItemStack = CreateNewItemStack(ItemDefinition, ItemRarity);
ItemStack->ItemName = ItemDisplayName;
//2、如果有子生成器,则一并生成并放入子Stack中
for (UArcItemGenerator* SubItemGenerator : SubItemGenerators){
if (IsValid(SubItemGenerator)){
UArcItemStack* SubItem = SubItemGenerator->GenerateItemStack(Context);
ItemStack->AddSubItemStack(SubItem);}}
return ItemStack;
}
实现:
继承自父类的GenerateItemStack
的override
直接调用父类的CreateNewItemStack
生成Stack并赋上Name,然后循环生成所有的Generator并放入子Stack中。
其是Generator的完整实现,完成了一个Stack生成器的所有内容
比较奇怪的是为什么ItemName会放在Generator中而不是Definition中
UArcItemGenerator_Static
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Generator")
TSubclassOf<UArcItemGenerator> StaticGenerator;
一个指向UArcItemGenerator
子类的指针StaticGenerator
if (IsValid(StaticGenerator)){
return StaticGenerator->GetDefaultObject<UArcItemGenerator>()->GenerateItemStack(Context);}
return nullptr;
调用StaticGenerator的GenerateItemStack
迷惑
ArcItemStack_Perk
目前与UArcItemGenerator_SimpleRandom
有比较强的互动
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Perk Settings", Replicated)
TArray<FArcPerkModifierData> Modifiers;
简单直接,只有一个属性修改器数组
virtual bool CanAttachTo_Implementation(UArcItemStack* OtherStack) override;
CanAttachTo
作用:
重写了ArcItemStack
的CanAttachTo
,判断是否能成为该Stack的子Stack
bool UArcItemStack_Perk::CanAttachTo_Implementation(UArcItemStack* OtherStack)
{
if (OtherStack->IsA(UArcItemStack_Perk::StaticClass())){
return false;}
return true;
}
实现:
除了自身以外都可以成为其子Stack
类似于一个属性修改器数组挂到其他Stack上,目前还不清楚一个Stack上挂一堆属性修改器是做什么。也许是武器给所有者提供的数值?
UArcItemGenerator_SimpleRandom
随机生成ItemStack
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Default")
FGameplayTagQuery ItemTagQuery;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Default")
TArray< FArcItemGenerator_RaritySelector> RarityTable;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Default")
bool bRequireUniqueModifiers;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Default")
TArray<FArcItemGenerator_DynamicModifier> WeightedPossibleModifiers;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Perks")
bool bRequireUniquePerks;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Perks")
TArray<FArcItemGenerator_PerkSelector> WeightedPossiblePerks;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Instanced, Category = "Perks")
UArcItemGenerator* DynamicPerkItemGenerator;
ItemTagQuery
物品的Tag类型,根据Query找到对应物品集合
RarityTable
规定了物品中的Rarity类型、属性修改器数量、Perk数量
bRequireUniqueModifiers
在筛选多个属性修改器时是否每个修改器都是唯一的
WeightedPossibleModifiers
属性修改器的筛选范围
bRequireUniquePerks
在筛选多个ArcItemStack_Perk
时是否每个ArcItemStack_Perk
是唯一的
WeightedPossiblePerks
ArcItemStack_Perk
的候选名单和对应权重
DynamicPerkItemGenerator
给属性修改器生成指定的ArcItemStack_Perk
GenerateItemStack
作用:
随机生成物品和Rarity,以及根据RarityEntry生成修改器和Perk放入子Stack中
UArcItemStack* UArcItemGenerator_SimpleRandom::GenerateItemStack_Implementation(const FArcItemGeneratorContext& Context){
//1、找一个符合条件的Definition
//1.1、找到Query相关的所有Definitions
TArray<TSubclassOf<UArcItemDefinition_New>> ItemDefs;
if (!UArcItemBPFunctionLibrary::QueryMatchingItemDefinitions(ItemTagQuery, ItemDefs){
return nullptr;}
//1.2、从Definitions中随机一个Definition
TSubclassOf<UArcItemDefinition_New> ItemDef = ItemDefs[FMath::RandRange(0, ItemDefs.Num() - 1)];
//2、从RarityTable中随机一个RarityEntry
FArcItemGenerator_RaritySelector RarityEntry = SelectSingleWeightedItem(RarityTable);
//3、找一个符合条件的Rarity
//3.1、根据RarityEntry中的Query查询符合条件的Rariety并放入Rarieties
TArray<UArcItemRarity*> Rarieties;
if (!UArcItemBPFunctionLibrary::QueryMatchingItemRarities(RarityEntry.RarityTagQuery, Rarieties)){}
UArcItemRarity* Rarity = nullptr;
//3.2、从Rarieties中随机一个Rariety
if (Rarieties.Num() > 0){
Rarity = Rarieties[FMath::RandRange(0, Rarieties.Num() - 1)];}
//4、创建一个Stack并赋值Definition和Rariety
UArcItemStack* ItemStack = CreateNewItemStack(ItemDef, Rarity);
//5、创建动态修改器补偿
if (RarityEntry.NumberOfModifiers > 0 && IsValid(DynamicPerkItemGenerator)){
//5.1、选择出一组属性修改器
TArray< FArcItemGenerator_DynamicModifier> SelectedMods;
SelectWeightedItems(WeightedPossibleModifiers, RarityEntry.NumberOfModifiers, bRequireUniqueModifiers, SelectedMods);
//5.2、创建UArcItemStack_Perk,将所有修改器移入新Perk中,将Perk放入Stack的子Stack中
if (UArcItemStack_Perk* Perk = Cast<UArcItemStack_Perk>(DynamicPerkItemGenerator->GenerateItemStack(Context))){
for (FArcItemGenerator_DynamicModifier& DynamicMod : SelectedMods){
FArcPerkModifierData ModifierData;
ModifierData.Attribute = DynamicMod.Attribute;
ModifierData.SlotApplicationRequirement = DynamicMod.SlotApplicationRequirement;
ModifierData.ModifierOp = DynamicMod.ModifierOp;
ModifierData.Value = FMath::RandRange(DynamicMod.MinValue, DynamicMod.MaxValue);
Perk->Modifiers.Add(ModifierData);}
ItemStack->AddSubItemStack(Perk);}}
//6、选择一组PerGenerator生成Perk并放入子Stack中
if (RarityEntry.NumberOfPerks > 0){
TArray<FArcItemGenerator_PerkSelector>;
SelectWeightedItems(WeightedPossiblePerks, RarityEntry.NumberOfPerks, bRequireUniquePerks, SelectedPerks);
for (FArcItemGenerator_PerkSelector& SelectedPerk : SelectedPerks){
TArray<UArcItemGenerator*> PerkGenerators;
if (!UArcItemBPFunctionLibrary::QueryMatchingItemGenerators(SelectedPerk.PerkTagQuery, PerkGenerators)){
continue;}
UArcItemGenerator* PerkGenerator = PerkGenerators[FMath::RandRange(0, PerkGenerators.Num() - 1)];
if (!IsValid(PerkGenerator)){
continue;}
UArcItemStack* PerkInstance = PerkGenerator->GenerateItemStack(Context);
ItemStack->AddSubItemStack(PerkInstance);}}
return ItemStack;
}
方法:
ItemTagQuery
随机生成Definition
RarityTable
中随机一个RarityEntryRarityEntry
中的QueryTag
寻找Rarities并从中随机一个RarityUArcItemStack_Perk
,从WeightedPossibleModifiers
中获取到(RarityEntry
中的NumberOfModifiers
)个修改器放入Perk中,最后将该Perk放入主Stack中SelectWeightedItems
作用:
Protected函数
这是一个随机多选择器,可以在一组物品中根据物品权重和总物品数量随机出一组物品出来。但仍需指定确定数量。可以应用于打怪后爆出的随机物品生成中
template <typename WeightedStructType>
void SelectWeightedItems(const TArray<WeightedStructType>& WeightedRarities, int32 NumberToSelect, bool bSelectUnique, TArray< WeightedStructType>& OutSelections)
{
//1、获取总权重
int32 SumOfWeights = 0;
for (const WeightedStructType& Entry : WeightedRarities){
SumOfWeights += Entry.Weight;}
//2、选择一个候选列表(PossibleSelection)出来
TArray<WeightedStructType*> PossibleSelection;
for (int32 i = 0; i < NumberToSelect; i++){
int32 Tries = 0;
//2.1、根据权重随机出一个GrabbedStruct,重复则继续随机,随机10次后还找不到则跳过
WeightedStructType* GrabbedStruct = nullptr;
do{
GrabbedStruct = PickWeightedItem(WeightedRarities, SumOfWeights);
if (bSelectUnique && PossibleSelection.Contains(GrabbedStruct)){
GrabbedStruct = nullptr;}
} while (GrabbedStruct == nullptr || ++Tries > 10);
//2.2、添加进候选数组
if (GrabbedStruct != nullptr){
PossibleSelection.Add(GrabbedStruct);}}
//3、将候选列表转移至输出列表
for (WeightedStructType* Selection : PossibleSelection){
OutSelections.Add(*Selection);}
}
方法:
多次使用PickWeightedItem
获取单个Rarity,最后输出选中的Rarity集合
PickWeightedItem
作用:
Protected函数
挑选加权项目。根据总权重和Rarities的各个分权重随机出一个Rarities出来
template <typename WeightedStructType>
WeightedStructType* PickWeightedItem(const TArray<WeightedStructType>& WeightedRarities, int32 SumOfWeights)
{
int32 RandomRoll = FMath::RandRange(0, SumOfWeights);
for (const WeightedStructType& Struct : WeightedRarities){
if (RandomRoll < Struct.Weight){
return const_cast<WeightedStructType*>(&Struct);}
RandomRoll -= Struct.Weight;}
return nullptr;
}
使用:
随机一个数,找到这个数对应的区间,返回该区间对应的Rarity
UArcItemGenerator
提供所有物品生成器的核心并做成基类,然后根据基类衍生出不同的生成类型UArcItemGenerator_Unique
用于根据Definition和Rarity生成特定物理种类和稀有度的物品UArcItemGenerator_Static
不知道用来干啥ArcItemStack_Perk
与UArcItemGenerator_SimpleRandom
共同实现:不同物品实体、不同品质对应不同属性加成效果的随机生成器