ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

UE5多人游戏开发必学:GAS技能系统核心概念与实战详解

UE5多人游戏开发必学:GAS技能系统核心概念与实战详解 1. 为什么要在UE5多人游戏中使用GAS1.1 多人游戏开发的核心痛点在虚幻引擎5中做多人游戏开发很多新手会遇到一个很典型的问题技能、属性、Buff、冷却这些逻辑到底该写在客户端还是服务器如果写错地方轻则出现表现不一致重则直接造成外挂漏洞或逻辑错乱。举个例子如果你想实现一个“向前冲刺”技能如果技能逻辑只写在客户端那么玩家可以通过修改本地内存或修改代码逻辑让冲刺距离变成无限远。如果技能逻辑只写在服务器那么客户端角色移动会出现明显的延迟感甚至回弹体验极差。更麻烦的是当游戏拥有多个技能、血量、蓝量、攻击力、防御、Buff、Debuff、伤害计算、冷却时间、技能切换、多人联机同步等复杂需求时如果每个技能都自己写一套逻辑项目会迅速变成难以维护的“屎山”。这些问题不是靠单纯堆代码能解决的而是需要一个框架来约束和规范。1.2 GAS到底解决了什么问题GAS 是 Gameplay Ability System 的缩写直译为“游戏能力系统”是虚幻引擎中一套专门用于技能、属性和游戏效果管理的框架。它最初来自虚幻引擎的示例项目 Action RPG 后来被官方整合进引擎成为多人动作游戏和角色扮演游戏开发的标配框架。GAS 的核心设计思想是把游戏中的“技能”抽象成可复用的对象。它把传统的技能逻辑拆分成几个核心部件AbilitySystemComponentASC技能系统的“大脑中枢”负责管理当前角色拥有的技能、属性和效果。GameplayAbilityGA技能自身的逻辑比如“释放火球术”。GameplayEffectGE属性的修改器比如“火球术命中后扣血 20 点”。AttributeSetAS属性集合比如血量、蓝量、攻击力。GameplayTag游戏标签用于技能之间的控制和标记。GameplayCue表现通知比如命中时播放特效、音效。这个框架最强大的地方在于它天生为多人游戏而设计。服务器和客户端的同步、技能触发权限、属性修改验证、技能冷却管理这些在原生 GAS 中都有成熟的解决方案。关于 GAS 在国内的学习资料市面上一部分来自英文文档和社区教程的翻译整理。像“CodeX精翻”这类系列文章本质上就是把国外优秀的 UE5 教程系统性翻译并补充中文笔记方便国内开发者按顺序学习。本文也会采用类似“精翻实战笔记”的方式把 GAS 的核心概念和多人游戏开发最佳实践整理成一套可直接落地的方案。1.3 本文适合的读者和前置要求这篇文章适合以下读者已经掌握 UE5 基本操作想进一步学习多人游戏开发的初中级开发者。在单机游戏项目中已经写过技能系统但无法简单迁移到多人游戏的开发者。准备在工作中使用 GAS但被官方文档和国外视频教程劝退的学习者。想要了解 UE5 C 工程化开发方式的 C 后端开发者。前置要求熟悉 UE5 编辑器基本界面和蓝图逻辑。有一定的 C 基础至少知道类、继承、虚函数、指针和引用的区别。知道如何在 UE5 中创建 C 类并编译项目。即使你目前 C 还不够熟练也可以先通过蓝图部分理解 GAS 的运行逻辑然后再逐步替换成 C 实现。2. 环境准备与开发环境搭建2.1 UE5版本与项目类型选择本文以Unreal Engine 5.0 / 5.1 以上版本为例因为从 UE5.0 开始引擎内置了对 GAS 更完善的支持特别是 Lyra 示例项目发布后GAS 在很多工程中的使用方式有了更成熟的参考。创建项目时建议选择C 基础模板例如 Third Person 模板。选择 C 模板的目的是后续可以直接创建 C 类并编写 GAS 相关代码避免纯蓝图项目在引用 C 类时造成不必要的切换麻烦。不过需要提醒的是UE5 每个小版本之间的 API 可能有微调。例如某些引擎函数在 5.0 和 5.3 之间存在废弃或重命名。后续示例代码提供的是核心思路具体到你本地的版本如果编译报错建议优先查看引擎源码中对应类的头文件确认函数签名。2.2 C开发环境配置UE5 C 开发在 Windows 平台上强烈建议使用Visual Studio 2022。在安装 VS2022 时需要勾选以下工作负载使用 C 的桌面开发适用于游戏的 C 开发含 Windows SDK如果你只是运行 UE5 编辑器不需要额外安装独立的 Microsoft Visual C Redistributable因为 VS2022 编译出的 UE5 项目自带对应运行库。但如果你的游戏需要分发给玩家那么打包时通常需要附带对应版本的 Visual C 运行库。很多玩家反馈“游戏启动报缺少 VCRUNTIME140.dll”基本上就是没有安装或没有正确打包 Visual C Redistributable 导致的。另外如果你是那种习惯使用 VS Code 写 C 的开发者需要明确一点UE5 项目不建议直接用 VS Code 作为主力编辑器。UE5 的 C 项目依赖 UHTUnreal Header Tool进行反射代码生成VS Code 本身不提供 UBTUnreal Build Tool的自动集成。虽然社区有插件能让 VS Code 识别部分头文件但碰到代码跳转失败或变量无法索引时排查环境的时间成本远高于直接用 VS2022。这也是很多热词中提到“VSCode配置C/C环境”后仍然无法跳转 UE5 源码的原因。在 UE5 中编写 C 代码编译方式有两种直接点击编辑器的“Compile”按钮适合修改项目代码后快速编译测试。使用 Visual Studio 打开.sln解决方案文件编译适合调试和查看完整编译输出。日常开发推荐第一种因为 UE5 的增量编译已经足够快。2.3 启用GAS插件与项目结构GAS 在 UE5 中并不是默认启用的模块。虽然引擎已经自带了相关代码但你需要在项目的 Build.cs 文件中手动添加模块依赖。打开项目下的Source/项目名/项目名.Build.cs在PublicDependencyModuleNames中添加// 文件路径Source/项目名/项目名.Build.cs PublicDependencyModuleNames.AddRange(new string[] { Core, CoreUObject, Engine, InputCore, GameplayAbilities, GameplayTags, GameplayTasks });其中GameplayAbilitiesGAS 的核心模块包含 ASC、GA、GE 等主要类。GameplayTags标签系统模块GAS 依赖它实现技能的标记和过滤。GameplayTasks任务模块用于技能中复杂的异步逻辑。添加完成后关闭编辑器重新右键项目文件生成 Visual Studio 项目文件再启动编辑器编译一次。如果你发现编辑器左侧搜索不到AbilitySystemComponent等蓝图节点通常是模块没有成功加载或者编辑器没有完全重启。3. GAS核心概念完全拆解3.1 ASC、Attribute、GA、GE、Cue的关系GAS 中几个核心类之间的关系可以用一句话概括ASC 把属性和技能统筹起来技能GA负责触发逻辑技能通过效果GE修改属性Attribute效果产生时通过 Cue 表现视听反馈。具体展开来看1AbilitySystemComponentASCASC 是整个技能系统的大脑它挂在角色身上负责注册技能、分发技能触发事件、管理属性、执行效果。一个角色想要拥有技能和属性就必须挂载一个 ASC。通常建议将 ASC 挂载在 Pawn 或 PlayerState 上如果挂在 Pawn 上ASC 会随着角色死亡而销毁。如果挂在 PlayerState 上角色死亡重生后 ASC 仍然保留适合需要在重生后保留技能冷却状态的游戏。实际操作中玩家角色推荐挂在 Pawn 上但如果是需要跨生命周期保留的属性比如累计击杀数则可以考虑 PlayerState。2AttributeSetASAttributeSet 是属性的容器。你可以把它理解为一个专门管理血量、蓝量、攻击力的数据类。属性值本身使用FGameplayAttributeData类型并会自动通过 ASC 进行网络复制。3GameplayAbilityGAGA 是一个技能类。一个技能由多个函数组成最核心的几个包括CanActivateAbility检查技能是否满足激活条件。ActivateAbility技能激活后的主逻辑。EndAbility技能结束逻辑。每个技能实例在激活时可以设置是否允许同时和多个技能共存是否需要目标是否需要在服务器上执行等。4GameplayEffectGEGE 本身是一份“数据配置”它描述了一次属性修改。比如“造成30点伤害”是一个 GE“回复20点法力”也是一个 GE。GE 不执行代码逻辑它只是一组数据和规则真正的执行由 ASC 完成。GE 常见配置包括Duration Policy持续策略是立即生效、持续一段时间、还是无限持续。Modifier属性修改器可以修改伤害、治疗、护盾等。Execution自定义执行逻辑适合复杂的伤害公式。5GameplayCueCue 和 GE 不同它不修改任何数据只负责表现。比如技能命中后播放粒子特效、音效、屏幕震动。Cue 通常配合 GE 一起使用当 GE 生效时Cue 被触发。3.2 多人游戏中的网络同步模型GAS 在多人游戏中的网络设计非常强调“服务器权威”。简单来说服务器负责技能的实际执行和属性修改。客户端只负责向服务器发起请求以及同步表现。当你按下技能键时客户端调用 ASC 的TryActivateAbility这个请求会被发送到服务器。服务器执行技能逻辑后如果命中目标则由服务器生成 GE 修改目标属性。属性修改的结果再通过引擎的复制机制同步给所有相关客户端。这样做的好处是玩家不能通过修改本地内存来改动服务器端属性。比如你想把自己的血量改成无限大客户端本地再改也没有用因为服务器只认自己计算出的属性值。在实际开发中还需要区分技能是在客户端预测执行Prediction还是纯服务器执行。GAS 原生支持客户端的预测机制例如冲刺技能中角色的位移可以通过客户端预测减少延迟感。但是预测机制非常复杂建议新手在前期先不要碰预测先把服务器执行的技能逻辑跑通再逐步优化体验。3.3 标签GameplayTag与技能触发逻辑GameplayTag 是 GAS 中非常重要的辅助工具。你可以把它理解成一个结构化的字符串标记。例如Ability.Attack.JumpStatus.StunnedState.Dead标签常用于判断技能是否可以被激活。比如角色处于眩晕状态时拥有Status.Stunned标签此时攻击技能因为需要Status.Stunned不匹配而无法激活。管理技能之间的互斥状态。比如闪避技能激活期间攻击技能不能同时使用。标记 Cue 的触发类型。GAS 中常用的标签配置包括AbilityTags当前技能的标签。CancelAbilitiesWithTag激活本技能时哪些标签对应的技能会被取消。BlockAbilitiesWithTag本技能激活时阻止哪些标签对应的技能激活。ActivationOwnedTags激活本技能时自动添加到自己身上的标签。下面是一个典型的技能类中标签配置示例// 文件路径Source/项目名/Abilities/GA_Fireball.h UCLASS() class MYGAME_API UGA_Fireball : public UGameplayAbility { GENERATED_BODY() public: UGA_Fireball(); protected: virtual void ActivateAbility( const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; // 技能自身的标签 UPROPERTY(EditDefaultsOnly, Category Ability) FGameplayTagContainer AbilityTags; // 激活时需要占用自身的标签 UPROPERTY(EditDefaultsOnly, Category Ability) FGameplayTagContainer ActivationOwnedTags; };在实际项目中你需要在项目设置中提前创建这些标签。也可以选择在 C 中通过FGameplayTag::RequestGameplayTag动态请求不过编辑器中手动配置更直观。4. 实战实现一个多人技能系统4.1 需求与功能拆分这一节我们实现一个简单但完整的多人技能效果流程如下角色拥有 ASC 和 AttributeSet。角色拥有两个属性血量Health和法力Mana。角色拥有一个“治疗”技能消耗法力并回复血量。技能只能在服务器端执行避免客户端作弊。属性修改结果通过网络同步给所有客户端。功能拆分成三个部分属性部分创建 AttributeSet并注册到 ASC。技能部分创建 GA 治疗技能绑定按键。效果部分创建 GE 治疗数据。4.2 创建核心类文件先在项目中创建以下 C 类MyAttributeSet继承自UAttributeSet。MyAbilitySystemComponent继承自UAbilitySystemComponent。MyCharacter继承自ACharacter挂载 ASC 并初始化属性。GA_HealSelf继承自UGameplayAbility实现治疗逻辑。4.3 实现 AttributeSet// 文件路径Source/项目名/AbilitySystem/MyAttributeSet.h #pragma once #include CoreMinimal.h #include AttributeSet.h #include AbilitySystemComponent.h #include MyAttributeSet.generated.h UCLASS() class MYGAME_API UMyAttributeSet : public UAttributeSet { GENERATED_BODY() public: UMyAttributeSet(); // 血量 UPROPERTY(BlueprintReadOnly, Category Attributes) FGameplayAttributeData Health; // 最大血量 UPROPERTY(BlueprintReadOnly, Category Attributes) FGameplayAttributeData MaxHealth; // 法力 UPROPERTY(BlueprintReadOnly, Category Attributes) FGameplayAttributeData Mana; // 最大法力 UPROPERTY(BlueprintReadOnly, Category Attributes) FGameplayAttributeData MaxMana; // 提供属性访问器GAS 用它在修改属性时获取当前值 ATTRIBUTE_ACCESSORS(UMyAttributeSet, Health) ATTRIBUTE_ACCESSORS(UMyAttributeSet, MaxHealth) ATTRIBUTE_ACCESSORS(UMyAttributeSet, Mana) ATTRIBUTE_ACCESSORS(UMyAttributeSet, MaxMana) };在 AttributeSet 中每个属性都通过FGameplayAttributeData类型保存并使用ATTRIBUTE_ACCESSORS宏生成静态访问函数。这个宏是 GAS 的标准写法后续 GE 在修改这些属性时会通过访问器找到对应属性。4.4 实现 GameplayAbility 治疗技能// 文件路径Source/项目名/Abilities/GA_HealSelf.h #pragma once #include CoreMinimal.h #include Abilities/GameplayAbility.h #include GA_HealSelf.generated.h UCLASS() class MYGAME_API UGA_HealSelf : public UGameplayAbility { GENERATED_BODY() public: UGA_HealSelf(); protected: // 治疗回复量 UPROPERTY(EditDefaultsOnly, Category Heal) float HealAmount 30.0f; // 法力消耗量 UPROPERTY(EditDefaultsOnly, Category Heal) float ManaCost 10.0f; virtual void ActivateAbility( const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; };接着实现 ActivateAbility 函数// 文件路径Source/项目名/Abilities/GA_HealSelf.cpp #include GA_HealSelf.h #include AbilitySystemComponent.h #include AbilitySystem/MyAttributeSet.h UGA_HealSelf::UGA_HealSelf() { // 默认只在服务器上执行避免客户端直接修改属性 NetExecutionPolicy EGameplayAbilityNetExecutionPolicy::ServerOnly; } void UGA_HealSelf::ActivateAbility( const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) { if (!ActorInfo || !ActorInfo-AbilitySystemComponent.IsValid()) { EndAbility(Handle, ActorInfo, ActivationInfo, true, false); return; } UAbilitySystemComponent* ASC ActorInfo-AbilitySystemComponent.Get(); if (!ASC) { EndAbility(Handle, ActorInfo, ActivationInfo, true, false); return; } // 获取属性集 UMyAttributeSet* AttrSet const_castUMyAttributeSet*( ASC-GetSetUMyAttributeSet()); if (!AttrSet) { EndAbility(Handle, ActorInfo, ActivationInfo, true, false); return; } // 判断法力是否足够 float CurrentMana AttrSet-GetMana(); if (CurrentMana ManaCost) { EndAbility(Handle, ActorInfo, ActivationInfo, true, false); return; } // 修改属性扣法力和加血 ASC-ApplyModToAttribute(AttrSet-GetManaAttribute(), EGameplayModOp::Additive, -ManaCost); ASC-ApplyModToAttribute(AttrSet-GetHealthAttribute(), EGameplayModOp::Additive, HealAmount); // 技能结束 EndAbility(Handle, ActorInfo, ActivationInfo, true, false); }这段代码的核心逻辑非常清晰检查 ASC 是否存在。通过GetSetUMyAttributeSet()获取角色的属性集。判断当前法力是否足够。通过ApplyModToAttribute进行属性修改。调用EndAbility结束技能。其中NetExecutionPolicy ServerOnly非常关键。它保证了技能的属性修改只在服务器端执行客户端只是向服务器发起激活请求。这是多人游戏中最基础、最安全的技能执行方式。4.5 创建 GameplayEffect 配置前面的例子里使用了ApplyModToAttribute直接修改属性这种方式适合快速原型。但在正规项目中更推荐使用 GameplayEffect。创建一个UGameplayEffect蓝图类在内容浏览器中右键 - Gameplay Effect命名为GE_HealSelf。打开后找到 Modifiers 数组添加两个 Modifier第一个Attribute 选择MyAttributeSet.ManaModifier Op 选择 AddMagnitude 设为-10。第二个Attribute 选择MyAttributeSet.HealthModifier Op 选择 AddMagnitude 设为30。设置 Duration Policy 为 Instant。然后在 C 或蓝图中通过 ASC 的MakeOutgoingSpec和ApplyGameplayEffectSpecToSelf来执行这个 GE。使用 GE 的好处是你可以把数值配置完全放在数据资产中策划同学可以独立调整程序员不需要每次改代码。4.6 给角色挂载 ASC 和 AttributeSet接下来把 ASC 和 AttributeSet 挂到角色类上。// 文件路径Source/项目名/Characters/MyCharacter.h #pragma once #include CoreMinimal.h #include GameFramework/Character.h #include AbilitySystemInterface.h #include MyCharacter.generated.h class UAbilitySystemComponent; class UMyAttributeSet; UCLASS() class MYGAME_API AMyCharacter : public ACharacter, public IAbilitySystemInterface { GENERATED_BODY() public: AMyCharacter(); // 实现 IAbilitySystemInterface virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override; protected: UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category GAS) TObjectPtrUAbilitySystemComponent AbilitySystemComponent; UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category GAS) TObjectPtrUMyAttributeSet AttributeSet; void SetupInitialAttributes(); virtual void BeginPlay() override; };实现文件如下// 文件路径Source/项目名/Characters/MyCharacter.cpp #include MyCharacter.h #include AbilitySystemComponent.h #include AbilitySystem/MyAttributeSet.h AMyCharacter::AMyCharacter() { // 创建 ASC 和 AttributeSet AbilitySystemComponent CreateDefaultSubobjectUAbilitySystemComponent(TEXT(AbilitySystemComponent)); AttributeSet CreateDefaultSubobjectUMyAttributeSet(TEXT(AttributeSet)); } UAbilitySystemComponent* AMyCharacter::GetAbilitySystemComponent() const { return AbilitySystemComponent; } void AMyCharacter::BeginPlay() { Super::BeginPlay(); if (AbilitySystemComponent) { // 初始化属性 SetupInitialAttributes(); } } void AMyCharacter::SetupInitialAttributes() { if (!AbilitySystemComponent || !AttributeSet) { return; } // 初始化属性值 AbilitySystemComponent-SetNumericAttributeBase(AttributeSet-GetMaxHealthAttribute(), 100.0f); AbilitySystemComponent-SetNumericAttributeBase(AttributeSet-GetHealthAttribute(), 100.0f); AbilitySystemComponent-SetNumericAttributeBase(AttributeSet-GetMaxManaAttribute(), 50.0f); AbilitySystemComponent-SetNumericAttributeBase(AttributeSet-GetManaAttribute(), 50.0f); }此时如果编译运行在游戏开始时角色就有 100 血和 50 蓝。4.7 技能触发绑定最后需要把治疗技能绑定到一个按键上。可以在角色的 SetupPlayerInputComponent 中处理// 文件路径Source/项目名/Characters/MyCharacter.cpp #include InputActionValue.h #include Abilities/GameplayAbility.h void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent); // 绑定按键到自定义函数 PlayerInputComponent-BindAction(HealSelf, IE_Pressed, this, AMyCharacter::OnHealSelfPressed); } void AMyCharacter::OnHealSelfPressed() { if (!AbilitySystemComponent) { return; } // 需要提前在项目设置中创建 GameplayTag: Ability.Heal FGameplayAbilitySpecHandle HealAbilityHandle FindAbilityByTag( FGameplayTag::RequestGameplayTag(FName(Ability.Heal))); if (HealAbilityHandle.IsValid()) { AbilitySystemComponent-TryActivateAbility(HealAbilityHandle, true); } }这里涉及到一个关键步骤技能需要提前注册到 ASC 中。通常在角色 BeginPlay 或玩家控制器创建时将 GA 类加入 ASCAbilitySystemComponent-GiveAbility( FGameplayAbilitySpec(UGA_HealSelf::StaticClass(), 1, INDEX_NONE, this));4.8 运行验证与预期结果编译运行后在第三人称模板中打开 UI 或日志先确认角色初始血量为 100法力为 50。按下绑定按键触发技能。观察角色属性变化法力从 50 变为 40血量从 100 变为 130如果未受伤则血量会超额实际项目中往往需要限制最大值这一点涉及 GE 的 Clamp 配置这里不再展开。如果在编辑器里打开“网络模拟”模式把玩家数量设置为 2你还可以通过服务器日志和客户端日志分别观察属性变化的输出顺序。5. 常见问题与排查思路GAS 的学习曲线比较陡很多同学在配置过程中会遇到各种问题。下面是几个高频问题及排查思路。问题现象常见原因解决思路编译报错无法打开 GameplayAbilities 头文件Build.cs 未添加模块依赖在 Build.cs 中补充 GameplayAbilities、GameplayTags、GameplayTasks编辑器找不到 AbilitySystemComponent 节点插件模块加载失败完全关闭编辑器重新生成 VS 项目文件再启动技能不会被触发未给 ASC 注册技能或标签不匹配检查GiveAbility是否调用检查TryActivateAbility传入的标签是否正确技能触发后属性没有变化NetExecutionPolicy 设置为 ServerOnly但当前测试环境没有服务器在编辑器中使用“运行专用服务器”或临时改为 LocalPredicted 测试属性在不同客户端显示不一致AttributeSet 没有注册到 ASC或不支持网络复制确认 AttributeSet 在 ASC 中注册检查使用FGameplayAttributeData而非普通 float角色死亡后属性丢失ASC 挂在 Pawn 上Pawn 被销毁将 ASC 迁移到 PlayerState并在 Pawn 的PossessedBy中初始化GE 配置了但完全没有效果GE 的 Duration Policy 或 Modifier 配置错误检查 GE 是否设置为 Instant检查 Modifier 的 Attribute 是否选择了正确的 AttributeSet排查 GAS 问题的时候有一个非常高效的工具Gameplay Debugger。在游戏中按Shift ~打开控制台输入AbilitySystem.Debug.AbilityActor 角色名就可以看到当前角色的所有技能、属性、效果和标签状态。这是定位技能不生效的第一手段。6. 最佳实践与工程化建议6.1 多人游戏中的网络设计与权限校验GAS 的权限模型是“服务器权威”但这不代表开发者可以掉以轻心。以下几条是多年多人游戏开发的通用经验所有改变核心数值的逻辑必须在服务器端执行。客户端的表现可以预测但最终数值必须由服务器确认。不要在技能逻辑中直接调用客户端 UI 更新。UI 更新应通过属性变化回调或事件通知机制否则客户端状态和服务器状态容易不同步。每个网络技能都明确 NetExecutionPolicy。一个技能到底应该ServerOnly、LocalPredicted还是ClientOnly在设计阶段就要决定不要临时切换。不要信任客户端的伤害数值。客户端上报伤害不如由服务器根据 GE 重新计算这样外挂修改本地数值后也无法起作用。6.2 GAS代码组织与命名规范在工程项目中GAS 相关代码量通常会快速膨胀建议按以下结构组织目录Source/项目名/ ├── AbilitySystem/ │ ├── Attributes/ │ │ └── MyAttributeSet.h/cpp │ ├── Abilities/ │ │ ├── GA_HealSelf.h/cpp │ │ └── GA_Fireball.h/cpp │ ├── Effects/ │ │ └── GE_HealSelf.h/cpp │ └── Tasks/ │ └── ... ├── Characters/ │ ├── MyCharacter.h/cpp │ └── MyPlayerController.h/cpp └── UI/ └── ...命名上推荐技能类以前缀GA_开头。效果类以前缀GE_开头。属性集类以AttributeSet结尾或直接叫XxxAttributeSet。技能任务以前缀UAT_开头。这样命名有两个好处内容浏览器中所有技能、效果按前缀集中分类。蓝图类和数据资产通过搜索前缀能快速定位。6.3 性能与稳定性的考虑GAS 虽然强大但如果不加约束性能开销也会很明显。尽量少实例化技能。一些简单的技能比如普通攻击可以被多个 Actor 复用而不需要每个 Actor 都创建一个完整技能实例。使用UGameplayAbility的静态默认对象可以降低开销但要注意不要在该技能中保存 Actor 状态。属性复制不要过于频繁。Actor 上如果有大量FGameplayAttributeData默认每一帧都会尝试复制。可以通过调整 ASC 的MinReplicationDelay或指定属性的复制条件来控制网络流量。慎用预测。GAS 的预测机制非常强大但一旦实现错误会出现“客户端以为自己释放了技能服务器却不认可”的问题。新手选择技能时建议先从ServerOnly做起。之后再根据体验考虑是否使用预测。日志规范。在 GAS 逻辑中适度加入ABILITY_LOG或UE_LOG并标记LogAbilitySystemComponent的 Verbosity。上线前可以提升警告级别方便线上问题排查。另外有一点很重要GAS 文档最好跟着项目沉淀。由于 GAS 的学习门槛较高团队内建议维护一份自己的 GAS 约定文档至少包含每个新技能的创建流程。属性集的扩展方式。哪些情况不允许使用预测。数值修改必须通过 GE 而非直接修改属性。这些约定能有效避免后期重构带来的混乱。7. 总结与后续学习路线通过这篇文章你已经掌握了 UE5 多人游戏开发中最核心的一套框架GAS。具体包括GAS 五大核心组件ASC、AS、GA、GE、Cue各自的职责。多人游戏中服务器权威和客户端预测的基本思路。如何在 C 工程中配置 GAS 模块。如何创建一个完整的技能从属性集、技能类到按键触发。常见 GAS 报错和排查工具的使用。多人游戏开发和 GAS 代码组织的工程化经验。下一步的学习路线建议按顺序尝试以下方向先不要急着做复杂技能。先把一个完整简单的“治疗”流程彻底跑通初始化属性、触发技能、修改属性、网络同步。尝试用 GameplayEffect 替代直接属性修改。理解 GE 的无限持续、Durational、Instant 三种持续策略的区别。研究 GameplayTag 的进阶用法。学会使用标签管理眩晕、无敌、沉默等状态。实现一个带冷却时间的技能。通过 GE 的 Cooldown 配置或 Tag 方式实现冷却逻辑。尝试技能升级与多段连击。这时候你会真正体会到 GAS 和传统“每个技能写一套代码”的思路差异。最后再碰预测。例如冲刺技能的客户端预测接着是技能伤害判定的预测。如果你刚开始接触 GAS建议多翻一翻 UE5 自带的 Lyra 示例项目。Lyra 是目前官方对 GAS 最佳实践最完整的展示里面包含技能、属性、装备、武器和多人联机的大量落地代码。虽然 Lyra 的重心是射击游戏但里面 GAS 的用法完全可以迁移到动作游戏、角色扮演游戏甚至 MOBA 类项目。如果这篇笔记对你有帮助欢迎收藏备用。GAS 是一门需要反复实践和踩坑的技术后续我会继续更新更多 GAS 实战细节包括技能冷却、目标选择、伤害计算流程、预测、Cue 管理和 GAS 与 GameplayMessage 的联动使用。也欢迎在评论区留下你在 GAS 开发中遇到的报错或疑惑一起讨论。
返回列表