181 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			181 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| 
								 | 
							
								## 前言
							 | 
						|||
| 
								 | 
							
								本人最近看了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;
							 | 
						|||
| 
								 | 
							
								```
							 |