视频学习: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

  • 职责

    • 定义修改哪些属性(如 HealthManaAttackPower),如何修改(加、减、乘、除)。

    • 携带和授予游戏标签(Gameplay Tags),例如应用一个GE来为目标添加 Status.Stun(眩晕)标签。

    • 定义周期效果(Periodic Effects),如每秒造成一次伤害。

4. Attribute Set - “数值仓库”

  • 是什么? 一个 UObject 类,通常被ASC所持有。

  • 作用定义和存储所有游戏属性的当前值(如生命值、魔法值、力量、敏捷)。

  • 职责

    • 声明属性(如 HealthMaxHealthManaAttackPower)。

    • 响应属性值的变化(通过 PreAttributeChange 和 PostGameplayEffectExecute 函数),可以在这里进行 clamping(限制范围,如确保生命值不超过最大值)或触发其他事件。

5. Gameplay Tags - “描述状态的标签系统”

  • 是什么? 一个类似于 FName 但组织成树形结构的标签系统(如 Ability.Spell.FireballStatus.StunState.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

视频:2-13_哔哩哔哩_bilibili

能力是赋给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

视频:2-14_哔哩哔哩_bilibili

创建武器类

//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);
};
  1. ActivateOnGivenAbilities (授予时激活的技能)

    • 比喻:像一个被动生效的“光环”或“状态”

    • 特点:一旦学会(授予),它就像电器的电源开关被打开一样,开始持续工作或立即产生一次效果,之后它要么持续生效,要么就结束了。它只关心“获得”这个动作本身

    • 例如

      • +10% 最大生命的buff(持续生效)。

      • 获得时播放一个升级光环特效(瞬发,一次性的)。

  2. 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());
	}
}

英雄蓝图分配数据

完成

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐