// Fill out your copyright notice in the Description page of Project Settings. #include "ShooterAnimInstance.h" #include "ShooterCharacter.h" #include "GameFramework/CharacterMovementComponent.h" #include "Kismet/KismetMathLibrary.h" #include "Weapon.h" // For GetEquippedWeapon() #include "WeaponType.h" // For GetWeaponType() UShooterAnimInstance::UShooterAnimInstance() : Speed(0.f), bIsInAir(false), bIsAccelerating(false), MovementOffsetYaw(0.f), LastMovementOffsetYaw(0.f), bAiming(false), TIPCharacterYaw(0.f), TIPCharacterYawLastFrame(0.f), RootYawOffset(0.f), Pitch(0.f), bReloading(false), OffsetState(EOffsetState::EOS_Hip), CharacterRotation(FRotator(0.f)), CharacterRotationLastFrame(FRotator(0.f)), RecoilWeight(1.f), bTurningInPlace(false), EquippedWeaponType(EWeaponType::EWT_MAX), bShouldUseFABRIK(false) { } // We call UpdateAnimationProperties every frame (on Tick) in ShooterAnimBP void UShooterAnimInstance::UpdateAnimationProperties(float DeltaTime) { // If at any given frame, the ShooterCharacter is a nullptr, we try to assign it again if (ShooterCharacter == nullptr) { ShooterCharacter = Cast(TryGetPawnOwner()); } // If the Cast is successful if (ShooterCharacter) { bCrouching = ShooterCharacter->GetCrouching(); bReloading = ShooterCharacter->GetCombatState() == ECombatState::ECS_Reloading; bEquipping = ShooterCharacter->GetCombatState() == ECombatState::ECS_Equipping; bShouldUseFABRIK = ShooterCharacter->GetCombatState() == ECombatState::ECS_Unoccupied || ShooterCharacter->GetCombatState() == ECombatState::ECS_FireTimerInProgress; //Get the speed of the character from velocity FVector Velocity{ ShooterCharacter->GetVelocity() }; Velocity.Z = 0; //since we only want lateral velocity Speed = Velocity.Size(); // Magnitude of Velocity // Is the character in the air? bIsInAir = ShooterCharacter->GetCharacterMovement()->IsFalling(); // Is the character accelerating? if (ShooterCharacter->GetCharacterMovement()->GetCurrentAcceleration().Size() > 0.f) { bIsAccelerating = true; } else { bIsAccelerating = false; } FRotator AimRotation = ShooterCharacter->GetBaseAimRotation(); FString RotationMessage = FString::Printf(TEXT("Base Aim Rotation: %f"), AimRotation.Yaw); FRotator MovementRotation = UKismetMathLibrary::MakeRotFromX(ShooterCharacter->GetVelocity()); FString MovementRotationMessage = FString::Printf(TEXT("Movement Rotation Yaw: %f"), MovementRotation.Yaw); MovementOffsetYaw = UKismetMathLibrary::NormalizedDeltaRotator(MovementRotation, AimRotation).Yaw; FString OffsetMessage = FString::Printf(TEXT("Movement Offset Yaw: %f"), MovementOffsetYaw); if (ShooterCharacter->GetVelocity().Size() > 0.f) { LastMovementOffsetYaw = MovementOffsetYaw; //ShooterCharacter->GetCharacterMovement()->bOrientRotationToMovement = false; //changes } bAiming = ShooterCharacter->GetAiming(); if (bReloading) { OffsetState = EOffsetState::EOS_Reloading; } else if (bIsInAir) { OffsetState = EOffsetState::EOS_InAir; } else if (bAiming) { OffsetState = EOffsetState::EOS_Aiming; } else { OffsetState = EOffsetState::EOS_Hip; } // Check if ShooterCharacter has a valid EquippedWeapon if (ShooterCharacter->GetEquippedWeapon()) { EquippedWeaponType = ShooterCharacter->GetEquippedWeapon()->GetWeaponType(); } if (GEngine) { GEngine->AddOnScreenDebugMessage(1, 0.f, FColor::White, RotationMessage); GEngine->AddOnScreenDebugMessage(2, 0.f, FColor::White, MovementRotationMessage); GEngine->AddOnScreenDebugMessage(3, 0.f, FColor::White, OffsetMessage); } } TurnInPlace(); Lean(DeltaTime); } void UShooterAnimInstance::NativeInitializeAnimation() { // Remember, ShooterCharacter is the pointer we defined in the .h file ShooterCharacter = Cast(TryGetPawnOwner()); // We get the Pawn and Cast it into a AShooterCharacter (Pawn to Character) } void UShooterAnimInstance::TurnInPlace() { if (ShooterCharacter == nullptr) return; Pitch = ShooterCharacter->GetBaseAimRotation().Pitch; if (Speed > 0 || bIsInAir) { // Don't want turn in place, since Character is moving RootYawOffset = 0.f; TIPCharacterYaw = ShooterCharacter->GetActorRotation().Yaw; TIPCharacterYawLastFrame = TIPCharacterYaw; RotationCurveLastFrame = 0.f; RotationCurve = 0.f; } else { TIPCharacterYawLastFrame = TIPCharacterYaw; TIPCharacterYaw = ShooterCharacter->GetActorRotation().Yaw; const float TIPYawDelta{ TIPCharacterYaw - TIPCharacterYawLastFrame }; // Root Yaw Offset, updated and clamped to [-180,180] RootYawOffset = UKismetMathLibrary::NormalizeAxis(RootYawOffset - TIPYawDelta); if (GEngine) { GEngine->AddOnScreenDebugMessage(4, -1, FColor::Blue, FString::Printf(TEXT("Character Yaw: %f"), TIPCharacterYaw)); GEngine->AddOnScreenDebugMessage(5, -1, FColor::Red, FString::Printf(TEXT("RootYawOffset: %f"), RootYawOffset)); } const float Turning{ GetCurveValue(TEXT("Turning")) }; // Get Curve Value of the Turning Metadata Curve // 1.0 if turning (Animation is actively playing) , 0.0 if not (Animation is not playing) if (Turning > 0) { bTurningInPlace = true; RotationCurveLastFrame = RotationCurve; RotationCurve = GetCurveValue(TEXT("Rotation")); const float DeltaRotation{ RotationCurve - RotationCurveLastFrame }; // if RootYawOffset > 0, then we're Turning Left, if < 0, we're Turning Right RootYawOffset > 0 ? RootYawOffset -= DeltaRotation : RootYawOffset += DeltaRotation; const float ABSRootYawOffset{ FMath::Abs(RootYawOffset) }; if (ABSRootYawOffset > 90.f) { const float YawExcess{ ABSRootYawOffset - 90.f }; RootYawOffset > 0 ? RootYawOffset -= YawExcess : RootYawOffset += YawExcess; } } else { bTurningInPlace = false; } } // Set the Recoil Weight if (bTurningInPlace) // If Turning in Place { if (bReloading || bEquipping) // And Reloading or Equipping { RecoilWeight = 1.f; } else // And not Reloading { RecoilWeight = 0.f; } } else // if not turning in place { if (bCrouching) // And Crouching { if (bReloading || bEquipping) // And Reloading or Equipping { RecoilWeight = 1.f; } else // And not Reloading { RecoilWeight = 0.1f; } } else // And not Crouching { if (bAiming || bReloading || bEquipping) // And (Aiming or Reloading or Equipping) { RecoilWeight = 1.f; } else // And not Aiming { RecoilWeight = 0.5f; } } } } void UShooterAnimInstance::Lean(float DeltaTime) // We're using DeltaTime since we want the Leaning to be smooth { if (ShooterCharacter == nullptr) return; CharacterRotationLastFrame = CharacterRotation; CharacterRotation = ShooterCharacter->GetActorRotation(); const FRotator Delta{ UKismetMathLibrary::NormalizedDeltaRotator(CharacterRotation, CharacterRotationLastFrame) }; const float Target{ (Delta.Yaw) / DeltaTime }; //const float Interp{ FMath::FInterpTo(YawDelta, Target, DeltaTime, 6.f) }; const float Interp{ FMath::FInterpTo(YawDelta, Target, DeltaTime, 1.f) }; YawDelta = FMath::Clamp(Interp, -90.f, 90.f); //YawDelta = FMath::Clamp(Interp, -85.f, 85.f); if (GEngine) { GEngine->AddOnScreenDebugMessage(6, -1, FColor::Cyan, FString::Printf(TEXT("Yaw Delta: %f"), YawDelta)); GEngine->AddOnScreenDebugMessage(7, -1, FColor::Orange, FString::Printf(TEXT("Delta.Yaw: % f"), Delta.Yaw)); GEngine->AddOnScreenDebugMessage(8, -1, FColor::Orange, FString::Printf(TEXT("Crouching: % d"), bCrouching)); } }