BlueRoseNote/03-UnrealEngine/Gameplay/GAS/(1)开始编写GameAbility相关逻辑.md
2023-06-29 11:55:02 +08:00

181 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 前言
本人最近看了GameplayAbility的wiki与官方的ActionRPG案例大致对此有了一定了解。所以在此分享相关经验顺便作为学习笔记。
wiki采用了第三人称模板来进行讲解。讲解了几个主要类的概念、参数以及用法。
ActionRPG则是一个较为完整的案例它通过c++ 往几个GameplayAbility的基础类中添加了若干逻辑使得之更加适合于RPG项目。大部分改动都很不错甚至直接复制让它作为你的RPG模板都是没问题。它的主要逻辑以及表现都在于蓝图中(技能与效果)所以它也是一个c++与蓝图结合开发的好例子。(注意GameAbility无法完全通过蓝图开发)
所以接下来我也会适当讲解actionRPG的结构。<br>
注意:有关网络同步的我都会略过。
## 相关资料
wiki
https://wiki.unrealengine.com/index.php?title=GameplayAbilities_and_You#Common_Issues
GameplayAbility文档
https://docs.unrealengine.com/en-us/Gameplay/GameplayAbilitySystem
actionRPG案例文章
https://docs.unrealengine.com/en-US/Resources/SampleGames/ARPG/index.html
## 启用GameAbility插件并且在你的项目中添加该模块
1. 在编辑Edit->插件Plugins找到Gameplay Ability 并启用。
2. 在你的c++项目(ProjectName).Build.cs文件的PublicDependencyModuleNames变量中添加 "GameplayAbilities", "GameplayTags", "GameplayTasks"。
例如:
```
PublicDependencyModuleNames.AddRange(new string[] { "GameplayAbilities", "GameplayTags", "GameplayTasks", "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay" });
```
### 注意事项
**在模块文件以及c++中包含GameplayBility前先需要确定是否在项目中启用GameplayAbility插件默认是不启用的。不然你的项目编译时会通过但运行时会显示无法加载游戏模块。**
## 建议
在模块h文件与cpp中定义这个模块的log标签这样你可以对log进行过滤显示。
```
//在h文件中
ACTIONRPG_API DECLARE_LOG_CATEGORY_EXTERN(LogActionRPG, Log, All);
```
```
//在cpp文件中
DEFINE_LOG_CATEGORY(LogActionRPG);
```
## 在角色类中挂载自定义的UGameplayAbilityComponent
actionRPG中定义了URPGAbilitySystemComponent作为挂载组件类它实现了以下函数
```
//通过Tag可以是多个tag来获取激活的技能
void GetActiveAbilitiesWithTags(const FGameplayTagContainer& GameplayTagContainer, TArray<URPGGameplayAbility*>& ActiveAbilities);
//取得角色等级这个意义不大因为不是所有的rpg游戏都有等级这一说
int32 GetDefaultAbilityLevel() const;
//通过全局的AbilitySystem来获取对应Actor所绑定的组件指针
static URPGAbilitySystemComponent* GetAbilitySystemComponentFromActor(const AActor* Actor, bool LookForComponent = false);
```
```
void URPGAbilitySystemComponent::GetActiveAbilitiesWithTags(const FGameplayTagContainer& GameplayTagContainer, TArray<URPGGameplayAbility*>& ActiveAbilities)
{
TArray<FGameplayAbilitySpec*> AbilitiesToActivate;
GetActivatableGameplayAbilitySpecsByAllMatchingTags(GameplayTagContainer, AbilitiesToActivate, false);
for (FGameplayAbilitySpec* Spec : AbilitiesToActivate)
{
TArray<UGameplayAbility*> AbilityInstances = Spec->GetAbilityInstances();
for (UGameplayAbility* ActiveAbility : AbilityInstances)
{
ActiveAbilities.Add(Cast<URPGGameplayAbility>(ActiveAbility));
}
}
}
URPGAbilitySystemComponent* URPGAbilitySystemComponent::GetAbilitySystemComponentFromActor(const AActor* Actor, bool LookForComponent)
{
return Cast<URPGAbilitySystemComponent>(UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Actor, LookForComponent));
}
```
## 定义角色类
ARPGCharacterBase继承于ACharacter与接口类IAbilitySystemInterface。
actionRPG中定义了ARPGCharacterBase作为角色类的基础类。因为这个类会适用于主角、友善NPC、敌方NPC所有我们不应该在这个类中进行输入绑定、以及各种Camera、movementComponent等非共用性组件的绑定与设置。
接下来我会讲解我阅读代码的顺序具体代码可以就请读者参考actionRPG。
首先
```
//GameplayAbilityComponent指针
class URPGAbilitySystemComponent* AbilitySystemComponent;
//因为继承IAbilitySystemInterface所以需要实现这个接口
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
//用以判断这个Actor是否初始化了GameplayAbilityComponent
bool bAbilitiesInitialized;
//用于存储GameplayAbility角色技能的容器并且会在游戏开始时向AbilitySystemComponent进行注册
//读者可以在看了我下一篇文章再添加
TArray<TSubclassOf<URPGGameplayAbility>> GameplayAbilities;
```
除此之外actionRPG还重写了PossessedBy以此来实现向AbilitySystemComponent注册Ability的功能。Wiki上的教程选择在Beginplay事件中进行注册之后再PossessedBy中进行刷新判断技能TArray是否有变化如果有就进行相应得修改。在本教程中我选择actionRPG的方案。
```
ARPGCharacterBase::ARPGCharacterBase()
{
AbilitySystemComponent = CreateDefaultSubobject<URPGAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
bAbilitiesInitialized = false;
}
void ARPGCharacterBase::AddStartupGameplayAbilities()
{
if (!bAbilitiesInitialized)
{
for (TSubclassOf<URPGGameplayAbility>& StartupAbility : GameplayAbilities)
{
AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(StartupAbility,1,INDEX_NONE,this));
}
bAbilitiesInitialized = true;
UE_LOG(LogActionRPG, Warning, TEXT("%s"), *FString("All Ablities registered"));
}
}
//RemoveStartupGameplayAbilities在actionRPG中的SetCharacterLevel函数中被调用
//但这个函数和GameplayAbility框架关系不大我就省略了
void ARPGCharacterBase::RemoveStartupGameplayAbilities()
{
if (bAbilitiesInitialized)
{
TArray<FGameplayAbilitySpecHandle> AbilitiesToRemove;
for (const FGameplayAbilitySpec& Spec : AbilitySystemComponent->GetActivatableAbilities())
{
if ((Spec.SourceObject == this) && GameplayAbilities.Contains(Spec.Ability->GetClass()))
{
AbilitiesToRemove.Add(Spec.Handle);
}
}
for (int32 i = 0; i < AbilitiesToRemove.Num(); i++)
{
AbilitySystemComponent->ClearAbility(AbilitiesToRemove[i]);
}
bAbilitiesInitialized = false;
}
}
void ARPGCharacterBase::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
if (AbilitySystemComponent)
{
AddStartupGameplayAbilities();
AbilitySystemComponent->InitAbilityActorInfo(this, this);
}
}
```
## UAbilitySystemComponent中的委托
```
/** Used to register callbacks to ability-key input */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAbilityAbilityKey, /*UGameplayAbility*, Ability, */int32, InputID);
/** Used to register callbacks to confirm/cancel input */
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FAbilityConfirmOrCancel);
/** Delegate for when an effect is applied */
DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnGameplayEffectAppliedDelegate, UAbilitySystemComponent*, const FGameplayEffectSpec&, FActiveGameplayEffectHandle);
/** Called on server whenever a GE is applied to self. This includes instant and duration based GEs. */
FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToSelf;
/** Called on server whenever a GE is applied to someone else. This includes instant and duration based GEs. */
FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToTarget;
/** Called on both client and server whenever a duraton based GE is added (E.g., instant GEs do not trigger this). */
FOnGameplayEffectAppliedDelegate OnActiveGameplayEffectAddedDelegateToSelf;
/** Called on server whenever a periodic GE executes on self */
FOnGameplayEffectAppliedDelegate OnPeriodicGameplayEffectExecuteDelegateOnSelf;
/** Called on server whenever a periodic GE executes on target */
FOnGameplayEffectAppliedDelegate OnPeriodicGameplayEffectExecuteDelegateOnTarget;
/** Register for when an attribute value changes */
FOnGameplayAttributeValueChange& GetGameplayAttributeValueChangeDelegate(FGameplayAttribute Attribute);
/** Callback anytime an ability is ended */
FAbilityEnded AbilityEndedCallbacks;
/** Called with a failure reason when an ability fails to execute */
FAbilityFailedDelegate AbilityFailedCallbacks;
```