BlueRose
文章90
标签22
分类7
UE5 Lyra学习笔记(1)—LyraEditor

UE5 Lyra学习笔记(1)—LyraEditor

前言

ULyraInventoryManagerComponent通过GameFeature功能在模块激活时挂载Controller上,具体可以查看ShooterCore的GameFeature配置文件。
Lyra中的物品系统采用 Entry - Instance - Definition - Fragment的结构。但该系统有没有完成的痕迹,所以个人不建议直接拿来用,但里面有考虑到网络同步的设计值得学习。
与ActionRPG项目相比它没有实现SaveGame/LoadGame部分,也没有使用FPrimaryAssetId来减少网络同步以及存档大小。所以也挺可惜的。

Inventory的主要的资产为ULyraInventoryItemDefinition,前缀名为ID_;Equipment的主要资产为ULyraEquipmentDefinition,前缀名为WID_ 。这些资产位于:

  • Plugins\GameFeatures\ShooterCore\Content\Items\
  • Plugins\GameFeatures\ShooterCore\Content\Weapons\

Inventory

Class

  • ULyraInventoryManagerComponent:整个背包系统的管理组件。使用FLyraInventoryList来管理物品数据。
    • FLyraInventoryList:类型为FFastArraySerializer(为了解决网络传输而实现的类)。内部使用TArray<FLyraInventoryEntry> Entries来存储物品数据。
    • FLyraInventoryEntry:类型为FFastArraySerializerItem(为了解决网络传输而实现的类)。内部使用ULyraInventoryItemInstance来存储物品数据,内部还有StackCount与LastObservedCount变量(该变量不同步)。
      • ULyraInventoryItemInstance:物品实例。内部变量有TSubclassOf<ULyraInventoryItemDefinition> ItemDef以及一个用于存储物品状态的FGameplayTagStackContainer(Lyra定义的Tag堆栈)
      • ULyraInventoryItemDefinition:物品定义,UCLASS(Blueprintable, Const, Abstract)。内部变量有TArray<TObjectPtr<ULyraInventoryItemFragment>> Fragments,也就是说一个ItemDefinition会有多个ItemFragment。另外就是用于显示名称的FText DisplayName
      • ULyraInventoryItemFragment:物品片段,UCLASS(DefaultToInstanced, EditInlineNew, Abstract)。可以理解为物品信息碎片,一个物品定义由多个碎片组成。

相关UClassMeta的官方文档解释:

  • Blueprintable:将此类公开为用于创建蓝图的可接受基类。默认为NotBlueprintable。子类会继承该标签。
  • Const:此类中的所有属性和函数都是const并且导出为const。子类会继承该标签。
  • Abstract:Abstract说明符会将类声明为”抽象基类”。阻止用户向关卡中添加此类的Actor。
  • DefaultToInstanced:此类的所有实例都被认为是”实例化的”。实例化的类(组件)将在构造时被复制。子类会继承该标签。
  • EditInlineNew:指示可以从虚幻编辑器”属性(Property)”窗口创建此类的对象,而非从现有资源引用。默认行为是仅可通过”属性(Property)”窗口指定对现有对象的引用。子类会继承该标签;子类可通过 NotEditInlineNew 来覆盖它。

ItemInstance

该类为物品的实例类,也是物品的操作类。包含了ItemDefinition之外还记录了一些额外的Runtime状态数据(FGameplayTagStackContainer也是一个FFastArraySerializer类),还定义各种操作用函数。

ItemDefinition

该类为定义物品各种属性的资产类,主要通过ItemFragment进行碎片化描述。通过蓝图继承的方式来创建一个个的物品定义。

PS.个人很好奇为什么不把这这个类做成一个DataAsset,因为这个类也就是个静态数据类。至少ULyraPickupDefinition继承自UDataAsset。

ItemFragment

该类为物品碎片化的属性描述类,同时附带OnInstanceCreated()虚函数,以实现一些属性变化外的特殊效果。

在Lyra中实现了这一些:

  • UInventoryFragment_EquippableItem:用于指定ULyraEquipmentDefinition(定义于Equipment)类。
  • UInventoryFragment_SetStats:用于给ItemInstance添加状态标签。
  • UInventoryFragment_QuickBarIcon:用于指定快捷栏SlateBrush资源以及UI显示名称。
  • UInventoryFragment_PickupIcon:用于指定骨骼物体(枪械或者血包)、显示名称以及pad颜色。

Pickup

定义了Pickup相关类:

  • FInventoryPickup:可以理解为Entry。
    • FPickupInstance:Instance。
      • FPickupTemplate:可以理解为Definition。
  • UPickupable与IPickupable:接口类。

AddEntry

给FLyraInventoryList添加Entry的逻辑大致为:

  1. 检查相关变量
  2. TArray<FLyraInventoryEntry> Entries添加Entry。
    1. 使用NewObject()填充Instance指针。
    2. 使用Instance->SetItemDef()设置ItemDefinition类。
    3. 通过ItemDefinition取得ItemFragment,并且调用接口函数OnInstanceCreated(),部分ItemDefinition会执行一些特殊逻辑。
  3. 设置Entry的StackCount,并设置Entry为Dirty,触发更新逻辑。
  4. 返回Instance指针。

代码如下:

ULyraInventoryItemInstance* FLyraInventoryList::AddEntry(TSubclassOf<ULyraInventoryItemDefinition> ItemDef, int32 StackCount)  
{  
   ULyraInventoryItemInstance* Result = nullptr;  

   check(ItemDef != nullptr);  
   check(OwnerComponent);  

   AActor* OwningActor = OwnerComponent->GetOwner();  
   check(OwningActor->HasAuthority());  


   FLyraInventoryEntry& NewEntry = Entries.AddDefaulted_GetRef();  
   NewEntry.Instance = NewObject<ULyraInventoryItemInstance>(OwnerComponent->GetOwner());  //@TODO: Using the actor instead of component as the outer due to UE-127172  
   NewEntry.Instance->SetItemDef(ItemDef);  
   for (ULyraInventoryItemFragment* Fragment : GetDefault<ULyraInventoryItemDefinition>(ItemDef)->Fragments)  
   {      
        if (Fragment != nullptr)  
        {
           Fragment->OnInstanceCreated(NewEntry.Instance);  
        } 
    }   
    NewEntry.StackCount = StackCount;  
    Result = NewEntry.Instance;  

   //const ULyraInventoryItemDefinition* ItemCDO = GetDefault<ULyraInventoryItemDefinition>(ItemDef);  
   MarkItemDirty(NewEntry);  

   return Result;  
}

Equipment

Lyra的装备系统只使用了Entry - Instance - Definition结构。

Class

  • ULyraEquipmentManagerComponent:类似ULyraInventoryManagerComponent的装备管理类。
    • FLyraEquipmentList
    • FLyraAppliedEquipmentEntry
      • ULyraEquipmentInstance:装备实例。
        • ULyraEquipmentDefinition:装备定义。
        • FLyraEquipmentActorToSpawn:Spawn用的相关数据。
  • ULyraPickupDefinition:UDataAsset。主要是有ULyraInventoryItemDefinition以及其他美术资产指针。
    • ULyraWeaponPickupDefinition:ULyraPickupDefinition的子类,储存Spawn用的LocationOffset与Scale数据。
  • ULyraGameplayAbility_FromEquipment:Equipment类型GA的父类,实现2个ULyraEquipmentInstance/ULyraInventoryItemInstance Get函数。子类有2个蓝图与1个c++类。
  • ULyraQuickBarComponent

ULyraEquipmentInstance

OnEquipped ()/OnUnequipped()会调用对用的K2函数(BlueprintImplementableEvent),具体逻辑在蓝图实现。主要的逻辑就是播放对应的Montage动画。

ULyraEquipmentDefinition

  • TSubclassOf<ULyraEquipmentInstance> InstanceType:装备类型,通过Class类型来判断装备类型。
  • TArray<TObjectPtr<const ULyraAbilitySet>> AbilitySetsToGrant:装备的附带能力集。
  • TArray<FLyraEquipmentActorToSpawn> ActorsToSpawn :装备Actor。