UE Lyra
前言
本篇为Lyra 的解析 目前解析了:
- UI
- Dash
UI 框架
前言
Lyra项目中的UI功能主要由游戏UI管理子系统(GameUIManagerSubsystem),游戏UI策略(GameUIPolicy),游戏布局UI([PrimaryGameLayout]三个部分构成UI框架,CommonUI插件以及UIExtension插件负责UI层面的使用。
在插件中主要有三个类:
PrimaryGameLayout
GameUIManagerSubsystem
GameUIPolicy
与UI相关的类在Lyra项目中的继承关系如下:
GamePlay
- B_LyraGameInstance(蓝图) -> LyraGameInstance -> CommonGameInstance ->GameInstance
- LyraLocalPlayer -> CommonLocalPlayer ->LocalPlayer
- LyraPlayerController -> CommonPlayerController -> ModularPlayerController -> PlayerController
UIMangaer
LyraUIManagerSubsystem -> GameUIManagerSubsystem ->GameInstanceSubsystem
B_LyraUIPolicy(蓝图) -> GameUIPolicy -> Object
W_OverallUILayout(蓝图)-> PrimaryGameLayout -> CommonUserWidget(CommonUI插件中的类)
- 类作用
- GameUIManagerSubsystem负责GameUIPolicy的配置管理、生命周期等;
- GameUIPolicy负责PrimaryGameLayout的配置管理、生命周期等;
- PrimaryGameLayout负责游戏中UI的分层布局等;
Subsystem
GameUIManagerSubsystem
1.判断创建哪一个游戏UI管理子系统
在ShouldCreateSubsystem函数中会判断子系统是否有子类,如果没子类就创建该子系统,有子类就不创建该子系统,由于Lyra项目创建了LyraUIManagerSubsystem并继承GameUIManagerSubsystem,所以项目会创建LyraUIManagerSubsystem而不会创建GameUIManagerSubsystem.
由于GameUIManagerSubsystem标记了Abstract宏,所以GameUIManagerSubsystem默认不会创建,所以需要自己继承一个或者修改掉插件里的宏定义。
- ```cpp
UCLASS(MinimalAPI, Abstract, config = Game)
class UGameUIManagerSubsystem : public UGameInstanceSubsystem1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
2.配置GameUIPolicy(游戏策略)类
- 在游戏UI管理子系统中有一个GameUIPolicy(游戏策略)类的配置项,配置位置在DefaultGame.ini文件中

# Dash
[(99+ 封私信 / 80 条消息) 基于 Unreal Lyra 的 GAS 属性扩展和应用 - 知乎](https://zhuanlan.zhihu.com/p/643301579)
- ShooterCore
- IMC_Default
- IA_Ability_Dash
- LyraHeroComponent::InitializePlayerInput()
- LyraIC->BindAbilityActions(InputConfig, this, &ThisClass::Input_AbilityInputTagPressed, &ThisClass::Input_AbilityInputTagReleased, /*out*/ BindHandles);
- ULyraAbilitySystemComponent::AbilityInputTagPressed -- ASC
- for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items) 遍历所有能力(AbilitySpec)
- 有对应InputTag 进入
- UAbilitySystemComponent::TryActivateAbility -- ASC
- GA_Hero_Dash (在插件文件夹中) -- 蓝图
- Select Directional Montage 选择蒙太奇
- Play Montage And Wait 播放动画
- UAbilityTask_PlayMontageAndWait* UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(..)
- UAbilitySystemComponent::PlayMontage
- Apply Root Motion Constant Force 位移
- MovementComponent->ApplyRootMotionSource(ConstantForce)
# 一次攻击流程
[(99+ 封私信 / 80 条消息) UE5 GAS - 以Lyra为例 - 知乎](https://zhuanlan.zhihu.com/p/1934717850632823338)
- BT_Lyra_Shooter_Bot
- BTS_Shoot
- Tick
- Send Gameplay Event to Actor (传入Event.Tag -- FireTag)
- UAbilitySystemComponent::**HandleGameplayEvent**(FGameplayTag EventTag, const FGameplayEventData* Payload) 常用接口
- UAbilitySystemComponent::TriggerAbilityFromGameplayEvent(..)
- UAbilitySystemComponent::InternalTryActivateAbility(..)
- UGameplayAbility::CallActivateAbility(..)
- K2_ActivateAbility() 调用GA
- GA_Weapon_Fire (父类)
- GA_Weapon_Fire_Rifle_Auto(步枪) 数据类实际逻辑在父类中
- 继承链:GA_Weapon_Fire_Rifle_Auto -> GA_Weapon_Fire -> ULyraGameplayAbility_RangedWeapon -> ULyraGameplayAbility_FromEquipment -> ULyraGameplayAbility
- GA_Weapon_Fire 蓝图中 Start Ranged Weapon Targeting
- ULyraGameplayAbility_RangedWeapon::StartRangedWeaponTargeting()
- ULyraGameplayAbility_RangedWeapon::OnTargetDataReadyCallback()
- 射线检测找出一个他需要攻击的目标
- OnRangedWeaponTargetDataReady() -- 蓝图调用
- 蓝图中:On Ranged Weapon Target Data Ready
- Apply Gameplay Effect To Target 通过GA叠加GE (导入GE Damage)
- ULyraHealthSet::PostGameplayEffectExecute()
- 射线检测
- 生命值 <0 死亡 (死亡也是一个GA)
- ULyraHealthComponent::HandleOutOfHealth
- 通过委托去播死亡GA HandleGameplayEvent()
- LyraGameplayAbility_Death -> ULyraGameplayAbility
```cpp
void ULyraHealthSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
// 调用父类实现以保留基础后处理逻辑
Super::PostGameplayEffectExecute(Data);
// 判断此次效果是否带有“自爆伤害”标签(用于绕过某些免伤/作弊检测)
const bool bIsDamageFromSelfDestruct = Data.EffectSpec.GetDynamicAssetTags().HasTagExact(TAG_Gameplay_DamageSelfDestruct);
// 初始化最小允许生命值,默认 0(有些作弊或调试模式下会设置为 1)
float MinimumHealth = 0.0f;
#if !UE_BUILD_SHIPPING
// 在非发行构建中,检查 GodMode 或 UnlimitedHealth 作弊标签(自爆伤害仍然有效)
if (!bIsDamageFromSelfDestruct &&
(Data.Target.HasMatchingGameplayTag(LyraGameplayTags::Cheat_GodMode) || Data.Target.HasMatchingGameplayTag(LyraGameplayTags::Cheat_UnlimitedHealth)))
{
// 如果满足作弊条件,则把最小生命值设为 1,防止被伤害致死(仅调试/作弊用途)
MinimumHealth = 1.0f;
}
#endif // #if !UE_BUILD_SHIPPING
// 获取效果上下文(包含 instigator、causer 等信息)
const FGameplayEffectContextHandle& EffectContext = Data.EffectSpec.GetEffectContext();
// 从上下文获取原始施放者(可能为触发该效果的 actor)
AActor* Instigator = EffectContext.GetOriginalInstigator();
// 从上下文获取直接造成者(EffectCauser)
AActor* Causer = EffectContext.GetEffectCauser();
// 如果该效果修改的是 Damage 属性,进入伤害处理分支
if (Data.EvaluatedData.Attribute == GetDamageAttribute())
{
// 若伤害量大于 0,发送一个标准化的伤害消息供其他系统观察/记录
if (Data.EvaluatedData.Magnitude > 0.0f)
{
// 构造 Lyra 的动词消息结构并填充信息
FLyraVerbMessage Message;
Message.Verb = TAG_Lyra_Damage_Message; // 设置动词标签为 Lyra 的伤害消息标签
Message.Instigator = Data.EffectSpec.GetEffectContext().GetEffectCauser(); // 填充 instigator(causer)
Message.InstigatorTags = *Data.EffectSpec.CapturedSourceTags.GetAggregatedTags(); // 填充 instigator 的捕获标签
Message.Target = GetOwningActor(); // 填充目标 Actor(属性集的拥有者)
Message.TargetTags = *Data.EffectSpec.CapturedTargetTags.GetAggregatedTags(); // 填充目标的捕获标签
// @TODO: 补充上下文标签,以及来自非能力系统的来源/施放者标签
// @TODO: 判断是否为敌对队伍击杀、自杀、队友击杀等情形
Message.Magnitude = Data.EvaluatedData.Magnitude; // 填充伤害数值
// 获取全局消息子系统并广播该消息
UGameplayMessageSubsystem& MessageSystem = UGameplayMessageSubsystem::Get(GetWorld());
MessageSystem.BroadcastMessage(Message.Verb, Message);
}
// 将 Damage 转换为对 Health 的减值,并在 MinimumHealth 与 MaxHealth 之间夹紧后写回
SetHealth(FMath::Clamp(GetHealth() - GetDamage(), MinimumHealth, GetMaxHealth()));
// 清除已消费的 Damage 值
SetDamage(0.0f);
}
else if (Data.EvaluatedData.Attribute == GetHealingAttribute())
{
// 如果是 Healing 属性,将其作为对 Health 的加值并夹紧到合法范围
SetHealth(FMath::Clamp(GetHealth() + GetHealing(), MinimumHealth, GetMaxHealth()));
// 清除已消费的 Healing 值
SetHealing(0.0f);
}
else if (Data.EvaluatedData.Attribute == GetHealthAttribute())
{
// 如果直接修改的是 Health,则对其进行夹紧(后续会有掉血处理)
SetHealth(FMath::Clamp(GetHealth(), MinimumHealth, GetMaxHealth()));
}
else if (Data.EvaluatedData.Attribute == GetMaxHealthAttribute())
{
// 如果修改的是 MaxHealth,触发最大生命变化通知(是否同时夹紧当前生命由 TODO 决定)
OnMaxHealthChanged.Broadcast(Instigator, Causer, &Data.EffectSpec, Data.EvaluatedData.Magnitude, MaxHealthBeforeAttributeChange, GetMaxHealth());
}
// 若 Health 实际变化,触发 OnHealthChanged 回调,通知监听者变更前后值
if (GetHealth() != HealthBeforeAttributeChange)
{
OnHealthChanged.Broadcast(Instigator, Causer, &Data.EffectSpec, Data.EvaluatedData.Magnitude, HealthBeforeAttributeChange, GetHealth());
}
// 若当前 Health 掉至 0 且之前没有处于无生命状态,则触发 OnOutOfHealth 事件
if ((GetHealth() <= 0.0f) && !bOutOfHealth)
{
OnOutOfHealth.Broadcast(Instigator, Causer, &Data.EffectSpec, Data.EvaluatedData.Magnitude, HealthBeforeAttributeChange, GetHealth());
}
// 再次检查当前 Health,以防上面事件回调修改了生命值;更新 bOutOfHealth 标志
bOutOfHealth = (GetHealth() <= 0.0f);
}
- ```cpp
Lyra架构
(99+ 封私信 / 80 条消息) UE5 Lyra - 基础架构和各个关键节点 - 知乎
UE5 的 Lyra Starter Game 项目是一个极其重要的官方示例,它展示了构建一个现代、健壮、可扩展、多平台的多人游戏所需的最佳实践和核心架构。理解 Lyra 是深入掌握 UE5 高级游戏开发的关键。
以下是对 Lyra 项目架构和关键节点的详细讲解:
核心设计理念
- 模块化与插件化: 功能被拆分成独立的插件 (
Game Features,Modular Gameplay),便于复用、组合、热更新和团队并行开发。 - Gameplay Ability System (GAS) 为核心: 战斗、技能、状态、效果、属性等核心游戏逻辑高度依赖 GAS 实现。
- 数据驱动: 大量使用数据资产定义游戏行为、角色能力、UI 等,减少硬编码,提高可配置性和可迭代性。
- 前端与后端分离: 清晰的 UI/表现层与游戏逻辑/数据层的分离。
- 支持多种游戏模式: 架构设计易于扩展,支持如 PvP、PvE、大逃杀等多种模式。
- 平台抽象: 对输入、在线子系统、社交功能等进行了良好封装,便于跨平台。
核心架构层次与关键节点
1. 游戏基础框架
LyraGameMode/LyraGameState:- 游戏规则和全局状态的核心管理者。
LyraGameMode负责游戏流程(匹配、回合、胜利条件等)。它本身逻辑相对轻量,更多是协调各个Game Feature。LyraGameState存储所有玩家共享的、与游戏规则相关的状态(如当前游戏阶段、剩余时间、团队分数、全局事件等)。
LyraPlayerController/LyraPlayerState:LyraPlayerController: 代表一个玩家在服务器和其对应的客户端上的存在。处理玩家输入、生成 Pawn、复制非 Pawn 相关的玩家状态到客户端、管理 UI、处理相机等。它充当客户端与游戏世界之间的桥梁。LyraPlayerState: 存储单个玩家的持久化状态数据,如玩家名称、分数、K/D/A、等级、选择的英雄/角色、装备等。这些数据会在服务器和所有相关客户端之间复制,让所有玩家都能看到彼此的信息。它通常持有该玩家的AbilitySystemComponent。
LyraPawn/LyraPawnExtensionComponent/LyraHeroComponent:LyraPawn: 玩家或 AI 在游戏世界中的物理表现(可被操控的角色或物体)。在 Lyra 中,LyraPawn本身逻辑较少。LyraPawnExtensionComponent: 这是 Lyra 架构的一个关键创新点。它作为 Pawn 的核心“协调器”,负责:- 管理 Pawn 的生命周期状态(初始化、重生、死亡等)。
- 动态加载和卸载与 Pawn 关联的
Game Feature插件提供的组件。 - 持有并管理 Pawn 的
AbilitySystemComponent。 - 协调其他关键组件(如
LyraHeroComponent)的初始化和交互。
LyraHeroComponent: 附加到代表“英雄”角色的 Pawn 上。它负责:- 加载和应用玩家选择的
LyraPawnData。 - 设置输入映射上下文。
- 初始化相机模式。
- 管理装备外观等。它是连接
PlayerState(存储选择)和Pawn(表现选择)的桥梁。
- 加载和应用玩家选择的
LyraPawnData: 一个关键的数据资产。它定义了特定“英雄”或“角色类型”的核心配置:- 使用的
Character Class。 - 默认的
Input Mapping Context。 - 初始的
Ability Sets。 - 初始的
Camera Mode。 - 初始的
Equipment/Cosmetics。 - 关联的
UI。 通过LyraHeroComponent应用。
- 使用的
2. Game Feature 插件系统 (核心架构支柱)
概念: 这是 Lyra 最强大的架构之一。它将离散的游戏功能(如一种武器、一个技能、一个游戏模式、一套UI、一个交互系统)封装成独立的插件。
关键节点:
GameFeaturePlugin: UE 插件的基础。UGameFeatureData: 定义插件内容的核心数据资产。它指定了插件激活时需要加载和注册的内容:Actions: 插件激活/停用时执行的操作。Lyra 大量使用了UGameFeatureAction_AddComponents和UGameFeatureAction_AddAbilities。AddComponents: 动态地将特定组件添加到符合条件的 Actor(如 Pawn, PlayerState, GameMode)上。这是实现模块化 Pawn 的关键!AddAbilities: 动态地将GameplayAbility和GameplayEffect授予符合条件的 Actor(通常是 Pawn 或 PlayerState)。
PrimaryAssetTypesToScan: 定义插件管理的资源类型(如LyraExperienceDefinition)。
GameFeaturePluginStateMachine: 引擎内部管理插件生命周期(加载、注册、激活、卸载)的状态机。
工作流程:
- 一个 Game Feature 插件被加载和激活。
- 其
UGameFeatureData被读取。 - 其中定义的
Actions被执行。 - 例如:
AddComponentsAction 检测到符合条件的 Pawn,并将该插件定义的组件(如一个武器组件、一个特殊技能组件)动态添加到该 Pawn 上。AddAbilitiesAction 将技能和效果授予该 Pawn。
优势:
- 功能解耦: 新功能作为独立插件开发,无需修改核心游戏代码。
- 动态组合: 运行时根据需要加载/卸载功能(如不同地图加载不同插件)。
- 热更新: 理论上可以单独更新一个功能插件。
- 团队协作: 不同团队负责不同插件。
- DLC/Mod 支持: 天然支持扩展内容。
3. 体验系统
概念: 定义在特定游戏会话中应用哪些
Game Feature插件和配置。关键节点:
ULyraExperienceDefinition:****核心数据资产。代表一种“游戏体验”,如“默认团队死亡竞赛”、“训练场”、“特定活动模式”。GameFeaturesToEnable: 指定此体验需要激活哪些Game Feature插件。DefaultPawnData: 指定此体验中玩家默认使用的LyraPawnData。Actions: 类似GameFeatureData的 Actions,但作用于整个体验级别,可用于体验特定的初始化。
ULyraExperienceManagerComponent: 通常存在于GameState上。负责加载、应用和管理当前游戏会话的LyraExperienceDefinition。
工作流程:
- 游戏开始时(如
GameMode初始化),确定要加载哪个LyraExperienceDefinition(通常由地图或游戏模式决定)。 LyraExperienceManagerComponent加载该 Experience 资产。- Experience 加载其
GameFeaturesToEnable列表中的所有GameFeaturePlugin。 - 激活的
GameFeaturePlugin执行它们自己的Actions。 - Experience 自身也可能执行其定义的
Actions。 - 最终结果是,为该特定游戏会话组合并激活了一组特定的
GameFeature插件及其功能。
- 重要性: 这是 Lyra 数据驱动和动态组合的核心。通过切换 Experience,你可以完全改变游戏的规则、可用角色、武器、技能等。
4. Gameplay Ability System (GAS) 集成
渗透性: GAS 是 Lyra 战斗、技能、状态、属性系统的基石,几乎无处不在。
关键节点:
AbilitySystemComponent: 存在于LyraPlayerState(玩家状态相关的能力/效果) 和LyraPawnExtensionComponent(Pawn 实例相关的能力/效果) 上。处理所有 GAS 逻辑:激活技能、应用效果、管理属性、处理预测。LyraGameplayAbility/LyraGameplayEffect: Lyra 自定义的基础UGameplayAbility和UGameplayEffect类,添加了项目特定的功能或约定。LyraAbilitySet:****关键数据资产。定义一组GameplayAbility和GameplayEffect。它本身不授予能力,而是提供一种分组方式。LyraPawnData和GameFeatureAction_AddAbilities使用AbilitySet来授予初始能力。GameFeatureAction_AddAbilities: 如上所述,是GameFeature或Experience动态授予AbilitySet中定义的能力和效果的主要机制。
流程示例 (玩家获得武器):
- 武器功能封装在一个
GameFeaturePlugin中。 - 该插件的
GameFeatureData包含一个AddComponentsAction,指定当 Pawn 初始化时,将特定的“武器组件”添加到 Pawn 上。 - 该插件的
GameFeatureData还包含一个AddAbilitiesAction,指定当 Pawn 初始化时,授予它一个包含“开火”、“换弹”等技能的AbilitySet。 - 当玩家选择该武器或进入包含该武器插件的体验时,这些组件和能力被动态添加。
5. 输入系统
LyraInputConfig:****关键数据资产。将InputAction映射到GameplayTag。例如,IA_Jump动作映射到InputTag.Jump标签。LyraInputComponent: 自定义的UEnhancedInputComponent。它使用LyraInputConfig来绑定输入动作,并将输入事件转化为触发关联GameplayTag的Ability。InputMappingContext: 定义具体的键位/设备绑定到InputAction。在LyraPawnData中指定。- 流程:
LyraHeroComponent应用LyraPawnData中指定的InputMappingContext到玩家控制器。LyraInputComponent使用LyraInputConfig监听绑定的InputAction。- 当输入触发时,
LyraInputComponent根据配置找到对应的InputTag。 - 该
InputTag被广播出去。 - 拥有
AbilitySystemComponent的 Actor (Pawn 或 PlayerState) 上,那些被AbilityTags或ActivationOwnedTags或TriggerTags匹配的GameplayAbility会被尝试激活。
6. UI 系统
Common UI框架: Lyra 深度集成了 Common UI 插件,用于构建健壮、可扩展、支持多平台输入和本地化的 UI。LyraHUD: 游戏内 HUD 的主要管理者。LyraUICameraManager: 管理与 UI 渲染相关的相机设置。LyraWidgetFactory/Layout Widgets: 用于根据数据动态创建和布局 UI 元素(如玩家状态栏、记分板条目)。GameplayMessage子系统: UI 经常监听GameplayMessage(如“玩家死亡”、“目标达成”)来更新显示。这是一种松耦合的通信方式。
7. 资产与加载
Primary Data Assets: Lyra 大量使用主数据资产来定义核心游戏对象(LyraPawnData,LyraExperienceDefinition,LyraInputConfig,LyraAbilitySet,Cosmetics等)。它们有唯一的PrimaryAssetId。Asset Manager: 负责加载、卸载和管理主数据资产。LyraExperienceManagerComponent和GameFeature系统都依赖AssetManager来加载所需的PrimaryAssets。
总结与关键点
GameFeaturePlugin是基石: 所有离散功能都封装于此,实现模块化和动态组合。LyraExperienceDefinition是组合器: 定义一次游戏会话需要哪些GameFeature和配置。LyraPawnExtensionComponent是 Pawn 的协调中心: 管理生命周期、动态组件加载和 ASC。LyraPawnData定义角色模板: 配置输入、能力、相机、外观等。LyraHeroComponent应用玩家选择: 将PlayerState中的选择(哪个英雄)应用到Pawn(使用LyraPawnData)。Gameplay Ability System驱动逻辑: 技能、状态、属性、战斗的核心。GameFeatureAction是动态注入机制: 特别是AddComponents和AddAbilities,使GameFeature和Experience能够动态修改 Actor。- 数据驱动: 几乎所有行为都由数据资产 (
LyraPawnData,Experience,InputConfig,AbilitySet,GameFeatureData) 配置。 - 输入绑定到
GameplayTag:LyraInputConfig和LyraInputComponent将输入事件转化为 GAS 可识别的标签。 Common UI构建前端: 提供强大的 UI 框架支持。
学习建议
- 从
ShooterCore开始: 这是 Lyra 的核心 GameFeature,包含了最基本的角色、武器、输入、UI。 - 追踪一个 Pawn 的创建: 从
GameMode生成PlayerController->PlayerController生成Pawn->LyraPawnExtensionComponent初始化 -> 应用LyraPawnData(LyraHeroComponent) -> 加载关联GameFeature->GameFeatureAction添加组件和能力。 - 分析一个简单 Ability: 比如跳跃或开火。看它的
InputTag如何配置 (InputConfig),如何被授予 (PawnData或GameFeatureAction),如何被输入触发,如何执行逻辑。 - 理解
LyraExperienceDefinition的加载流程: 看LyraExperienceManagerComponent如何加载 Experience,Experience 如何激活 GameFeatures。 - 使用调试工具: UE 的
GameplayDebugger(按 ``` 键) 对查看 GAS 状态(属性、Granted Abilities/Effects、Tags)非常有用。ShowDebug AbilitySystem控制台命令也很关键。 - 查看官方文档和源码注释: Epic 在 Lyra 源码中留下了大量有价值的注释,是理解设计意图的最佳资料。
Lyra 的架构代表了当前 UE5 多人动作游戏开发的先进理念和实践。虽然学习曲线陡峭,但深入理解其设计将对你的 UE5 项目开发能力产生质的飞跃。

