diff --git a/Content/Blueprints/Character/Animation/BlasterAnimBP.uasset b/Content/Blueprints/Character/Animation/BlasterAnimBP.uasset index a298c6a..87f8d0b 100644 Binary files a/Content/Blueprints/Character/Animation/BlasterAnimBP.uasset and b/Content/Blueprints/Character/Animation/BlasterAnimBP.uasset differ diff --git a/Source/Blaster/Character/BlasterAnimInstance.cpp b/Source/Blaster/Character/BlasterAnimInstance.cpp index 5e75a17..d7971a5 100644 --- a/Source/Blaster/Character/BlasterAnimInstance.cpp +++ b/Source/Blaster/Character/BlasterAnimInstance.cpp @@ -37,7 +37,7 @@ void UBlasterAnimInstance::NativeUpdateAnimation(float DeltaSeconds) bIsCrouched = BlasterCharacter->bIsCrouched; bAiming = BlasterCharacter->IsAiming(); TurningInPlace = BlasterCharacter->GetTurningInPlace(); - + bRotateRootBone = BlasterCharacter->ShouldRotateRootBone(); // Offset Yaw for Strafing FRotator AimRotation = BlasterCharacter->GetBaseAimRotation(); FRotator MovementRotation = UKismetMathLibrary::MakeRotFromX(BlasterCharacter->GetVelocity()); diff --git a/Source/Blaster/Character/BlasterAnimInstance.h b/Source/Blaster/Character/BlasterAnimInstance.h index d22e717..8245809 100644 --- a/Source/Blaster/Character/BlasterAnimInstance.h +++ b/Source/Blaster/Character/BlasterAnimInstance.h @@ -72,4 +72,7 @@ private: UPROPERTY(BlueprintReadOnly, Category="Movement", meta=(AllowPrivateAccess = "true")) bool bLocallyControlled; + + UPROPERTY(BlueprintReadOnly, Category="Movement", meta=(AllowPrivateAccess = "true")) + bool bRotateRootBone; }; diff --git a/Source/Blaster/Character/BlasterCharacter.cpp b/Source/Blaster/Character/BlasterCharacter.cpp index 3fcb516..f38f7c0 100644 --- a/Source/Blaster/Character/BlasterCharacter.cpp +++ b/Source/Blaster/Character/BlasterCharacter.cpp @@ -55,6 +55,14 @@ void ABlasterCharacter::GetLifetimeReplicatedProps(TArray& Ou DOREPLIFETIME_CONDITION(ABlasterCharacter, OverlappingWeapon, COND_OwnerOnly); } +void ABlasterCharacter::OnRep_ReplicatedMovement() +{ + Super::OnRep_ReplicatedMovement(); + + SimProxiesTurn(); + TimeSinceLastMovementReplication = 0.f; +} + void ABlasterCharacter::PostInitializeComponents() { Super::PostInitializeComponents(); @@ -73,7 +81,7 @@ void ABlasterCharacter::PlayFireMontage(bool bAiming) if (AnimInstance && FireWeaponMontage) { AnimInstance->Montage_Play(FireWeaponMontage); - FName SectionName = bAiming ? FName("RifleADS") : FName("RifleHip"); + const FName SectionName = bAiming ? FName("RifleADS") : FName("RifleHip"); AnimInstance->Montage_JumpToSection(SectionName); } } @@ -100,7 +108,20 @@ void ABlasterCharacter::Tick(float DeltaTime) { Super::Tick(DeltaTime); - AimOffset(DeltaTime); + if (GetLocalRole() > ROLE_SimulatedProxy && IsLocallyControlled()) + { + AimOffset(DeltaTime); + } + else + { + TimeSinceLastMovementReplication += DeltaTime; + if (TimeSinceLastMovementReplication > 0.25f) + { + OnRep_ReplicatedMovement(); + } + CalculateAO_Pitch(); + } + HideCameraIfCharacterClose(); } @@ -201,15 +222,14 @@ void ABlasterCharacter::AimOffset(float DeltaTime) { if (Combat && Combat->EquippedWeapon == nullptr) return; - FVector Velocity = GetVelocity(); - Velocity.Z = 0.f; - float Speed = Velocity.Size(); - bool bIsInAir = GetCharacterMovement()->IsFalling(); + const float Speed = CalculateSpeed(); + const bool bIsInAir = GetCharacterMovement()->IsFalling(); if (Speed == 0.f && !bIsInAir) // Standing still, not jumping { - FRotator CurrentAimRotation = FRotator(0.f, GetBaseAimRotation().Yaw, 0.f); - FRotator DeltaAimRotation = UKismetMathLibrary::NormalizedDeltaRotator(CurrentAimRotation, StartingAimRotation); + bRotateRootBone = true; + const FRotator CurrentAimRotation = FRotator(0.f, GetBaseAimRotation().Yaw, 0.f); + const FRotator DeltaAimRotation = UKismetMathLibrary::NormalizedDeltaRotator(CurrentAimRotation, StartingAimRotation); AO_Yaw = DeltaAimRotation.Yaw; if (TurningInPlace == ETurningInPlace::ETIP_NotTurning) { @@ -220,24 +240,67 @@ void ABlasterCharacter::AimOffset(float DeltaTime) } if (Speed > 0.f || bIsInAir) // Running or jumping { + bRotateRootBone = false; StartingAimRotation = FRotator(0.f, GetBaseAimRotation().Yaw, 0.f); AO_Yaw = 0.f; bUseControllerRotationYaw = true; TurningInPlace = ETurningInPlace::ETIP_NotTurning; } + CalculateAO_Pitch(); +} + +void ABlasterCharacter::CalculateAO_Pitch() +{ AO_Pitch = GetBaseAimRotation().Pitch; // Fix pitch/yaw compression if (AO_Pitch > 90.f && !IsLocallyControlled()) { // map pitch from [270, 360) to [-90, 0) - FVector2d InRange(270.f, 360.f); - FVector2d OutRange(-90.f, 0.f); + const FVector2d InRange(270.f, 360.f); + const FVector2d OutRange(-90.f, 0.f); AO_Pitch = FMath::GetMappedRangeValueClamped(InRange, OutRange, AO_Pitch); } } +void ABlasterCharacter::SimProxiesTurn() +{ + if (Combat == nullptr || Combat->EquippedWeapon == nullptr) return; + + bRotateRootBone = false; + + const float Speed = CalculateSpeed(); + if (Speed > 0.f) + { + TurningInPlace = ETurningInPlace::ETIP_NotTurning; + return; + } + + ProxyRotationLastFrame = ProxyRotation; + ProxyRotation = GetActorRotation(); + ProxyYaw = UKismetMathLibrary::NormalizedDeltaRotator(ProxyRotation, ProxyRotationLastFrame).Yaw; + + if (FMath::Abs(ProxyYaw) > TurnThreshold) + { + if (ProxyYaw > TurnThreshold) + { + TurningInPlace = ETurningInPlace::ETIP_Right; + } + else if (ProxyYaw < -TurnThreshold) + { + TurningInPlace = ETurningInPlace::ETIP_Left; + } + else + { + TurningInPlace = ETurningInPlace::ETIP_NotTurning; + } + return; + } + + TurningInPlace = ETurningInPlace::ETIP_NotTurning; +} + void ABlasterCharacter::Jump() { if (bIsCrouched) @@ -273,7 +336,6 @@ void ABlasterCharacter::ServerEquipButtonPressed_Implementation() void ABlasterCharacter::TurnInPlace(float DeltaTime) { - // UE_LOG(LogTemp, Warning, TEXT("AO_Yaw: %f"), AO_Yaw); if (AO_Yaw > 90.f) { TurningInPlace = ETurningInPlace::ETIP_Right; @@ -314,14 +376,15 @@ void ABlasterCharacter::MulticastHit_Implementation() void ABlasterCharacter::HideCameraIfCharacterClose() { if (!IsLocallyControlled()) return; - if((FollowCamera->GetComponentLocation() - GetActorLocation()).Size() < CameraThreshold) + if ((FollowCamera->GetComponentLocation() - GetActorLocation()).Size() < CameraThreshold) { GetMesh()->SetVisibility(false); if (Combat && Combat->EquippedWeapon && Combat->EquippedWeapon->GetWeaponMesh()) { Combat->EquippedWeapon->GetWeaponMesh()->bOwnerNoSee = true; } - } else + } + else { GetMesh()->SetVisibility(true); if (Combat && Combat->EquippedWeapon && Combat->EquippedWeapon->GetWeaponMesh()) @@ -331,6 +394,13 @@ void ABlasterCharacter::HideCameraIfCharacterClose() } } +float ABlasterCharacter::CalculateSpeed() +{ + FVector Velocity = GetVelocity(); + Velocity.Z = 0.f; + return Velocity.Size(); +} + void ABlasterCharacter::SetOverlappingWeapon(AWeapon* Weapon) { if (OverlappingWeapon) diff --git a/Source/Blaster/Character/BlasterCharacter.h b/Source/Blaster/Character/BlasterCharacter.h index 6bd4eb9..df9fdc4 100644 --- a/Source/Blaster/Character/BlasterCharacter.h +++ b/Source/Blaster/Character/BlasterCharacter.h @@ -19,6 +19,7 @@ public: virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; virtual void PostInitializeComponents() override; + virtual void OnRep_ReplicatedMovement() override; void PlayFireMontage(bool bAiming); UFUNCTION(NetMulticast, Unreliable) @@ -35,7 +36,9 @@ protected: void CrouchButtonPressed(); void AimButtonPressed(); void AimButtonReleased(); + void CalculateAO_Pitch(); void AimOffset(float DeltaTime); + void SimProxiesTurn(); virtual void Jump() override; void FireButtonPressed(); void FireButtonReleased(); @@ -81,6 +84,14 @@ private: UPROPERTY(EditAnywhere) float CameraThreshold = 200.f; + + bool bRotateRootBone; + float TurnThreshold = 0.8f; + FRotator ProxyRotationLastFrame; + FRotator ProxyRotation; + float ProxyYaw; + float TimeSinceLastMovementReplication; + float CalculateSpeed(); public: void SetOverlappingWeapon(AWeapon* Weapon); @@ -92,4 +103,5 @@ public: FORCEINLINE ETurningInPlace GetTurningInPlace() const { return TurningInPlace; } FVector GetHitTarget() const; FORCEINLINE UCameraComponent* GetFollowCamera() const { return FollowCamera; } + FORCEINLINE bool ShouldRotateRootBone() const { return bRotateRootBone; } };