From 021f30632a0806a5363557f2198b3452e217d1cc Mon Sep 17 00:00:00 2001 From: MostExcellent Date: Thu, 24 Oct 2024 15:16:02 -0700 Subject: [PATCH] Fix ability input system and more --- .../Abilities/VRGameplayAbility.cpp | 37 ++++ .../VRAbilitySystemComponent.cpp | 164 ++++++++++++++++++ .../Private/Player/VRPlayerController.cpp | 95 ++++++++++ .../VRGAS/Source/VRGAS/Private/VRGASTypes.cpp | 2 +- .../Abilities/VRGameplayAbility.h | 38 ++++ .../AbilitySystem/VRAbilitySystemComponent.h | 52 +++++- .../VRGAS/Public/Input/VRInputComponent.h | 7 +- .../VRGAS/Public/Player/VRPlayerController.h | 12 ++ .../VRGAS/Source/VRGAS/Public/VRGASTypes.h | 3 +- 9 files changed, 398 insertions(+), 12 deletions(-) create mode 100644 Plugins/VRGAS/Source/VRGAS/Private/AbilitySystem/Abilities/VRGameplayAbility.cpp create mode 100644 Plugins/VRGAS/Source/VRGAS/Public/AbilitySystem/Abilities/VRGameplayAbility.h diff --git a/Plugins/VRGAS/Source/VRGAS/Private/AbilitySystem/Abilities/VRGameplayAbility.cpp b/Plugins/VRGAS/Source/VRGAS/Private/AbilitySystem/Abilities/VRGameplayAbility.cpp new file mode 100644 index 0000000..ce317a6 --- /dev/null +++ b/Plugins/VRGAS/Source/VRGAS/Private/AbilitySystem/Abilities/VRGameplayAbility.cpp @@ -0,0 +1,37 @@ +// Copyright + + +#include "AbilitySystem/Abilities/VRGameplayAbility.h" + +#include "AbilitySystemComponent.h" + +UVRGameplayAbility::UVRGameplayAbility() +{ + ActivationPolicy = EVRAbilityActivationPolicy::OnInputTriggered; +} + +void UVRGameplayAbility::OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) +{ + Super::OnGiveAbility(ActorInfo, Spec); + + if (ActivationPolicy == EVRAbilityActivationPolicy::OnGiven) + { + if (ActorInfo && !Spec.IsActive()) + { + ActorInfo->AbilitySystemComponent->TryActivateAbility(Spec.Handle); + } + } +} + +void UVRGameplayAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) +{ + Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); + + if (ActivationPolicy == EVRAbilityActivationPolicy::OnGiven) + { + if (ActorInfo) + { + ActorInfo->AbilitySystemComponent->ClearAbility(Handle); + } + } +} diff --git a/Plugins/VRGAS/Source/VRGAS/Private/AbilitySystem/VRAbilitySystemComponent.cpp b/Plugins/VRGAS/Source/VRGAS/Private/AbilitySystem/VRAbilitySystemComponent.cpp index 01c8ef0..c7f329e 100644 --- a/Plugins/VRGAS/Source/VRGAS/Private/AbilitySystem/VRAbilitySystemComponent.cpp +++ b/Plugins/VRGAS/Source/VRGAS/Private/AbilitySystem/VRAbilitySystemComponent.cpp @@ -2,3 +2,167 @@ #include "AbilitySystem/VRAbilitySystemComponent.h" + +#include "Logging.h" +#include "AbilitySystem/Abilities/VRGameplayAbility.h" +#include "AbilitySystem/StartupData/VRStartupAbilitySystemData.h" + +UVRAbilitySystemComponent::UVRAbilitySystemComponent(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +} + +void UVRAbilitySystemComponent::ApplyStartupData() +{ + if (StartupData) + { + StartupData->GiveToAbilitySystemComponent(this, DefaultStartingAbilityLevel); + } + + UE_LOG(VRGAS_Log, Error, TEXT("UVRAbilitySystemComponent::ApplyStartupData - No Startup Data found!")) +} + +void UVRAbilitySystemComponent::InputTagPressed(FGameplayTag& InTag) +{ + if (!InTag.IsValid()) return; + + FScopedAbilityListLock ActiveScopeLoc(*this); + for (FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities()) + { + if (AbilitySpec.Ability && AbilitySpec.DynamicAbilityTags.HasTagExact(InTag)) + { + InputPressedSpecHandles.AddUnique(AbilitySpec.Handle); + InputHeldSpecHandles.AddUnique(AbilitySpec.Handle); + } + } +} + +void UVRAbilitySystemComponent::InputTagReleased(FGameplayTag& InTag) +{ + if (!InTag.IsValid()) return; + + FScopedAbilityListLock ActiveScopeLoc(*this); + for (FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities()) + { + if (AbilitySpec.Ability && (AbilitySpec.DynamicAbilityTags.HasTagExact(InTag))) + { + InputReleasedSpecHandles.AddUnique(AbilitySpec.Handle); + InputHeldSpecHandles.Remove(AbilitySpec.Handle); + } + } +} + +// We're doing the Lyra approach +void UVRAbilitySystemComponent::ProcessAbilityInput(float DeltaTime, bool bGamePaused) +{ + // TODO: Add tag that blocks all abilities + + TArray AbilitiesToActivate; + + // Handle held abilities + for (const FGameplayAbilitySpecHandle& AbilitySpecHandle : InputHeldSpecHandles) + { + if (const FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(AbilitySpecHandle)) + { + if (AbilitySpec->Ability && !AbilitySpec->IsActive()) + { + const UVRGameplayAbility* AbilityCDO = CastChecked(AbilitySpec->Ability); + + if (AbilityCDO->GetActivationPolicy() == EVRAbilityActivationPolicy::OnInputHeld) + { + AbilitiesToActivate.AddUnique(AbilitySpec->Handle); + } + } + } + } + + for (const FGameplayAbilitySpecHandle& AbilitySpecHandle : InputPressedSpecHandles) + { + if (FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(AbilitySpecHandle)) + { + if (AbilitySpec->Ability) + { + AbilitySpec->InputPressed; + + if (AbilitySpec->IsActive()) + { + AbilitySpecInputPressed(*AbilitySpec); + } + else + { + // ReSharper disable once CppTooWideScopeInitStatement + const UVRGameplayAbility* AbilityCDO = CastChecked(AbilitySpec->Ability); + + if (AbilityCDO->GetActivationPolicy() == EVRAbilityActivationPolicy::OnInputTriggered) + { + AbilitiesToActivate.AddUnique(AbilitySpec->Handle); + } + } + } + } + + } + + // Activate all abilities to be activated + for (const FGameplayAbilitySpecHandle& AbilitySpecHandle : AbilitiesToActivate) + { + TryActivateAbility(AbilitySpecHandle); + } + + // Handle released ability inputs + for (FGameplayAbilitySpecHandle& AbilitySpecHandle : InputReleasedSpecHandles) + { + if (FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(AbilitySpecHandle)) + { + if (AbilitySpec->Ability) + { + AbilitySpec->InputPressed = false; + + if (AbilitySpec->IsActive()) + { + AbilitySpecInputReleased(*AbilitySpec); + } + } + } + } + + // Clear cached ability handles, as in Lyra + InputPressedSpecHandles.Empty(); + InputReleasedSpecHandles.Empty(); +} + +void UVRAbilitySystemComponent::ClearAbilityInput() +{ + InputPressedSpecHandles.Empty(); + InputReleasedSpecHandles.Empty(); + InputHeldSpecHandles.Empty(); +} + +void UVRAbilitySystemComponent::AbilitySpecInputPressed(FGameplayAbilitySpec& Spec) +{ + Super::AbilitySpecInputPressed(Spec); + + // Do like Lyra + if (Spec.IsActive()) + { + // Invoke the InputPressed event. + // This is not replicated here. + // If someone is listening, they may replicate the InputPressed event to the server. + InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputPressed, Spec.Handle, Spec.ActivationInfo.GetActivationPredictionKey()); + } +} + +void UVRAbilitySystemComponent::AbilitySpecInputReleased(FGameplayAbilitySpec& Spec) +{ + Super::AbilitySpecInputReleased(Spec); + + if (Spec.IsActive()) + { + // Invoke the InputReleased event. + // This is not replicated here. + // If someone is listening, they may replicate the InputReleased event to the server. + InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputReleased, Spec.Handle, Spec.ActivationInfo.GetActivationPredictionKey()); + } +} + + diff --git a/Plugins/VRGAS/Source/VRGAS/Private/Player/VRPlayerController.cpp b/Plugins/VRGAS/Source/VRGAS/Private/Player/VRPlayerController.cpp index b217032..548d0c8 100644 --- a/Plugins/VRGAS/Source/VRGAS/Private/Player/VRPlayerController.cpp +++ b/Plugins/VRGAS/Source/VRGAS/Private/Player/VRPlayerController.cpp @@ -2,3 +2,98 @@ #include "Player/VRPlayerController.h" + +#include "AbilitySystemGlobals.h" +#include "EnhancedInputSubsystems.h" +#include "Logging.h" +#include "AbilitySystem/VRAbilitySystemComponent.h" +#include "Input/VRInputComponent.h" + +AVRPlayerController::AVRPlayerController() +{ + bReplicates = true; +} + +void AVRPlayerController::PostProcessInput(const float DeltaTime, const bool bGamePaused) +{ + if (UVRAbilitySystemComponent* VRAbilitySystemComponent = GetVRAbilitySystemComponent()) + { + VRAbilitySystemComponent->ProcessAbilityInput(DeltaTime, bGamePaused); + } + + Super::PostProcessInput(DeltaTime, bGamePaused); +} + +void AVRPlayerController::SetupInputComponent() +{ + Super::SetupInputComponent(); + + UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(GetLocalPlayer()); + // We could check the subsystem instead of doing an if statement + // check(Subsystem); + + if (InputMappingContext) + { + Subsystem->AddMappingContext(InputMappingContext, 0); + } + else + { + UE_LOG(VRGAS_Log, Error, TEXT("AVRPlayerController::SetupInputComponent - Input Mapping Context not found!")) + } + + if (UVRInputComponent* VRInputComponent = Cast(InputComponent)) + { + VRInputComponent->BindAbilityActions(InputConfig, this, + &ThisClass::Input_AbilityTagPressed, &ThisClass::Input_AbilityTagReleased); + } + else + { + UE_LOG(VRGAS_Log, Error, TEXT("AVRPlayerController::SetupInputComponent - Input Component is not of VRGAS type!")) + } +} + +void AVRPlayerController::OnPossess(APawn* InPawn) +{ + Super::OnPossess(InPawn); + + CachedAbilitySystemComponent = Cast( + UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(GetPawn())); +} + +UAbilitySystemComponent* AVRPlayerController::GetAbilitySystemComponent() +{ + if (!CachedAbilitySystemComponent.IsValid()) + { + CachedAbilitySystemComponent = Cast( + UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(GetPawn())); + + if (!CachedAbilitySystemComponent.IsValid()) return nullptr; + } + + return CachedAbilitySystemComponent.Get(); +} + +UVRAbilitySystemComponent* AVRPlayerController::GetVRAbilitySystemComponent() +{ + return Cast(GetAbilitySystemComponent()); +} + +void AVRPlayerController::Input_AbilityTagPressed(FGameplayTag InTag) +{ + if (!GetVRAbilitySystemComponent()) + { + UE_LOG(VRGAS_Log, Error, TEXT("AVRPlayerController::Input_AbilityTagPressed - No Vagabond Rose ASC present!")) + } + + GetVRAbilitySystemComponent()->InputTagPressed(InTag); +} + +void AVRPlayerController::Input_AbilityTagReleased(FGameplayTag InTag) +{ + if (!GetVRAbilitySystemComponent()) + { + UE_LOG(VRGAS_Log, Error, TEXT("AVRPlayerController::Input_AbilityTagReleased - No Vagabond Rose ASC present!")) + } + + GetVRAbilitySystemComponent()->InputTagReleased(InTag); +} diff --git a/Plugins/VRGAS/Source/VRGAS/Private/VRGASTypes.cpp b/Plugins/VRGAS/Source/VRGAS/Private/VRGASTypes.cpp index 12841e0..e5e7515 100644 --- a/Plugins/VRGAS/Source/VRGAS/Private/VRGASTypes.cpp +++ b/Plugins/VRGAS/Source/VRGAS/Private/VRGASTypes.cpp @@ -4,7 +4,7 @@ bool FVRAbilitySet::IsValid() const { - return AbilityClass; + return AbilityClass != nullptr; } bool FVRAbilitySet::IsValidInput() const diff --git a/Plugins/VRGAS/Source/VRGAS/Public/AbilitySystem/Abilities/VRGameplayAbility.h b/Plugins/VRGAS/Source/VRGAS/Public/AbilitySystem/Abilities/VRGameplayAbility.h new file mode 100644 index 0000000..437cbdd --- /dev/null +++ b/Plugins/VRGAS/Source/VRGAS/Public/AbilitySystem/Abilities/VRGameplayAbility.h @@ -0,0 +1,38 @@ +// Copyright + +#pragma once + +#include "CoreMinimal.h" +#include "Abilities/GameplayAbility.h" +#include "VRGameplayAbility.generated.h" + +UENUM(BlueprintType) +enum class EVRAbilityActivationPolicy : uint8 +{ + OnInputTriggered, + OnInputHeld, + OnGiven +}; +/** + * + */ +UCLASS() +class VRGAS_API UVRGameplayAbility : public UGameplayAbility +{ + GENERATED_BODY() + +public: + UVRGameplayAbility(); + + EVRAbilityActivationPolicy GetActivationPolicy() const { return ActivationPolicy; } + +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, BlueprintReadOnly, Category = "Activation") + EVRAbilityActivationPolicy ActivationPolicy; + +}; diff --git a/Plugins/VRGAS/Source/VRGAS/Public/AbilitySystem/VRAbilitySystemComponent.h b/Plugins/VRGAS/Source/VRGAS/Public/AbilitySystem/VRAbilitySystemComponent.h index 9a1dc30..2b6e717 100644 --- a/Plugins/VRGAS/Source/VRGAS/Public/AbilitySystem/VRAbilitySystemComponent.h +++ b/Plugins/VRGAS/Source/VRGAS/Public/AbilitySystem/VRAbilitySystemComponent.h @@ -6,9 +6,10 @@ #include "AbilitySystemComponent.h" #include "VRAbilitySystemComponent.generated.h" -DECLARE_MULTICAST_DELEGATE_OneParam(FEffectAssetTags, const FGameplayTagContainer& /*AssetTags*/); -DECLARE_MULTICAST_DELEGATE(FAbilitiesGiven); +// DECLARE_MULTICAST_DELEGATE_OneParam(FEffectAssetTags, const FGameplayTagContainer& /*AssetTags*/); +// DECLARE_MULTICAST_DELEGATE(FAbilitiesGiven); +class UVRStartupAbilitySystemData; /** * */ @@ -18,13 +19,50 @@ class VRGAS_API UVRAbilitySystemComponent : public UAbilitySystemComponent GENERATED_BODY() public: - void AbilityActorInfoSet(); - - FEffectAssetTags EffectAssetTags; - FAbilitiesGiven OnAbilitiesGiven; + UVRAbilitySystemComponent(const FObjectInitializer& ObjectInitializer); + // void AbilityActorInfoSet(); + // FEffectAssetTags EffectAssetTags; + // FAbilitiesGiven OnAbilitiesGiven; + #pragma region PublicStartup + UPROPERTY(BlueprintReadOnly, Category = "Startup Data") bool bStartupAbilitiesGiven = false; - // Add startup abilities + virtual void ApplyStartupData(); + #pragma endregion + // Add startup abilities + + #pragma region PublicInput + // Handle pressed input + virtual void InputTagPressed(FGameplayTag& InTag); + // Handle released input + virtual void InputTagReleased(FGameplayTag& InTag); + + void ProcessAbilityInput(float DeltaTime, bool bGamePaused); + void ClearAbilityInput(); + #pragma endregion + +protected: + #pragma region ProtectedInput + virtual void AbilitySpecInputPressed(FGameplayAbilitySpec& Spec) override; + virtual void AbilitySpecInputReleased(FGameplayAbilitySpec& Spec) override; + + // Handles to abilities that had their input pressed this frame. + TArray InputPressedSpecHandles; + + // Handles to abilities that had their input released this frame. + TArray InputReleasedSpecHandles; + + // Handles to abilities that have their input held. + TArray InputHeldSpecHandles; + #pragma endregion + + #pragma region ProtectedStartup + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Startup Data") + TObjectPtr StartupData; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Startup Data", meta = (ClampMin = 1)) + int DefaultStartingAbilityLevel = 1; + #pragma endregion }; diff --git a/Plugins/VRGAS/Source/VRGAS/Public/Input/VRInputComponent.h b/Plugins/VRGAS/Source/VRGAS/Public/Input/VRInputComponent.h index d1012dd..6d2d7e8 100644 --- a/Plugins/VRGAS/Source/VRGAS/Public/Input/VRInputComponent.h +++ b/Plugins/VRGAS/Source/VRGAS/Public/Input/VRInputComponent.h @@ -13,6 +13,7 @@ class VRGAS_API UVRInputComponent : public UEnhancedInputComponent { GENERATED_BODY() +public: template void BindNativeActions(const UVRInputConfig* InputConfig, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent, UserClass* Object,FuncType Func, bool bLogNotFound = true); @@ -25,7 +26,7 @@ template void UVRInputComponent::BindNativeActions(const UVRInputConfig* InputConfig, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent, UserClass* Object,FuncType Func, const bool bLogNotFound) { - check(InputConfig) + check(InputConfig); if (UInputAction* Action = InputConfig->FindNativeInputActionForTag(InputTag, bLogNotFound)) { BindAction(Action, TriggerEvent, Object, Func); @@ -33,9 +34,9 @@ void UVRInputComponent::BindNativeActions(const UVRInputConfig* InputConfig, con } template -void UVRInputComponent::BindAbilityActions(const UVRInputConfig* InputConfig, UserClass* Object, PressedFuncType PressedFunc, ReleasedFuncType ReleasedFunc); +void UVRInputComponent::BindAbilityActions(const UVRInputConfig* InputConfig, UserClass* Object, PressedFuncType PressedFunc, ReleasedFuncType ReleasedFunc) { - check(InputConfig) + check(InputConfig); for (const FVRTagInputBinding& Binding : InputConfig->AbilityInputActions) { if (Binding.IsValid()) diff --git a/Plugins/VRGAS/Source/VRGAS/Public/Player/VRPlayerController.h b/Plugins/VRGAS/Source/VRGAS/Public/Player/VRPlayerController.h index a1e0b85..f1a3cd2 100644 --- a/Plugins/VRGAS/Source/VRGAS/Public/Player/VRPlayerController.h +++ b/Plugins/VRGAS/Source/VRGAS/Public/Player/VRPlayerController.h @@ -6,6 +6,8 @@ #include "GameFramework/PlayerController.h" #include "VRPlayerController.generated.h" +class UVRAbilitySystemComponent; +struct FGameplayTag; class UVRInputConfig; class UInputMappingContext; class UAbilitySystemComponent; @@ -21,6 +23,10 @@ class VRGAS_API AVRPlayerController : public APlayerController public: AVRPlayerController(); + //~ Begin APlayerController Interface + virtual void PostProcessInput(const float DeltaTime, const bool bGamePaused) override; + //~ End APlayerContoller Interface + protected: // TODO: Implement controller virtual void SetupInputComponent() override; @@ -40,5 +46,11 @@ protected: UFUNCTION(BlueprintPure, Category = "Ability System", meta = (HideSelfPin)) UAbilitySystemComponent* GetAbilitySystemComponent(); + + UFUNCTION(BlueprintPure, Category = "Ability System", meta = (HideSelfPin)) + UVRAbilitySystemComponent* GetVRAbilitySystemComponent(); + + void Input_AbilityTagPressed(FGameplayTag InTag); + void Input_AbilityTagReleased(FGameplayTag InTag); #pragma endregion }; diff --git a/Plugins/VRGAS/Source/VRGAS/Public/VRGASTypes.h b/Plugins/VRGAS/Source/VRGAS/Public/VRGASTypes.h index eb3855d..86546dd 100644 --- a/Plugins/VRGAS/Source/VRGAS/Public/VRGASTypes.h +++ b/Plugins/VRGAS/Source/VRGAS/Public/VRGASTypes.h @@ -2,6 +2,7 @@ #include "CoreMinimal.h" #include "GameplayTagContainer.h" +#include "VRGASTypes.generated.h" class UGameplayAbility; @@ -17,7 +18,7 @@ struct FVRAbilitySet TSubclassOf AbilityClass; UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) - uint32 AbilityLevel = 1.f; + int AbilityLevel = 1; bool IsValid() const;