From f6d54647aa442023826e9175777c04d875c3d9a3 Mon Sep 17 00:00:00 2001 From: Rafal Swierczek Date: Thu, 11 Jan 2024 03:06:23 +0000 Subject: [PATCH] Implemented AMyVICharacterBase --- .../Mesh/SK_Mannequin_Arms_Skeleton.uasset | 4 +- .../Content/Levels/TrainingFacility.umap | 2 +- .../Blueprints/Blueprint_CeilingLight.uasset | 4 +- .../HDRI/HDRI_Epic_Courtyard_Daylight.uasset | 4 +- EndlessVendetta/EndlessVendetta.uproject | 5 + .../EndlessVendetta/EndlessVendetta.Build.cs | 2 +- .../EndlessVendettaCharacter.h | 1 + .../EndlessVendetta/MyVICharacterBase.cpp | 309 ++++++++++++++++++ .../EndlessVendetta/MyVICharacterBase.h | 119 +++++++ 9 files changed, 442 insertions(+), 8 deletions(-) create mode 100644 EndlessVendetta/Source/EndlessVendetta/MyVICharacterBase.cpp create mode 100644 EndlessVendetta/Source/EndlessVendetta/MyVICharacterBase.h diff --git a/EndlessVendetta/Content/FirstPersonArms/Character/Mesh/SK_Mannequin_Arms_Skeleton.uasset b/EndlessVendetta/Content/FirstPersonArms/Character/Mesh/SK_Mannequin_Arms_Skeleton.uasset index bccc6d9a..edf6ed1a 100644 --- a/EndlessVendetta/Content/FirstPersonArms/Character/Mesh/SK_Mannequin_Arms_Skeleton.uasset +++ b/EndlessVendetta/Content/FirstPersonArms/Character/Mesh/SK_Mannequin_Arms_Skeleton.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bba7ff2cd9205073c53bbf6989c00058bf9cd1efe18fe82e0d996a80e5af38c6 -size 19118 +oid sha256:68905095ac47d5d9324390bebca7b0474c00209ccc52a38b99d60efb30a2720a +size 22064 diff --git a/EndlessVendetta/Content/Levels/TrainingFacility.umap b/EndlessVendetta/Content/Levels/TrainingFacility.umap index 7850a572..7fdf6121 100644 --- a/EndlessVendetta/Content/Levels/TrainingFacility.umap +++ b/EndlessVendetta/Content/Levels/TrainingFacility.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23f1d30713deb7b63e333e5bffd36f97365280f94c773c2b5a1128418a658c42 +oid sha256:928263e4929943c3bca1c28ad56a4a5530a5a7e2318a11275415abcb276a0e15 size 679253 diff --git a/EndlessVendetta/Content/StarterContent/Blueprints/Blueprint_CeilingLight.uasset b/EndlessVendetta/Content/StarterContent/Blueprints/Blueprint_CeilingLight.uasset index a0b975e9..c5c3b84e 100644 --- a/EndlessVendetta/Content/StarterContent/Blueprints/Blueprint_CeilingLight.uasset +++ b/EndlessVendetta/Content/StarterContent/Blueprints/Blueprint_CeilingLight.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d64387f3ed1672d52c8e9e16320a53f60d3928e01b51c09f9bd32f98d6fb31aa -size 43745 +oid sha256:4a60a29ad596546d481e43dfb8698842a78cc07f4a4b1000fa397cfba4e72331 +size 158206 diff --git a/EndlessVendetta/Content/StarterContent/HDRI/HDRI_Epic_Courtyard_Daylight.uasset b/EndlessVendetta/Content/StarterContent/HDRI/HDRI_Epic_Courtyard_Daylight.uasset index 0f990580..23be4194 100644 --- a/EndlessVendetta/Content/StarterContent/HDRI/HDRI_Epic_Courtyard_Daylight.uasset +++ b/EndlessVendetta/Content/StarterContent/HDRI/HDRI_Epic_Courtyard_Daylight.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc35eb2d43a47427d30aba0196f9eac90d089dd3abca319528c5d25c83510d0d -size 72364642 +oid sha256:4d43fd35603e034f1d53c3a74456cd8666bf76207bbc00be641fe7fec022279e +size 66790690 diff --git a/EndlessVendetta/EndlessVendetta.uproject b/EndlessVendetta/EndlessVendetta.uproject index 340e263e..7e992166 100644 --- a/EndlessVendetta/EndlessVendetta.uproject +++ b/EndlessVendetta/EndlessVendetta.uproject @@ -23,6 +23,11 @@ "TargetAllowList": [ "Editor" ] + }, + { + "Name": "VaultIt", + "Enabled": true, + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/7d19b5622d6846edb9881e6c89bce05e" } ] } \ No newline at end of file diff --git a/EndlessVendetta/Source/EndlessVendetta/EndlessVendetta.Build.cs b/EndlessVendetta/Source/EndlessVendetta/EndlessVendetta.Build.cs index 619c70e9..1795e500 100644 --- a/EndlessVendetta/Source/EndlessVendetta/EndlessVendetta.Build.cs +++ b/EndlessVendetta/Source/EndlessVendetta/EndlessVendetta.Build.cs @@ -12,7 +12,7 @@ public class EndlessVendetta : ModuleRules { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "EnhancedInput", "AIModule", "GameplayTasks", "NavigationSystem", "UMG", "Slate", "SlateCore", "Niagara", "NiagaraCore", "NiagaraShader", - "HTTP", "Json", "JsonUtilities" + "HTTP", "Json", "JsonUtilities", "VaultIt", "MotionWarping", "GameplayAbilities" }); } } \ No newline at end of file diff --git a/EndlessVendetta/Source/EndlessVendetta/EndlessVendettaCharacter.h b/EndlessVendetta/Source/EndlessVendetta/EndlessVendettaCharacter.h index 88f03847..81ab9c4f 100644 --- a/EndlessVendetta/Source/EndlessVendetta/EndlessVendettaCharacter.h +++ b/EndlessVendetta/Source/EndlessVendetta/EndlessVendettaCharacter.h @@ -9,6 +9,7 @@ #include "Components/ArrowComponent.h" #include "GadgetSystem/GadgetManager.h" #include "Inventory/InventoryComponent.h" +#include "Pawn/VICharacter.h" #include "EndlessVendettaCharacter.generated.h" diff --git a/EndlessVendetta/Source/EndlessVendetta/MyVICharacterBase.cpp b/EndlessVendetta/Source/EndlessVendetta/MyVICharacterBase.cpp new file mode 100644 index 00000000..3f533ccc --- /dev/null +++ b/EndlessVendetta/Source/EndlessVendetta/MyVICharacterBase.cpp @@ -0,0 +1,309 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#include "EndlessVendetta/MyVICharacterBase.h" +#include "Pawn/VICharacterBase.h" +#include "Net/UnrealNetwork.h" +#include "GameFramework/CharacterMovementComponent.h" +#include "Pawn/VIPawnVaultComponent.h" +#include "MotionWarpingComponent.h" +#include "VIBlueprintFunctionLibrary.h" + +void AMyVICharacterBase::BeginPlay() +{ + Super::BeginPlay(); + + VaultComponent = IVIPawnInterface::Execute_GetPawnVaultComponent(this); + MotionWarpingComponent = IVIPawnInterface::Execute_GetMotionWarpingComponent(this); +} + +void AMyVICharacterBase::CheckJumpInput(float DeltaTime) +{ + const bool bIsVaulting = IsVaulting(); + + // Server update simulated proxies with correct vaulting state + if (GetLocalRole() == ROLE_Authority && GetNetMode() != NM_Standalone) + { + bRepIsVaulting = bIsVaulting; + } + + // Try to vault from local input + if (IsLocallyControlled() && VaultComponent) + { + // Disable jump if vaulting + if (VaultComponent->bPressedVault) + { + bPressedJump = false; + } + + // Possibly execute vault + if (GetCharacterMovement()) + { + VaultComponent->CheckVaultInput(DeltaTime, GetCharacterMovement()->MovementMode); + } + else + { + VaultComponent->CheckVaultInput(DeltaTime); + } + } + + // Pick up changes in vaulting state to change movement mode + // to something other than flying (required for root motion on Z) + if (bWasVaulting && !bIsVaulting) + { + StopVaultAbility(); + } + + // Call super so we actually jump if we're meant to + Super::CheckJumpInput(DeltaTime); + + // Cache end of frame + bWasVaulting = bIsVaulting; +} + +void AMyVICharacterBase::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const +{ + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + + DOREPLIFETIME_CONDITION(AMyVICharacterBase, bRepIsVaulting, COND_SimulatedOnly); + DOREPLIFETIME_CONDITION(AMyVICharacterBase, RepMotionMatch, COND_SimulatedOnly); +} + +void AMyVICharacterBase::Jump() +{ + // If missing critical components then jump and exit + if (!VaultComponent || !GetCharacterMovement()) + { + Super::Jump(); + return; + } + + // Either jump or vault, determined by VaultComponent::EVIJumpKeyPriority + if (VaultComponent->Jump(GetCharacterMovement()->GetGravityZ(), CanJump(), GetCharacterMovement()->IsFalling())) + { + // Jump normally + Super::Jump(); + } + else + { + // Jump key essentially presses the vault input + VaultComponent->Vault(); + } +} + +void AMyVICharacterBase::StopJumping() +{ + Super::StopJumping(); + + // Release vault input if the jump key pressed vault instead + if (VaultComponent) + { + VaultComponent->StopJumping(); + } +} + +void AMyVICharacterBase::StartVaultAbility_Implementation() +{ + // Called by GA_Vault + // Need to be in flying mode to have root motion on Z axis + if (GetCharacterMovement() && GetLocalRole() > ROLE_SimulatedProxy) + { + GetCharacterMovement()->SetMovementMode(MOVE_Flying); + } +} + +void AMyVICharacterBase::StopVaultAbility() +{ + // Called by CheckJumpInput() + // Exiting flying mode + // This may put is straight into falling if we aren't properly grounded, which is fine + if (GetCharacterMovement() && GetLocalRole() > ROLE_SimulatedProxy) + { + GetCharacterMovement()->SetMovementMode(GetCharacterMovement()->GetGroundMovementMode()); + } + + OnStopVaultAbility(); +} + +void AMyVICharacterBase::OnRep_MotionMatch() +{ + // Simulated proxies update their sync points here, sent from the server during GA_Vault + MotionWarpingComponent->AddOrUpdateWarpTargetFromLocationAndRotation(TEXT("VaultSyncPoint"), RepMotionMatch.Location, RepMotionMatch.Direction.Rotation()); +} + +bool AMyVICharacterBase::IsVaulting() const +{ + // Simulated proxies use the value provided by server + if (GetLocalRole() == ROLE_SimulatedProxy) + { + return bRepIsVaulting; + } + + // Local and authority uses gameplay tags for a predicted result + if (VaultComponent) + { + return VaultComponent->IsVaulting(); + } + + return false; +} + +// *********************************************** // +// ******** Begin Pawn Vaulting Interface ******** // +// *********************************************** // + +UVIPawnVaultComponent* AMyVICharacterBase::GetPawnVaultComponent_Implementation() const +{ + // You need to override this + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("AVICharacterBase::GetPawnVaultComponent not implemented for { %s }. Cannot Vault."), *GetName())); + return nullptr; +} + +UMotionWarpingComponent* AMyVICharacterBase::GetMotionWarpingComponent_Implementation() const +{ + // You need to override this + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("AVICharacterBase::GetMotionWarpingComponent not implemented for { %s }. Cannot Vault."), *GetName())); + return nullptr; +} + +FVIAnimSet AMyVICharacterBase::GetVaultAnimSet_Implementation() const +{ + // You need to override this + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("AVICharacterBase::GetVaultAnimSet not implemented for { %s }. Cannot Vault."), *GetName())); + return FVIAnimSet(); +} + +FVITraceSettings AMyVICharacterBase::GetVaultTraceSettings_Implementation() const +{ + // You need to override this + UVIBlueprintFunctionLibrary::MessageLogError(FString::Printf(TEXT("AVICharacterBase::GetVaultTraceSettings not implemented for { %s }. Using default trace settings."), *GetName()), false); + return FVITraceSettings(); +} + +FVector AMyVICharacterBase::GetVaultDirection_Implementation() const +{ + // Use input vector if available + if (GetCharacterMovement() && !GetCharacterMovement()->GetCurrentAcceleration().IsNearlyZero()) + { + return GetCharacterMovement()->GetCurrentAcceleration(); + } + + // Use character facing direction if not providing input + return GetActorForwardVector(); +} + +bool AMyVICharacterBase::CanVault_Implementation() const +{ + // Vaulting must finish before starting another vault attempt + if (IsVaulting()) + { + return false; + } + + // Invalid components + if (!VaultComponent || !GetCharacterMovement()) + { + return false; + } + + // Animation instance is required to play vault montage + if (!GetMesh() || !GetMesh()->GetAnimInstance()) + { + return false; + } + + // Authority not initialized (this isn't set on clients) + if (HasAuthority() && !VaultComponent->bVaultAbilityInitialized) + { + return false; + } + + // Exit if character is in a state they cannot vault from + if (GetCharacterMovement()->IsMovingOnGround() || GetCharacterMovement()->IsFalling() || GetCharacterMovement()->IsSwimming()) + { + if (GetCharacterMovement()->IsMovingOnGround() && !VaultComponent->bCanVaultFromGround) + { + return false; + } + + if (GetCharacterMovement()->IsFalling() && !VaultComponent->bCanVaultFromFalling) + { + return false; + } + + if (GetCharacterMovement()->IsSwimming() && !VaultComponent->bCanVaultFromSwimming) + { + return false; + } + } + else + { + return false; + } + + // Can't vault while crouching + if (!VaultComponent->bCanVaultFromCrouching && GetCharacterMovement()->IsCrouching()) + { + return false; + } + + // Passed all conditions + return true; +} + +void AMyVICharacterBase::OnLocalPlayerVault_Implementation(const FVector& Location, const FVector& Direction) +{ + // LocalPlayer just stores the data in the same place for convenience, ease of use, memory reduction, etc + RepMotionMatch = FVIRepMotionMatch(Location, Direction); +} + +void AMyVICharacterBase::GetVaultLocationAndDirection_Implementation(FVector& OutLocation, FVector& OutDirection) const +{ + // Because LocalPlayer stores in the same place, no need for any testing as they all use RepMotionMatch to store this + + // This is only currently used for FBIK tracing + OutLocation = RepMotionMatch.Location; + OutDirection = RepMotionMatch.Direction; +} + +void AMyVICharacterBase::ReplicateMotionMatch_Implementation(const FVIRepMotionMatch& MotionMatch) +{ + // GA_Vault has directed server to update it's RepMotionMatch property so that it will + // be replicated to simulated proxies with 1 decimal point of precision (net quantization) + RepMotionMatch = MotionMatch; +} + +bool AMyVICharacterBase::IsWalkable_Implementation(const FHitResult& HitResult) const +{ + // Surface we hit can be walked on or not + return GetCharacterMovement() && GetCharacterMovement()->IsWalkable(HitResult); +} + +bool AMyVICharacterBase::CanAutoVaultInCustomMovementMode_Implementation() const +{ + return true; + + // Example usage commented out + + /* + + if (GetCharacterMovement()) + { + switch (GetCharacterMovement()->CustomMovementMode) + { + case 0: + return false; + case 1: // Some example custom mode where auto vault can work + return true; + case 2: + return false; + default: + return true; + } + } + + */ +} + +// *********************************************** // +// ********* End Pawn Vaulting Interface ********* // +// *********************************************** // \ No newline at end of file diff --git a/EndlessVendetta/Source/EndlessVendetta/MyVICharacterBase.h b/EndlessVendetta/Source/EndlessVendetta/MyVICharacterBase.h new file mode 100644 index 00000000..8219c451 --- /dev/null +++ b/EndlessVendetta/Source/EndlessVendetta/MyVICharacterBase.h @@ -0,0 +1,119 @@ +// Copyright (c) 2019-2022 Drowning Dragons Limited. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "EndlessVendettaCharacter.h" + +#include "VITypes.h" +#include "Pawn/VIPawnInterface.h" +#include "MyVICharacterBase.generated.h" + +class UMotionWarpingComponent; +class UVIPawnVaultComponent; + +/** + * An incomplete character base class + * Needs to inherit from IAbilitySystemInterface and implement a UVIAbilitySystemComponent + * @see: AVICharacterAbilityBase where this is done for you + * + * Requires multiple overrides which will cause errors if not correctly performed + */ +UCLASS(abstract) +class ENDLESSVENDETTA_API AMyVICharacterBase : public AEndlessVendettaCharacter, public IVIPawnInterface +{ + GENERATED_BODY() + +public: + /** + * Motion Warping Component used for vaulting + */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Character) + UMotionWarpingComponent* MotionWarpingComponent; + + /** + * Pawn Vault Component used for core vaulting logic + * + * This is added in Blueprint and must be returned via + * the IVIPawnInterface::GetPawnVaultComponent function + */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Character) + UVIPawnVaultComponent* VaultComponent; + +protected: + /** Simulated proxies use this to update their vaulting state based on server values */ + UPROPERTY(Replicated, BlueprintReadWrite, Category = Vault) + bool bRepIsVaulting; + + /** Used to detect changes in vaulting state and call StopVaultAbility() */ + UPROPERTY() + bool bWasVaulting; + + /** + * Simulated proxies use this to reproduce motion matching results provided + * by server in the GA_Vault gameplay ability + * + * Local players use this as a cache for FBIK testing (returned via GetVaultLocationAndDirection) + * + * Net Serialized to one decimal point of precision + */ + UPROPERTY(ReplicatedUsing="OnRep_MotionMatch", BlueprintReadWrite, Category = Vault) + FVIRepMotionMatch RepMotionMatch; + +public: + virtual void BeginPlay() override; + + virtual void CheckJumpInput(float DeltaTime) override; + + virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; + +public: + virtual void Jump() override; + virtual void StopJumping() override; + + /** Called from gameplay ability when vault stops */ + UFUNCTION(BlueprintCallable, Category = Vault) + void StopVaultAbility(); + + UFUNCTION(BlueprintImplementableEvent, Category = Vault) + void OnStopVaultAbility(); + +protected: + UFUNCTION() + void OnRep_MotionMatch(); + +public: + /** + * @return True if vaulting + * Correct value must be returned based on net role here + * Simulated proxies return bRepIsVaulting + * Server & Authority must return CMC bIsVaulting + */ + UFUNCTION(BlueprintPure, Category = Vault) + virtual bool IsVaulting() const; + + // *********************************************** // + // *********** Begin IVIPawnInterface ************ // + // *********************************************** // + + // Read VIPawnInterface.h for detailed descriptions of these functions or look + // inside their functions themselves + + virtual UVIPawnVaultComponent* GetPawnVaultComponent_Implementation() const override; + virtual UMotionWarpingComponent* GetMotionWarpingComponent_Implementation() const override; + virtual USkeletalMeshComponent* GetMeshForVaultMontage_Implementation() const override { return GetMesh(); } + virtual FVIAnimSet GetVaultAnimSet_Implementation() const override; + virtual FVITraceSettings GetVaultTraceSettings_Implementation() const override; + virtual FVector GetVaultDirection_Implementation() const override; + virtual bool CanVault_Implementation() const override; + virtual void StartVaultAbility_Implementation() override; + virtual void OnLocalPlayerVault_Implementation(const FVector& Location, const FVector& Direction) override; + virtual void GetVaultLocationAndDirection_Implementation(FVector& OutLocation, FVector& OutDirection) const override; + virtual void ReplicateMotionMatch_Implementation(const FVIRepMotionMatch& MotionMatch) override; + virtual bool IsWalkable_Implementation(const FHitResult& HitResult) const override; + virtual bool CanAutoVaultInCustomMovementMode_Implementation() const override; + + // *********************************************** // + // ************* End IVIPawnInterface ************ // + // *********************************************** // +};