之前也介绍过GAS的使用:
UE 5 GAS Gameplay Ability System
UE 5 GAS 在项目中处理AttributeSet相关
UE 5 GAS 在项目中通过数据初始化
基础的讲解这里不再诉说,有兴趣的可以翻我之前的博客。
接下来,在RPG游戏中实现GAS系统的使用。
GAS系统可以放到角色Pawn身上,也可以放到PlayerState里面,如果放到Pawn身上,GAS也会跟着销毁,这个比较方便与敌人身上,所以敌人的Ability System Component(ASC)和Attribute Set我们将直接放置到敌人的Pawn身上,而玩家控制的角色,牵扯到一个复活的问题,我们需要保存技能相关的数据,将放置到PlayerState身上,那样如果销毁掉角色Pawn,相关数据也会保存下来。
创建PlayerState
首先点击创建c++类
创建一个新的玩家状态类
// 版权归暮志未晚所有。
#include "Player/PlayerStateBase.h"
APlayerStateBase::APlayerStateBase()
{
NetUpdateFrequency = 100.f; //每秒和服务器更新频率,使用GAS后可以设置的高一些
}
创建完成后再PlayerState.cpp中初始化时,修改NetUpdateFrequency 默认为2,使用了GAS可以设置的更高
打开ue,创建蓝图类
放置到蓝图文件夹内
将GameMode里面的玩家状态类修改成当前创建的蓝图。
GAS的载体就制作完成,接下来添加GAS系统。
添加GAS
打开插件,搜素gameplay,找到gameplay Abilities,添加,重新启动。
添加新的c++类,父类为AbilitySystemComponent
创建ASC基类
接下来创建数据集类,父类为AttributeSet
PrivateDependencyModuleNames.AddRange(new string[] { "GameplayAbilities", "GameplayTags", "GameplayTasks" });
在私有模块中添加相关模块
代码中添加
带有ASC的Actor也被称为ASC的OwnerActor。ASC实际作用的Actor叫作AvatarActor。OwnerActor和AvatarActor可以是同一个Actor,比如MOBA游戏中的野怪。它们也可以是不同的 Actors,比如MOBA游戏中玩家和AI控制的英雄角色,OwnerActor是PlayerState、AvatarActor是HeroCharacter。大部分情况下OwnerActor和AvatarActor可以是角色Actor。不过想像一下你控制的英雄角色死亡然后重生的过程,如果此时要保留死亡前的Attributes或者GameplayEffects,那么最理想的做法是将ASC交给PlayerState。
如果OwnerActor和AvatarActor是不同的Actors,那么两者都需要实现IAbilitySystemInterface。这个接口只有一个方法需要被重载UAbilitySystemComponent* GetAbilitySystemComponent() const,此方法将返回ASC。
首先在角色基础的类里面创建两个属性,分别用来定义技能组件和属性,记得声明它们的类。主角类和敌人基础的类都会继承角色基础类,那么,它们都会创建相关的属性。
AEnemyBase::AEnemyBase()
{
GetMesh()->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block); //设置可视为阻挡
AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponentBase>("AbilitySystemComponent");
AbilitySystemComponent->SetIsReplicated(true); //设置组件用于在网络上复制
AttributeSet = CreateDefaultSubobject<UAttributeSetBase>("AttributeSet");
}
敌人的基类里面,只需要在初始化时实例化相应的组件
主角的组件和数据,我们首先需要先在PlayerState身上设置,然后再characterBase身上去引用PlayerState身上的对应组件和数据,这样,即使character销毁掉,创建新的,我们一样可以获取到修改后的数据。
// 版权归暮志未晚所有。
#include "Player/PlayerStateBase.h"
#include "AbilitySystem/AbilitySystemComponentBase.h"
#include "AbilitySystem/AttributeSetBase.h"
APlayerStateBase::APlayerStateBase()
{
NetUpdateFrequency = 100.f; //每秒和服务器更新频率,使用GAS后可以设置的高一些
AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponentBase>("AbilitySystemComponent");
AbilitySystemComponent->SetIsReplicated(true); //设置组件用于在网络上复制
AttributeSet = CreateDefaultSubobject<UAttributeSetBase>("AttributeSet");
}
在PlayerStateBase.cpp里面实例化。
接下来比较重要的一步就是,如何在Character里面去获取PlayerState的技能组件,这里我们使用了GAS组件自带AbilitySystemInterface接口去实现。
需要在.h文件中使用技能组件,所以,我们将模块移入到Public列表中。
// 版权归暮志未晚所有。
#pragma once
#include "CoreMinimal.h"
#include "AbilitySystemInterface.h"
#include "GameFramework/PlayerState.h"
#include "PlayerStateBase.generated.h"
class UAbilitySystemComponent;
class UAttributeSet;
/**
*
*/
UCLASS()
class AURA_API APlayerStateBase : public APlayerState, public IAbilitySystemInterface
{
GENERATED_BODY()
public:
APlayerStateBase();
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override; //覆盖虚函数获取asc
UAttributeSet* GetAttributeSet() const { return AttributeSet; } //获取as
protected:
UPROPERTY()
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
UPROPERTY()
TObjectPtr<UAttributeSet> AttributeSet;
};
在playerState里,继承接口,实现接口的虚函数,用于获取asc,并实现获取as的函数。
UAbilitySystemComponent* APlayerStateBase::GetAbilitySystemComponent() const
{
return AbilitySystemComponent;
}
cpp里定义也只是返回asc组件。
character同样继承此接口,并实现对应的函数。
接下来我们要设置复制模式,以便来同步客户端和服务器端数据。
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
在角色的asc上面,我们使用Mixed模式同步。
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Minimal);
而怪物的asc,则使用Minimal模式
初始化AbilitySysytemComponent
ASC需要有OwnerActor和AvatarActor进行初始化,而且必须在服务器和客户端都要完成初始化。
对于玩家控制的角色,ASC存在于Pawn中,我通常在Pawn的 PossessedBy()方法中完成ASC在服务器端的初始化,在PlayerController的AcknowledgePawn()方法中完成ASC在客户端的初始化。
对于玩家控制的角色,ASC存在于PlayerState中,我通常在Pawn 的PossessedBy() 方法中完成ASC在服务器端的初始化(这一点与上述相同),在 Pawn的 OnRep_PlayerState()方法中完成ASC在客户端的初始化(这将确保PlayerState在客户端已存在)。
对于AI控制的角色,ASC存在于Pawn中,通常在Pawn的 BeginPlay()方法中完成ASC在服务器端和客户端的初始化。
既然根据文档知道了如何初始化,那么,我们按照相应的方式初始化。首先初始化敌人身上的ASC,敌人作为ai怪物,只需要在BeginPlay里面初始化即可。
首先覆盖BeginPlay()的函数
void AEnemyBase::BeginPlay()
{
Super::BeginPlay();
AbilitySystemComponent->InitAbilityActorInfo(this, this);
}
然后在BeginPlay()里面调用InitAbilityActorInfo初始化即可完成。
接下来是玩家控制角色的ASC初始化,玩家控制角色按照文档说法,我们需要在AvatarActor里面进行初始化。
如果ASC存在于PlayerState中,则覆盖这两个函数,然后添加私有初始化函数(因为里面初始化代码一致)
void AHeroCharacter::InitAbilityActorInfo()
{
APlayerStateBase* PlayerStateBase = GetPlayerState<APlayerStateBase>();
check(PlayerStateBase);
//从playerState获取ASC和AS
AbilitySystemComponent = PlayerStateBase->GetAbilitySystemComponent();
AttributeSet = PlayerStateBase->GetAttributeSet();
//初始化ASC
AbilitySystemComponent->InitAbilityActorInfo(PlayerStateBase, this);
}
初始化代码就是获取到玩家控制角色的PlayerState,从PlayerState身上获取ASC和AS,OwnerActor就是PlayerState,而AvatarActor则就是自身。
void AHeroCharacter::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
//初始化ASC的OwnerActor和AvatarActor
InitAbilityActorInfo();
}
void AHeroCharacter::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
//初始化ASC的OwnerActor和AvatarActor
InitAbilityActorInfo();
}
接下来就是在PossessedBy()和OnRep_PlayerState()调用,完成服务端和客户端的初始化。
//初始化ASC的OwnerActor和AvatarActor
InitAbilityActorInfo();
//设置OwnerActor的Controller
SetOwner(NewController);
注意: Mixed 复制模式要求OwnerActor的 Owner必须是Controller。 PlayerState的 Owner默认是Controller,但是Character不是。如果使用Mixed复制模式的OwnerActor不是PlayerState那么你需要在OwnerActor上调用SetOwner()并传递一个有效的Controller。(不过从4.24开始, PossessedBy() 会为Pawn设置一个新的Controller。)
参考:虚幻引擎游戏技能系统文档