This commit is contained in:
2023-06-29 11:55:02 +08:00
commit 36e95249b1
1236 changed files with 464197 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
---
title: UE5 Lyra学习笔记(1)—LyraEditor
date: 2022-08-09 13:55:15
tags: Lyra Editor
rating: ⭐️⭐️
---
# LyraEditor
主要的实现内容为:
- ULyraEditorEngine
- UCommandlet
- UEditorValidator
- 3个全局命令
- 1个自定义Asset
- FGameEditorStyle
在FLyraEditorModule启动与载入时增加GameplayCueNotify类与GameplayCue路径。并且绑定OnBeginPIE()与OnEndPIE()。
## ULyraEditorEngine
在DefaultEngine.ini的[/Script/Engine.Engine]中指定了这个类,来进行替换编辑器功能:
```ini
UnrealEdEngine=/Script/LyraEditor.LyraEditorEngine
EditorEngine=/Script/LyraEditor.LyraEditorEngine
```
- 重写了PreCreatePIEInstances()
- 根据ALyraWorldSettings的布尔变量ForceStandaloneNetMode来强制设置成**PIE_Standalone**网络模式。
- 调用ULyraDeveloperSettings与ULyraPlatformEmulationSettings类的OnPlayInEditorStarted()向FSlateNotificationManager传递消息。
- 返回父类函数结果。
- 实现了FirstTickSetup()并在Tick()中只调用一次。
- 让ContenBrowser显示插件文件夹
- 判断用户是否有修改停止PIE按键无修改情况下修改按键为Shift+ESC
## UCommandlet
参考https://zhuanlan.zhihu.com/p/512610557
它的应用场景主要为:
- 借助 Commandlet ,我们无需打开 UE 编辑器,即可在命令行下执行某段 C++ 代码。可用来批量处理 UE 工程中的资源等。结合 Jenkins 等自动化处理方案,可较方便地实现 UE 工程的自动化处理。
- 典型应用场景如 Resave Packages 、Fixup Redirects、Cooking、Localization Pipeline 、ImportAsset、ContentValidation等。
引擎中实现了几十个Commandlet具体可以查看引擎中的UCommandlet的子类。
#### 使用与实现
采用命令行启动:
`D:\\MyProject\\MyProjectDir\\MyProject.uproject -skipcompile -run={自定义的Commandlet的名字} {需要的各种参数}`
实现方法:重写`virtual int32 Main(const FString& Params) override;`即可。
#### Lyra中的实现
在Lyra中实现了UContentValidationCommandlet类。主要用于使用命令对修改过的Asset进行有效性检测。功能基于P4V所以对于其他版本管理软件需要花点时间进行改写。大致逻辑如下
- 从P4V取得所有修改文件列表并将信息添加到ChangedPackageNames、DeletedPackageNames、ChangedCode、ChangedOtherFiles中。
- 根据命令行InPath、OfType、Packages参数获取包名并添加到ChangedPackageNames数组中。
- 调用`GShaderCompilingManager->FinishAllCompilation()`停止其他Shader的编译。这样就不会让有效性检测范围外的Shader错误影响到检测结果。
- 调用UEditorValidator::ValidatePackages()与UEditorValidator::ValidateProjectSettings(),最后返回检测结果。
## UEditorValidator
该功能依赖于DataValidation插件。文档地址https://docs.unrealengine.com/5.0/en-US/data-validation/
目前只有2种创建验证规则的方式
1. 覆盖UObject的IsDataValid()。
2. 创建UEditorValidatorBase的派生类。
关键函数是CanValidateAsset()与ValidateLoadedAsset()。ValidateLoadedAsset()必须为其每个Asset返回AssetPasses或AssetFails。C++与蓝图实现的Validator将在编辑器启动时自动注册而Python版本的需要UEditorValidatorSubsystem中调用AddValidator()来进行注册。
验证资产有2种方法
1. 主动验证。在Asset上右键Asset Action -> Validate Assets。
2. 保存时验证。默认情况下是开启的。设置选项位于Edit -> Editor Preferences -> Advanced -> Data Validation -> Validate On Save
UEditorValidator几个函数的逻辑为
- ValidateCheckedOutContent取得ISourceControlModule与AssetRegistryModule根据文件状态加入对Asset进行验证调用ValidatePackages()如果是h文件会调用GetChangedAssetsForCode()进行验证。最后返回错误信息。会在点击UToolMenus的CheckGameContent按钮时执行这个函数。
- ValidatePackages:验证所有Asset返回结果与所有警告与错误字符串。
- ValidateProjectSettings:读取**PythonScriptPluginSettings**的**bDeveloperMode**变量。如果为bDeveloperMode为1打印错误信息并返回false。
- IsInUncookedFolder:判断指定的package是否处于Uncooked文件夹中并返回文件夹名。
- ShouldAllowFullValidation:是否需要完整验证。
- CanValidateAsset_Implementation:判断是否可以验证Asset默认为处于需要烘焙的文件夹中就为true。
- UEditorValidator_Load:定义检测函数GetLoadWarningsAndErrorsForPackage()。
- UEditorValidator_Blueprints:验证非数据蓝图如有警告与错误则返回EDataValidationResult::Invalid
- UEditorValidator_MaterialFunctions:验证AssetClass为UMaterial的Asset如有警告与错误则返回EDataValidationResult::Invalid
- UEditorValidator_SourceControl:检查Asset的依赖是否加入版本管理
## 3个全局命令
- Lyra.CheckChaosMeshCollision:检查所有载入的StaticMesh的Chaos碰撞数据。
- Lyra.CreateRedirectorPackage:创建Asset Redirector并且重定向替换引用。
- Lyra.DiffCollectionReferenceSupport:It will list the assets in Old that 'support' assets introduced in New (are referencers directly/indirectly) as well as any loose unsupported assets.The optional third argument controls whether or not multi-supported assets will be de-duplicated (true) or not (false)
## 自定义Asset
主要功能:
构建一个EffectTag->Context(物理材质表面类型Tag)与MetaSound Source的表。按需求载入对应的Sound与NiagaraAsset并且在需要的时候进行播放。在ULyraContextEffectsSubsystem(WorldSubsystem)与AnimNotify_LyraContextEffects中有引用。
LyraEditor中的实现文件为
- FAssetTypeActions_LyraContextEffectsLibrary
- ULyraContextEffectsLibraryFactory
Asset的UObject对象ULyraContextEffectsLibrary对象定义在LyraGame模块。

View File

@@ -0,0 +1,97 @@
---
title: UE5 Lyra学习笔记(2)—LyraPlugins
date: 2022-08-09 13:55:20
tags: Lyra Editor
rating: ⭐️⭐️
---
# Plugins
- AsyncMixin
- CommonGame
- CommonLoadingScreen
- CommonUser
- GameFeatures
- GameplayMessageRouter
- GameSettings
- GameSubtitles
- LyraExampleContent
- LyraExtTool
- ModularGameplayActors
- PocketWorlds
- UIExtension
## AsyncMixin
## ModularGameplayActors
引擎插件ModularGameplay的Actor版本增加了UGameFrameworkComponentManager控制的各种基础胶水类Lyra里的相关类都继承自他们。
- AModularGameModeBase指定了插件里实现的类
- AModularAIController
- AModularCharacter
- AModularGameStateBase
- AModularPawn
- AModularPlayerController
- AModularPlayerState
## UIExtension
主要实现了`UUIExtensionPointWidget`这个控件以及用于数据控制的`UUIExtensionSubsystem``UUIExtensionPointWidget`是一个布局控件实现一种以数据驱动方式通过GameplayTag 匹配方式来插入Widget的方式。一般通过在UMG控件中设置参插槽参数
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Lyra/Lyra_W_ShootHUDLayout.png)
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Lyra/Lyra_W_ShootHUDLayout_WidgetSetting.png)
在LyraExperienceActionSet中设置AddWidgetsLayoutClass、LayoutIDWidgetClass、SlotID最后会通过`UCommonUIExtensions::PushContentToLayer_ForPlayer`往屏幕上增加Widget。
![](https://cdn.jsdelivr.net/gh/blueroseslol/ImageBag@latest/ImageBag/Lyra/Lyra_LyraExperienceActionSet.png)
这样设计的其中一个优点在于实现解耦例如在相关Ability的Add/Remove进行UI注册与卸载做到一个Ability做到所有的逻辑。
- UUIExtensionSubsystem
- UUIExtensionHandleFunctions通过FUIExtensionPointHandle判断有效性与卸载函数。
- UUIExtensionPointHandleFunctions通过FUIExtensionPointHandle判断有效性与卸载函数。
- RegisterExtensionPointForContext使用传入数据构建`FUIExtensionPoint`并其填充`ExtensionPointMap`,并且调用`ExtensionPoint`绑定的委托。
- RegisterExtensionAsData使用传入数据构建`FUIExtension`并其填充`ExtensionMap`,并且调用`Extension`绑定的委托。
- UnregisterExtensionPoint通过Handle移除对应`FUIExtensionPoint`
- UnregisterExtension通过Handle移除对应`FUIExtension`
- 若干蓝图函数。
- UMG控件
- UUIExtensionPointWidget
AddWidgets代码位于`UGameFeatureAction_AddWidgets`类中。
## CommonUI
- CommonUI官方文档https://docs.unrealengine.com/5.0/zh-CN/overview-of-advanced-multiplatform-user-interfaces-with-common-ui-for-unreal-engine/
- Introduction to Common UIhttps://www.youtube.com/watch?v=TTB5y-03SnE
- Lyra跨平台UI开发https://www.bilibili.com/video/BV1mT4y167Fm?spm_id_from=333.999.0.0&vd_source=d47c0bb42f9c72fd7d74562185cee290
### CommonUIActionRouterBase
这是一个本地玩家用的子系统。可以用于注册InputAction。
## GameplayMessageRouter
Runtime模块实现了`UGameplayMessageSubsystem``UAsyncAction_ListenForGameplayMessage`Editor模块实现了`UK2Node_AsyncAction_ListenForGameplayMessages`节点。
### FGameplayMessageListenerHandle
存储了UGameplayMessageSubsystem的弱指针、Handle IDint32 ID、MessageChannelFGameplayTag Channel与FDelegateHandle。并且实现了Unregister()与IsValid()。
### UGameplayMessageSubsystem
是一个UGameInstanceSubsystem。成员变量只有`ListenerMap`记录MessageChannel=>FChannelListenerList的Map
```c++
// List of all entries for a given channel
struct FChannelListenerList
{
TArray<FGameplayMessageListenerData> Listeners;
int32 HandleID = 0;
};
private:
TMap<FGameplayTag, FChannelListenerList> ListenerMap;
```
- 泛型函数:获取到类型的`UScriptStruct`传入内部函数,最后返回`FGameplayMessageListenerHandle`
- RegisterListener
- BroadcastMessage
- UnregisterListener
- 泛型函数调用的内部函数
- RegisterListenerInternal从使用MessageChannel(GameplayTag)从`ListenerMap`找到对应(如果没有则添加)的`FChannelListenerList`,并填充`FGameplayMessageListenerData`数据:仿函数以及其他相关信息。
- BroadcastMessageInternal调用对应MessageChannel(GameplayTag)与UScriptStruct类型的所有仿函数该过程还会移除无效的仿函数所在项
- UnregisterListenerInternal从`ListenerMap`中找到对应MessageChannel(GameplayTag)的`FChannelListenerList`并从其中移除指定HandleID的`FGameplayMessageListenerData`。如果之后`FChannelListenerList`为空,则从`ListenerMap`移除这个键值。
### UAsyncAction_ListenForGameplayMessage
在Activate()中向`UGameplayMessageSubsystem`注册Channel、Lambda、`TWeakObjectPtr<UScriptStruct> MessageStructType`、`EGameplayMessageMatch`。lambda主要作用就是触发OnMessageReceived委托的多播。
UK2Node_AsyncAction_ListenForGameplayMessages为其封装的蓝图节点。

View File

@@ -0,0 +1,48 @@
---
title: UE5 Lyra学习笔记(3)—GAS
date: 2022-08-09 13:55:20
tags: Lyra Gameplay
rating: ⭐️⭐️
---
# LyraGame
主要逻辑集中在这个模块与插件中本文先解析GAS相关与GameFeature的逻辑。
UWorldSubsystem实现了ULyraAudioMixEffectsSubsystem、
- LyraGameplayTags实现单例类 FLyraGameplayTags来管理GAS相关标签。感觉不如使用JSON导入DataTable Asset的方式来管理GameplayTag来得方便。
- LyraLogChannels实现LogLyra、LogLyraExperience、LogLyraAbilitySystem、LogLyraTeams日志标签以及 返回网络Role字符串函数。
## AbilitySystem
- Abilities
- Interaction
- Inventory
- Player
- Weapons
- UI
### Abilities
#### LyraGameplayAbility
- 实现5个ActorInfo获取函数GetLyraAbilitySystemComponentFromActorInfo、GetLyraPlayerControllerFromActorInfo、GetControllerFromActorInfo、GetLyraCharacterFromActorInfo、GetHeroComponentFromActorInfo。
定义ELyraAbilityActivationPolicy与ELyraAbilityActivationGroup枚举并在类内的定义枚举变量与取值函数。用于
c++中实现:
- ULyraGameplayAbility_Death执行类型为ServerInitiated逻辑为能力激活与结束时调用角色的ULyraHealthComponent的StartDeath()与FinishDeath()。
- ULyraGameplayAbility_Jump执行类型为LocalPredicted调用角色的Jump()、UnCrouch()、StopJumping()。
- ULyraGameplayAbility_Reset执行类型为ServerInitiated角色复活时调用的能力将各个属性恢复成初始值,调用角色Reset()之后通过UGameplayMessageSubsystem广播FLyraPlayerResetMessage消息。会在GAS组件接收到**GameplayEvent.RequestReset**事件时激活。
#### Animation
ULyraAnimInstance针对GAS进行了定制定义**FGameplayTagBlueprintPropertyMap**变量。可以在AnimBP中添加**FGameplayTagBlueprintPropertyMapping**,主要作用是:
- NativeInitializeAnimation获取Actor的GAS组件用于给FGameplayTagBlueprintPropertyMap变量进行初始化。
- NativeUpdateAnimation获取Actor的Movement组件通过GetGroundInfo()来获取Actor到地面的距离来给类的GroundDistance变量赋值。
- IsDataValid用判断AnimBP Asset以及GameplayTagPropertyMap变量是否有效。
### GameFeatures
Lyra中实现的
#### GameFeatureAction
#### GameModes
#### Input

View File

@@ -0,0 +1,191 @@
---
title: UE5 Lyra学习笔记(4)—Inventory
date: 2022-08-10 13:55:20
tags: Lyra Gameplay
rating: ⭐️⭐️
---
# 前言
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。
- FPickupInstanceInstance。
- 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指针。
代码如下:
```c++
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装备定义。
- FLyraEquipmentActorToSpawnSpawn用的相关数据。
- ULyraPickupDefinitionUDataAsset。主要是有ULyraInventoryItemDefinition以及其他美术资产指针。
- ULyraWeaponPickupDefinitionULyraPickupDefinition的子类储存Spawn用的LocationOffset与Scale数据。
- ULyraGameplayAbility_FromEquipmentEquipment类型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。
## FGameplayTagStack
Lyra里定义了FGameplayTagStackContainer 与FGameplayTagStack这2个类来解决GameplayTagStack的同步问题。
其中TagToCountMap只是本地/服务器用于快速查找GameplayTag的Stack数而维护的Map它会在Add/RemoveStack()之外还会在三个网络同步函数进行对应修改。
- AddStack()
- RemoveStack()
- 网络同步函数
- PreReplicatedRemove()
- PostReplicatedAdd()
- PostReplicatedChange()
## ItemDefintion是否可以做成DataAsset
蓝图类以及非蓝图类资产,例如关卡和数据资产(`UDataAsset` 类的资产实例)。
### 非蓝图资产
参考官方文档[AssetManagement](https://docs.unrealengine.com/4.27/zh-CN/ProductionPipelines/AssetManagement/)
**假如主要资产类型不需要存储蓝图数据,你可以使用非蓝图资产。非蓝图资产在代码中的访问更简单,而且更节省内存。** 如需在编辑器中新建一个非蓝图主资产,请在"高级"内容浏览器窗口中新建一个数据资产,或使用自定义用户界面来创建新关卡等。以这种方式创建资产与创建蓝图类不一样;你创建的资产是类的实例,而非类本身。要访问类,请用 `GetPrimaryAssetObject` 这类C++函数加载它们或者用蓝图函数名称中没有Class。一旦加载后你就可以直接访问它们并读取数据。
>因为这些资产是实例而不是类,所以你无法从它们继承类或其他资产。如果你要这样做,例如,如果你想创建一个子资产,继承其父类的值(除了那些显式覆盖的值),你应该使用蓝图类来代替。
## UI
Lyra没有制作背包部分的逻辑但预留了相关的实现。采用在
- `void FLyraInventoryList::PreReplicatedRemove(const TArrayView<int32> RemovedIndices, int32 FinalSize)`
- `void FLyraInventoryList::PostReplicatedAdd(const TArrayView<int32> AddedIndices, int32 FinalSize)`
- `void FLyraInventoryList::PostReplicatedChange(const TArrayView<int32> ChangedIndices, int32 FinalSize)`
调用BroadcastChangeMessage()来实现传入数据到其他组件的目的。
```c++
/** A message when an item is added to the inventory */
USTRUCT(BlueprintType)
struct FLyraInventoryChangeMessage
{
GENERATED_BODY()
//@TODO: Tag based names+owning actors for inventories instead of directly exposing the component?
UPROPERTY(BlueprintReadOnly, Category=Inventory)
UActorComponent* InventoryOwner = nullptr;
UPROPERTY(BlueprintReadOnly, Category = Inventory)
ULyraInventoryItemInstance* Instance = nullptr;
UPROPERTY(BlueprintReadOnly, Category=Inventory)
int32 NewCount = 0;
UPROPERTY(BlueprintReadOnly, Category=Inventory)
int32 Delta = 0;
};
void FLyraInventoryList::BroadcastChangeMessage(FLyraInventoryEntry& Entry, int32 OldCount, int32 NewCount)
{
FLyraInventoryChangeMessage Message;
Message.InventoryOwner = OwnerComponent;
Message.Instance = Entry.Instance;
Message.NewCount = NewCount;
Message.Delta = NewCount - OldCount;
UGameplayMessageSubsystem& MessageSystem = UGameplayMessageSubsystem::Get(OwnerComponent->GetWorld());
MessageSystem.BroadcastMessage(TAG_Lyra_Inventory_Message_StackChanged, Message);
}
```
### StatTags的作用
物品系统的ULyraInventoryItemInstance中的来记录某个Tag对应的Int32数量在蓝图中调用GetStatTagCount来获取对应的数量。
```c++
UPROPERTY(Replicated)
FGameplayTagStackContainer StatTags;
```
AddStatTagCount在Fragmentstat里被调用用于设置初始的话数量RemoveStatTagCount则在 GameplayEffect的cost中被调用。
PS.可以用来记录 血药的使用量重量32000 用一次=》30000

View File

@@ -0,0 +1,134 @@
---
title: UE5 Lyra学习笔记(5)—QuickBar与Equipment
date: 2022-09-16 11:03:03
excerpt:
tags:
rating: ⭐
---
## 前言
## ULyraQuickBarComponent
继承自UControllerComponentModularGame的给Controller使用的胶水组件
### 成员变量
```c++
//QuickBar Slot中的ItemInstance数据
UPROPERTY(ReplicatedUsing=OnRep_Slots)
TArray<TObjectPtr<ULyraInventoryItemInstance>> Slots;
//当前激活的SlotIndex
UPROPERTY(ReplicatedUsing=OnRep_ActiveSlotIndex)
int32 ActiveSlotIndex = -1;
//当前装备的EquipmentInstance
UPROPERTY()
TObjectPtr<ULyraEquipmentInstance> EquippedItem;
```
2个OnRep()会使用GameplayMessageRouter发送Message该Message最后会被UI接收并且进行对应处理。
### 函数
只说几个相对重要的函数:
- CycleActiveSlotForward激活正向的下一个QuickBarSlot判断Index是否有效后调用SetActiveSlotIndex()。
- CycleActiveSlotBackward激活反向的上一个QuickBarSlot判断Index是否有效后调用SetActiveSlotIndex()。
- EquipItemInSlot装备指定的物品通过`Slot[ActiveSlotIndex]`获取到ItemInstanceItemInstance通过Fragment获取到EquipmentDefinition后用EquipmentManager调用函数EquipItem()来装备调用FindEquipmentManager获取
- UnequipItemInSlot脱下指定的物品EquipmentManager调用UnequipItem(EquippedItem)。
- FindEquipmentManager`Cast<AController>(GetOwner()))->GetPawn()->FindComponentByClass<ULyraEquipmentManagerComponent>()`
- SetActiveSlotIndex_Implementation含有`Server, Reliable`标记,依次调用`UnequipItemInSlot(); ActiveSlotIndex = NewIndex; EquipItemInSlot(); OnRep_ActiveSlotIndex(); `
- AddItemToSlot增加QuickBarSlot的ItemInstance数据并调用OnRep_Slots()。
- RemoveItemFromSlot如果处于装备状态会先调用UnequipItemInSlot()之后移除QuickBarSlot的ItemInstance数据并调用OnRep_Slots()。
### QuickBarUI
- W_QuickBar(组合控件)快捷栏主控件用于嵌套W_QuickBarSlot。
- W_QuickBarSlot
1. 通过GameplayMessageRouter读取FLyraQuickBarActiveIndexChangedMessage数据QuickBar组件的消息获取Payload拥有者是否与UI拥有者相同来播放对应的UI动画。
2. 通过GameplayMessageRouter读取FLyraQuickBarSlotsChangedMessage数据QuickBar组件的消息获取Payload拥有者以及ItemInstance数据通过ItemInstance找到到指定的Fragment来获取图标资源来更新UI的图标。
- W_WeaponAmmoAndName取得角色的QuickBar组件之后获取ActiveSlot的ItemInstance获取对应的StatTag来设置显示TextMagazineAmmo、SpareAmmo再通过Fragment找到对应的图标资产。
- 其他控件:
- W_ActionTouchButton通过GameplayMessageRouter读取消息获得Duration值。
## ULyraEquipmentManagerComponent
继承自UPawnComponent。ModularGame的给Pawn使用的胶水组件
### 成员变量
### 相关逻辑
- UI
- 获取角色对应组件逻辑:`GetOwningPlayer->GetComponentByClass<ULyraQuickBarComponent>()`
- C++
- Controll组件获取到角色组件方法`Cast<AController>(GetOwner()))->GetPawn()->FindComponentByClass<ULyraEquipmentManagerComponent>()`
### ItemInstance获取Pawn的方法
```c++
void ULyraEquipmentInstance::OnUnequipped()
{
K2_OnUnequipped();
}
APawn* ULyraEquipmentInstance::GetPawn() const
{
return Cast<APawn>(GetOuter());
}
APawn* ULyraEquipmentInstance::GetTypedPawn(TSubclassOf<APawn> PawnType) const
{
APawn* Result = nullptr;
if (UClass* ActualPawnType = PawnType)
{
if (GetOuter()->IsA(ActualPawnType))
{
Result = Cast<APawn>(GetOuter());
}
}
return Result;
}
```
## UI从ItemDefinition获取数据的方式
调用ItemInstance->FindFragmentByClass再从指定的FragmentClass中获取数据。
QuickBar使用了QuickBarIcon
```c++
void ULyraQuickBarComponent::EquipItemInSlot()  
{  
   check(Slots.IsValidIndex(ActiveSlotIndex));  
   check(EquippedItem == nullptr);  
   if (ULyraInventoryItemInstance* SlotItem = Slots[ActiveSlotIndex])  
   {      
        if (const UInventoryFragment_EquippableItem* EquipInfo = SlotItem->FindFragmentByClass<UInventoryFragment_EquippableItem>())  
        {        
            TSubclassOf<ULyraEquipmentDefinition> EquipDef = EquipInfo->EquipmentDefinition;  
            if (EquipDef != nullptr)  
            {            
                if (ULyraEquipmentManagerComponent* EquipmentManager = FindEquipmentManager())  
                {              
                    EquippedItem = EquipmentManager->EquipItem(EquipDef);  
                    if (EquippedItem != nullptr)  
                    {                  
                        EquippedItem->SetInstigator(SlotItem);  
                    }            
                }        
            }    
        }  
    }
}
```
### QuickBar UI更新机制
Lyra选择 tick来 getSlot
```c++
TArray<ULyraInventoryItemInstance*> GetSlots() const
{
return Slots;
}
UPROPERTY(ReplicatedUsing=OnRep_Slots)
TArray<TObjectPtr<ULyraInventoryItemInstance>> Slots;
```
之后根据Slot进行设置图标数据或者将其设置成null。
PS.所以我打算这么做ItemUISlot设置成null之后遍历消除nullslot

View File

@@ -0,0 +1,56 @@
---
title: 《Lyra初学者游戏包工程解读》 | quabqi 视频笔记
date: 2022-08-09 13:55:20
tags: Lyra
rating: ⭐️
---
# 《Lyra初学者游戏包工程解读》 | quabqi 视频笔记
## ModularGameplay
- LyraGameMode/LyraGameState/LyrrPlayerState/LyrrPlayerStart
- LyraChracter/LyraPawn/LyraPawnComponent
- LyraPlayerController/LyraPlayerBotController
## GameFeatures
GameFeatures=数据驱动+组件化通过配置自动挂载组件。GameFeatureData - Actions
- ShooterCore
- ShooterMap
- TopDownArena
## Experience
一个GameFeature包含多个ExperienceExperience通过Level或者房主指定所有业务都通过配置定义。
配表以实现数据驱动,几乎所有的逻辑都通过配置动态加载。
- LyraWorldSettings
- LyraUserFacingExperienceDefinition
## Character、Controller、Camera
只提供空壳Class基类用来挂载各种Component。不再堆积大量业务逻辑只做基本的消息转发逻辑。
Controller上使用了输入增强组件通过触发IA(inputAction)发送GameplayTag角色收到这些GameplayTag就会激活对应的GA。通过MapingContext这种数据表的方式避免了进行大量if-else的判断。
## Lyra上的换装系统
换装的数据由服务器创建并同步到客户端客户端收到后Spawn对应Actor并且Attach到主Mesh上主Mesh只有骨骼没有实际模型。
## Lyra动画
- 多线程动画UE5新增一个蓝图子线程Update函数BlueprintThreadSafeUpdateAnimation。
- 动画分层:对动画蓝图进行分层,之后通过**PickBestAnimLayer**与**LinkAnimClassLayer**进行动态连接。
- 新的LayerBlendPerBone节点可以调整每个骨骼的混合权重。
- UE5动画节点可以绑定回调函数了。
- 因为主SkeletalMesh没有模型所有在其他动画蓝图中需要使用CopyPose将父组件的动画复制过来。
## 网络同步
- GE尽量不开Full模式改用Mixed/Minimal
有些数据在外部同步可以显著减少流量
## Lyra UI
- 主UI分为4层模态弹出框的分层逻辑位于W_OverallUILayout
- 提供一种UI挂点方案支持按需求加载不同的UI。实现在UIExtension**建议复用**。
### Common UI组件建议复用
- 支持异步加载资源
- 支持UI的Style配置
- 支持Widget池复用UI
- 支持根据硬件自动适配,显示/隐藏
- 支持Action
- 支持返回键/自动关闭
- 可见性切换器
- Tab页