虚幻c++ gas
(如生命值、魔法值、力量、敏捷)。
视频学习:2-12_哔哩哔哩_bilibili
整个流程:
一、什么是Gameplay Ability System (GAS)
1. Ability System Component (ASC) - “大脑与中枢”
-
是什么? 一个
UActorComponent
( actor组件),必须添加到任何需要使用GAS的Actor上(如角色、怪物)。 -
作用:它是GAS的总指挥和管理器。所有其他GAS组件都通过ASC来交互。
-
职责:
-
存储和管理属性集(AttributeSet)。
-
授予(Grant)、激活(Activate)、结束(End)和撤销(Revoke)游戏能力(Gameplay Ability)。
-
应用和管理游戏效果(Gameplay Effect)。
-
处理游戏标签(Gameplay Tag) 的添加和移除。
-
处理网络复制和预测。
-
没有ASC,Actor就无法使用GAS的任何功能。
2. Gameplay Ability - “可执行的动作”
-
是什么? 继承自
UGameplayAbility
的类(C++或蓝图)。 -
作用:定义了一个完整的、可激活的行为。
-
例子:火球术、跳跃、治疗、开启护盾、被动闪避、使用药水。
-
职责:
-
包含技能从开始到结束的完整逻辑(
ActivateAbility
->Execute
->EndAbility
)。 -
通常会去应用Gameplay Effect(如下文)来实现技能的效果。
-
可以播放动画、生成投射物、消耗资源等。
-
3. Gameplay Effect (GE) - “持续性的状态改变”
-
是什么? 继承自
UGameplayEffect
的数据资产(Data Asset),主要在蓝图中配置。 -
作用:描述对属性和状态的修改。它本身不包含逻辑,只包含配置数据。
-
两种主要类型:
-
即时(Instantaneous):立即应用一次效果。例如:造成伤害、治疗、消耗魔法值。
-
配置:
Duration Policy
=Instant
-
-
持续(Duration / Infinite):在一段时间内或无限期地应用效果。例如:持续灼烧伤害、攻击力增益Buff、沉默Debuff。
-
配置:
Duration Policy
=Has Duration
或Infinite
-
-
-
职责:
-
定义修改哪些属性(如
Health
,Mana
,AttackPower
),如何修改(加、减、乘、除)。 -
携带和授予游戏标签(Gameplay Tags),例如应用一个GE来为目标添加
Status.Stun
(眩晕)标签。 -
定义周期效果(Periodic Effects),如每秒造成一次伤害。
-
4. Attribute Set - “数值仓库”
-
是什么? 一个
UObject
类,通常被ASC所持有。 -
作用:定义和存储所有游戏属性的当前值(如生命值、魔法值、力量、敏捷)。
-
职责:
-
声明属性(如
Health
,MaxHealth
,Mana
,AttackPower
)。 -
响应属性值的变化(通过
PreAttributeChange
和PostGameplayEffectExecute
函数),可以在这里进行 clamping(限制范围,如确保生命值不超过最大值)或触发其他事件。
-
5. Gameplay Tags - “描述状态的标签系统”
-
是什么? 一个类似于
FName
但组织成树形结构的标签系统(如Ability.Spell.Fireball
,Status.Stun
,State.Dead
)。 -
作用:提供一种高效、解耦的方式来标识和查询Actor或能力的状态。
-
职责:
-
阻塞(Blocking):一个能力可以要求自身不拥有
State.Dead
标签才能被激活。 -
取消(Canceling):一个带有
Status.Stun
标签的GE可以取消所有带有Ability.Type.Movement
标签的能力。 -
描述(Describing):GE可以添加或移除标签来描述状态(如添加
State.Invisible
)。
-
启用插件
创建WarriorAbilitySystemComponent、WarriorAttributeSet(需要添加模块GameplayTasks)
在WarriorBaseCharacter中加和初始化两个属性 WarriorAbilitySystemComponent、WarriorAttributeSet:
//WarriorBaseCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "AbilitySystemInterface.h"
#include "WarriorBaseCharacter.generated.h"
class UWarriorAbilitySystemComponent;
class UWarriorAttributeSet;
UCLASS()
class WARRIOR_API AWarriorBaseCharacter : public ACharacter, public IAbilitySystemInterface
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AWarriorBaseCharacter();
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AbilitySystem")
UWarriorAbilitySystemComponent* WarriorAbilitySystemComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AbilitySystem")
UWarriorAttributeSet* WarriorAttributeSet;
public:
FORCEINLINE UWarriorAbilitySystemComponent* GetWarriorAbilitySystemComponent() const { return WarriorAbilitySystemComponent; }
FORCEINLINE UWarriorAttributeSet* GetWarriorAttributeSet() const { return WarriorAttributeSet; }
};
//WarriarBaseCharacter.cpp中初始化两个属性
WarriorAbilitySystemComponent = CreateDefaultSubobject<UWarriorAbilitySystemComponent>(TEXT("WarriorAbilitySystemComponent"));
WarriorAttributeSet = CreateDefaultSubobject<UWarriorAttributeSet>(TEXT("WarriorAttributeSet"));
重写PossessedBy方法
//~ Begin APawn Interface.
virtual void PossessedBy(AController* NewController) override;
//~ End APawn Interface
void AWarriorBaseCharacter::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
if (WarriorAbilitySystemComponent)
{
WarriorAbilitySystemComponent->InitAbilityActorInfo(this, this);
ensureMsgf(!CharacterStartUpData.IsNull(), TEXT("Forgot to assign start up data to %s"), *GetName());
}
}
ProcessBy方法的核心作用是在服务器上,当一个新的控制器(Controller)开始掌控(Possess)这个角色时,进行关键的初始化工作,上面就是为GAS设置“所有者(Owner)”和“化身(Avatar)”信息。
class WARRIOR_API AWarriorBaseCharacter : public ACharacter, public IAbilitySystemInterface
AWarriorBaseCharacter实现IAbilitySystemInterface接口
class WARRIOR_API AWarriorBaseCharacter : public ACharacter, public IAbilitySystemInterface
重写纯虚函数GetAbilitySystemComponent
UAbilitySystemComponent* AWarriorBaseCharacter::GetAbilitySystemComponent() const
{
return GetWarriorAbilitySystemComponent();
}
在WarriorHeroCharacter也要实现PossessedBy方法
二、GameplayAbility
能力是赋给AbilitySystemComponent
方面 | OnGiven | OnTriggered |
---|---|---|
时机 | 能力获得时 | 能力激活时 |
频率 | 一次性的 | 可重复的 |
用途 | 初始化配置 | 执行 gameplay 逻辑 |
类比 | 装备武器 | 使用武器攻击 |
创建WarriorGameplayAbility
创建激活策略枚举
UENUM(BlueprintType)
enum class EWarriorAbilityActivationPolicy : uint8
{
OnTriggered,
OnGiven
};
/**
*
*/
UCLASS()
class WARRIOR_API UWarriorGameplayAbility : public UGameplayAbility
{
GENERATED_BODY()
protected:
//~ Begin UGameplayAbility Interface.
virtual void OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) override;
virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override;
//~ End UGameplayAbility Interface
UPROPERTY(EditDefaultsOnly, Category = "WarriorAbility")
EWarriorAbilityActivationPolicy AbilityActivationPolicy = EWarriorAbilityActivationPolicy::OnTriggered;
};
重写OnGiveAbility和EndAbility
//~ Begin UGameplayAbility Interface.
virtual void OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) override;
virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override;
//~ End UGameplayAbility Interface
OnGiveAbility - 能力授予时
作用 当GameplayAbility被授予给Actor的AbilitySystemComponent时调用
EndAbility - 能力结束时
作用 终止并清理GameplayAbility
void UWarriorGameplayAbility::OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec)
{
Super::OnGiveAbility(ActorInfo, Spec);
if (AbilityActivationPolicy == EWarriorAbilityActivationPolicy::OnGiven)
{
if (ActorInfo && !Spec.IsActive())
{
ActorInfo->AbilitySystemComponent->TryActivateAbility(Spec.Handle);
}
}
}
void UWarriorGameplayAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled)
{
Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);
if (AbilityActivationPolicy == EWarriorAbilityActivationPolicy::OnGiven)
{
if (ActorInfo)
{
ActorInfo->AbilitySystemComponent->ClearAbility(Handle);
}
}
}
FGameplayAbilitySpec
是 GAS 中一个非常重要的数据结构,它代表了一个GameplayAbility的实例化配置和运行时状态。
核心概念
FGameplayAbilitySpec
是能力蓝图类(UGameplayAbility)的运行时实例句柄和配置容器。
主要组成部分
struct GAMEPLAYABILITIES_API FGameplayAbilitySpec
{
// 核心成员变量:
FGameplayAbilitySpecHandle Handle; // 唯一标识符
UGameplayAbility* Ability; // 能力类指针
int32 Level; // 能力等级
int32 InputID; // 绑定的输入ID
FGameplayAbilityActivationInfo ActivationInfo; // 激活信息
TArray<FGameplayAbilitySpecHandle> SourceObjectList; // 来源对象
UObject* SourceObject; // 能力来源
// ... 其他成员
};
创建GameplayAbility蓝图
三、CreateWeaponClass
创建武器类
//WarriorWeaponBase.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "WarriorWeaponBase.generated.h"
class UBoxComponent;
UCLASS()
class WARRIOR_API AWarriorWeaponBase : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AWarriorWeaponBase();
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapons")
UStaticMeshComponent* WeaponMesh;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapons")
UBoxComponent* WeaponCollisionBox;
};
//WarriorWeaponBase.cpp
#include "items/Weapons/WarriorWeaponBase.h"
#include "Components/BoxComponent.h"
AWarriorWeaponBase::AWarriorWeaponBase()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
WeaponMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("WeaponMesh"));
SetRootComponent(WeaponMesh);
WeaponCollisionBox = CreateDefaultSubobject<UBoxComponent>(TEXT("WeaponCollisionBox"));
WeaponCollisionBox->SetupAttachment(GetRootComponent());
WeaponCollisionBox->SetBoxExtent(FVector(20.f));
WeaponCollisionBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
创建武器基类和主角武器蓝图
添加网格体和碰撞体
四、Grant Ability
使用SpawnActor蓝图生成武器并附着在角色上
保持相对 (Keep Relative) | 子物体保持当前相对于父物体的变换。 | 子物体与父物体保持附加瞬间的相对偏移量。 | 角色拿武器、车辆组装、UI元素嵌套。 |
保持场景 (Keep World) | 子物体保持当前在世界坐标系中的绝对变换。 | 子物体与父物体之间的相对关系由附加瞬间的世界坐标差决定。 | 将一个世界中的物体“交接”给一个移动的平台或角色。 |
对齐到目标 (Snap to Target) | 子物体立即移动到父物体的原点,相对变换归零。 | 子物体与父物体完全对齐,没有相对偏移。 | 将物体精准放入预定义的插槽(Socket)、组装零件到精确位置。 |
因为武器是OnGiven策略,所以后面要带EndAbility函数
创建子蓝图GA_Hero_SpawnAxe
在网格体添加武器插槽,调整武器位置
在GA_Hero_SpawnAxe能力中添加抛掷的斧头和插槽名称
现在还不会生成武器,因为还没有给角色分配能力
使用数据资源和函数分配
创建DataAsset_StartUpDataBase
//DataAsset_StartUpDataBase.h
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "DataAsset_StartUpDataBase.generated.h"
class UWarriorGameplayAbility;
class UWarriorAbilitySystemComponent;
/**
*
*/
UCLASS()
class WARRIOR_API UDataAsset_StartUpDataBase : public UDataAsset
{
GENERATED_BODY()
public:
virtual void GiveToAbilitySystemComponent(UWarriorAbilitySystemComponent* InASCToGive, int32 ApplyLevel = 1);
protected:
UPROPERTY(EditDefaultsOnly, Category = "StartUpData")
TArray< TSubclassOf < UWarriorGameplayAbility > > ActivateOnGivenAbilities;
UPROPERTY(EditDefaultsOnly, Category = "StartUpData")
TArray< TSubclassOf < UWarriorGameplayAbility > > ReactiveAbilities;
void GrantAbilities(const TArray< TSubclassOf < UWarriorGameplayAbility > >& InAbilitiesToGive, UWarriorAbilitySystemComponent* InASCToGive, int32 ApplyLevel = 1);
};
-
ActivateOnGivenAbilities
(授予时激活的技能)-
比喻:像一个被动生效的“光环”或“状态”。
-
特点:一旦学会(授予),它就像电器的电源开关被打开一样,开始持续工作或立即产生一次效果,之后它要么持续生效,要么就结束了。它只关心“获得”这个动作本身。
-
例如:
-
+10% 最大生命的buff(持续生效)。
-
获得时播放一个升级光环特效(瞬发,一次性的)。
-
-
-
ReactiveAbilities
(反应式技能)-
比喻:像一个自动触发的“陷阱”或“反射神经”。
-
特点:它被授予后,处于一种“待机”或“监听”状态。它不立即生效,而是在等待一个特定的“游戏事件”发生。当事件发生时,它才被“触发”或“激活”。
-
例如:
-
监听事件:“当被攻击时” -> 触发:有20%几率格挡这次伤害。
-
监听事件:“当生命值低于30%时” -> 触发:自动使用一个大血瓶
-
-
void UDataAsset_StartUpDataBase::GrantAbilities(const TArray<TSubclassOf<UWarriorGameplayAbility>>& InAbilitiesToGive, UWarriorAbilitySystemComponent* InASCToGive, int32 ApplyLevel)
{
if (InAbilitiesToGive.IsEmpty())
{
return;
}
for (const TSubclassOf<UWarriorGameplayAbility>& Ability : InAbilitiesToGive)
{
if (!Ability) continue;
FGameplayAbilitySpec AbilitySpec(Ability);
AbilitySpec.SourceObject = InASCToGive->GetAvatarActor();
AbilitySpec.Level = ApplyLevel;
InASCToGive->GiveAbility(AbilitySpec);
}
}
FGameplayAbilitySpec AbilitySpec(Ability)
是在创建一个能力的规格说明
InASCToGive->GiveAbility(AbilitySpec)就是给AbilitySystem分配GameplayAbility
创建子类DataAsset_HeroStartUpData,用于创建英雄数据的类,再创建数据资产
把GA_Hero_SpawnAxe能力分配到数组
五、Load In DataAsset
在角色类WarriorBaseCharacter中添加CharacterStartUpData
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "CharacterData")
TSoftObjectPtr< UDataAsset_StartUpDataBase > CharacterStartUpData;
特性 | CharacterStartUpData (软指针) |
InputConfigDataAsset (直接指针) |
---|---|---|
加载时机 | 按需、异步加载(Lazy Load) | 随拥有者同步、立即加载 |
内存管理 | 动态加载和卸载,优化内存使用 | 常驻内存,生命周期长 |
引用类型 | 弱引用:资源缺失不会导致父对象加载失败 | 强引用:资源缺失会导致父对象引用错误 |
设计目的 | 管理大型、可选的数据集,支持异步流 | 提供小型、关键、基础的即时服务 |
典型场景 | 角色初始数据、技能库、物品库、本地化数据表 | 输入配置、游戏基础设置、物理材料、控制台变量 |
两种加载方法(同步、异步)
修改WarriorHeroCharacter的PossessedBy方法
void AWarriorHeroCharacter::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
if (!CharacterStartUpData.IsNull())
{
if (UDataAsset_StartUpDataBase* LoadedData = CharacterStartUpData.LoadSynchronous())
{
LoadedData->GiveToAbilitySystemComponent(WarriorAbilitySystemComponent);
}
}
}
在WarriorBaseCharacter检查数据是否以分配
void AWarriorBaseCharacter::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
if (WarriorAbilitySystemComponent)
{
WarriorAbilitySystemComponent->InitAbilityActorInfo(this, this);
ensureMsgf(!CharacterStartUpData.IsNull(), TEXT("Forgot to assign start up data to %s"), *GetName());
}
}
英雄蓝图分配数据
完成
更多推荐
所有评论(0)