一.创建ASC
1.ASC创建位置
对于单机游戏ASC创建在玩家状态(Player State)还是玩家角色(Player Character)里都是一样的,但对于网络联机游戏,ASC最好放在玩家状态(Player State)中。
ASC绑在PlayerCharacter,会导致AttributeSet/GE/Tag等都会随着PlayerCharacter的死亡而销毁。ASC 绑在PlayerState则不会由于PlayerCharacter的死亡销毁而销毁。新Character出生后,通过 InitAbilityActorInfo找回 ASC,并调用InitAbilityActorInfo重新绑定,即可恢复全部原有数据。
2.创建ASC设置复制模式
#pragma once #include "CoreMinimal.h" #include "GameFramework/PlayerState.h" #include "AbilitySystemComponent.h" #include "DemoPlayerStateBase.generated.h" UCLASS() class DEMO_API ADemoPlayerStateBase : public APlayerState { GENERATED_BODY() public: ADemoPlayerStateBase(); protected: //创建ASC UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASC") UAbilitySystemComponent* ASC; };#include "Cores/DemoPlayerStateBase.h" ADemoPlayerStateBase::ADemoPlayerStateBase() { ASC = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("ASC")); if(ASC) { ASC->SetIsReplicated(true);//设置为可复制 ASC->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);//设置模式为混合模式 } }| 复制模式 | 适用场景 | 特点 |
|---|---|---|
| Full | 单人游戏/小型多人 | 全部客户端同步完整 GE 实例,Buff 时长 / 层数 / 数值全同步,网络开销大 |
| Mixed | 多人玩家角色 | 自身本地客户端同步完整 GE;其他玩家仅同步 GameplayTag、GameplayCue 视觉表现,无完整 GE 数值 |
| Minimal | NPC/观战单位 | 所有客户端均不传输完整 GE,仅同步 Tag 与 GC 特效,带宽占用最低 |
二.实现ASC组件接口
1.接口介绍
在面向对象编程中,接口是一种特殊的抽象类型,它定义了一组相关功能的行为协定,但不包含任何实现。接口具备以下特征:
①纯粹的抽象:接口只声明方法签名(函数名、参数、返回值),不提供任何方法体或成员变量。
②协议性:它规定了实现类“必须提供哪些功能”,而不是“如何实现这些功能”。
③解耦性:接口将“做什么”与“怎么做”完全分离,调用方只需知道接口协议,无需了解实现类的具体类型。
在Unreal Engine的C++中,接口是通过UINTERFACE宏声明的一个特殊的UObject子类。它本身不能被实例化,其主要作用是:
① 为不同的AActor或UObject子类建立统一的通信契约。
② 允许不相关的类共享相同的行为能力(如IAbilitySystemInterface统一提供了获取UAbilitySystemComponent的访问点)。
UE官方对接口也做出了相应介绍,具体可看官方文档
Gameplay技能系统 - 配置的最佳实践 | Epic Developer Community
2.实现 IAbilitySystemInterface 接口
实现 IAbilitySystemInterface 接口,就需要重写接口里的GetAbilitySystemComponent函数,并且提供获取ASC的功能
class DEMO_API ADemoPlayerStateBase : public APlayerState, public IAbilitySystemInterface { GENERATED_BODY() public: ADemoPlayerStateBase(); //IAbilitySystemInterface接口 virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;//实现IAbilitySystemInterface接口 UAbilitySystemComponent* ADemoPlayerStateBase::GetAbilitySystemComponent() const { return ASC; }三.ASC绑定Owner和Avatar
绑定Owner,Mixed复制模式才能正常生效,ASC 知道数据该精准发给哪个客户端。
绑定Avatar,技能才能正确获取物理位置、播放动画,GameplayCue找到作用目标。
Owner通常指向PlayerState,Avatar指向PlayerCharacter。
1.UE核心类的创建
①游戏实例(Game Instance)
最早创建,生命周期最长,通常跨关卡存在。
- 负责:存档、网络会话、全局 UI、玩家登录数据等
- 特点:不随关卡切换销毁(除非退出游戏)
- 类比:整个游戏的“总管理器”
②游戏模式(Game Mode)
关卡加载后,由 Game Instance 驱动创建。
- 负责:规则、胜负、刷怪、玩家重生、选 Pawn 类型等
- 特点:只在服务端存在(单机时本地就是服务端)
- 类比:这一局的“裁判 + 关卡导演”
③ 游戏状态(Game State)
Game Mode 创建后,用来表示整局游戏的全局状态。
- 负责:比分、剩余时间、队伍信息等
- 特点:会复制到所有客户端,大家看到同一份“局内数据”
- 类比:记分牌、比赛信息板
④玩家控制器(Player Controller)
玩家加入后,由 Game Mode 创建。
- 负责:输入、相机、UI、Possess Pawn
- 特点:先创建,是玩家在本地的“操作中枢”
- 类比:手柄/键鼠背后的“操作员”
⑤玩家状态(Player State)
在 Player Controller 初始化阶段创建,并绑定到 Controller。
- 负责:玩家名、分数、队伍、存活状态等跟玩家绑定、跨 Pawn 存活的数据
- 特点:后于 Controller 创建,但通常比 Pawn 活得更久
- 类比:玩家档案/战绩卡
⑥玩家角色(Player Character)
最后才生成并交给 Controller 控制。
- 负责:移动、动画、碰撞、具体玩法表现
- 特点:可被销毁、重生、替换,Controller 和 State 一般还在
- 类比:场上实际操作的“角色实体”
2.InitAbilityActorInfo函数
InitAbilityActorInfo 是AbilitySystemComponent最核心的初始化函数。它的作用就一个:正式建立 ASC 与它的“逻辑主人”和“物理身体”之间的绑定关系。
调用InitAbilityActorInfo函数:
建立引用:ASC 内部保存了
OwnerActor和AvatarActor的指针。之后技能系统通过 ASC 就能随时知道“谁拥有这个 ASC”和“这个 ASC 代表哪个身体”。激活网络复制:ASC 知道了 Owner,
Mixed复制模式才能正式生效——知道数据该发给哪个客户端。技能可以激活:GameplayAbility 需要从 Avatar 获取位置、朝向,从 Owner 获取权限。调用这个函数后,这些信息才完整。
GameplayEffect 可以正确应用:GE 应用时需要知道“修改谁的属性”(通过 Owner 找到属性集)、“效果作用在谁身上”(通过 Avatar 获取物理目标)。
触发
OnAvatarSet事件:你可以在蓝图中监听这个事件,用来在角色出生时给予初始技能。
3.绑定位置
在PlayerState创建后,PlayerCharacter还未创建,此时无法获取PlayerCharacter相关数据,即无法绑定Avatar。所以选择在PlayerCharacter中进行绑定。
同理,BeginPlay执行时,客户端上的PlayerState可能还未同步,服务器端 Controller 与 Pawn 的关联也可能尚未完全建立,导致 Owner 或 Avatar 为空。PossessedBy和OnRep_PlayerState是三者(Controller、PlayerState、Character)都就绪的最早安全时机。
在 Unreal Engine 的 GAS 框架中,调用InitAbilityActorInfo来绑定 Owner 和 Avatar 的标准位置有两个,分别针对服务器和客户端。
PossessedBy(服务器端)
执行位置:服务器
触发时机:当
PlayerController接管Character的控制权时(通常由RestartPlayer触发)。
OnRep_PlayerState(客户端)
执行位置:客户端
触发时机:当客户端的
Character收到服务器同步过来的PlayerState时。
public: //Possed By,服务端绑定 virtual void PossessedBy(AController* NewController) override; //OnRep PlayerState,客户端绑定 virtual void OnRep_PlayerState() override;// Possed By void ADemoCharacterBase::PossessedBy(AController* NewController) { Super::PossessedBy(NewController); InitAbilityActorInfo(); } //OnRep PlayerState void ADemoCharacterBase::OnRep_PlayerState() { Super::OnRep_PlayerState(); InitAbilityActorInfo(); }//InitAbilityActorInfo void ADemoCharacterBase::InitAbilityActorInfo() { if(ADemoPlayerStateBase * DemoState = GetPlayerState<ADemoPlayerStateBase>()) DemoState->GetAbilitySystemComponent()->InitAbilityActorInfo(DemoState, this); }