Init
This commit is contained in:
93
03-UnrealEngine/Gameplay/Lyra/UE5 Lyra学习笔记(1)—LyraEditor.md
Normal file
93
03-UnrealEngine/Gameplay/Lyra/UE5 Lyra学习笔记(1)—LyraEditor.md
Normal 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模块。
|
97
03-UnrealEngine/Gameplay/Lyra/UE5 Lyra学习笔记(2)—LyraPlugins.md
Normal file
97
03-UnrealEngine/Gameplay/Lyra/UE5 Lyra学习笔记(2)—LyraPlugins.md
Normal 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控件中设置参插槽参数;
|
||||
|
||||

|
||||

|
||||
|
||||
在LyraExperienceActionSet中设置AddWidgets(LayoutClass、LayoutID;WidgetClass、SlotID),最后会通过`UCommonUIExtensions::PushContentToLayer_ForPlayer`往屏幕上增加Widget。
|
||||

|
||||
|
||||
这样设计的其中一个优点在于实现解耦,例如:在相关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 UI:https://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 ID(int32 ID)、MessageChannel(FGameplayTag 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为其封装的蓝图节点。
|
48
03-UnrealEngine/Gameplay/Lyra/UE5 Lyra学习笔记(3)—GAS.md
Normal file
48
03-UnrealEngine/Gameplay/Lyra/UE5 Lyra学习笔记(3)—GAS.md
Normal 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
|
191
03-UnrealEngine/Gameplay/Lyra/UE5 Lyra学习笔记(4)—Inventory.md
Normal file
191
03-UnrealEngine/Gameplay/Lyra/UE5 Lyra学习笔记(4)—Inventory.md
Normal 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。
|
||||
- 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指针。
|
||||
|
||||
代码如下:
|
||||
```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:装备定义。
|
||||
- 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。
|
||||
|
||||
## 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在Fragment(stat)里被调用,用于设置初始的话数量,RemoveStatTagCount则在 GameplayEffect的cost中被调用。
|
||||
|
||||
PS.可以用来记录 血药的使用量,重量32000 用一次=》30000
|
@@ -0,0 +1,134 @@
|
||||
---
|
||||
title: UE5 Lyra学习笔记(5)—QuickBar与Equipment
|
||||
date: 2022-09-16 11:03:03
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
## 前言
|
||||
|
||||
|
||||
## ULyraQuickBarComponent
|
||||
继承自UControllerComponent(ModularGame的给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]`获取到ItemInstance,ItemInstance通过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来设置显示Text(MagazineAmmo、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
|
56
03-UnrealEngine/Gameplay/Lyra/《Lyra初学者游戏包工程解读》quabqi 视频笔记.md
Normal file
56
03-UnrealEngine/Gameplay/Lyra/《Lyra初学者游戏包工程解读》quabqi 视频笔记.md
Normal 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包含多个Experience;Experience通过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页
|
Reference in New Issue
Block a user