Skip to content

Commit

Permalink
#30 Added CameraLagVector
Browse files Browse the repository at this point in the history
  • Loading branch information
xthebat committed Oct 31, 2023
1 parent cc4a6e3 commit 040361a
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 42 deletions.
Binary file modified Content/Blueprints/Character/BP_Cloud9Character.uasset
Binary file not shown.
2 changes: 0 additions & 2 deletions Source/Cloud9/Character/Cloud9Character.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
Expand Down
150 changes: 131 additions & 19 deletions Source/Cloud9/Character/Components/Cloud9SpringArmComponent.cpp
Original file line number Diff line number Diff line change
@@ -1,35 +1,147 @@
#include "Cloud9SpringArmComponent.h"

#include "Cloud9/Tools/Cloud9ToolsLibrary.h"


UCloud9SpringArmComponent::UCloud9SpringArmComponent()
{
RotationVerticalLagSpeed = 0.0f;
CameraLagVector = FVector::OneVector;
}

void UCloud9SpringArmComponent::OnRegister()
void UCloud9SpringArmComponent::UpdateDesiredArmLocation(
bool bDoTrace,
bool bDoLocationLag,
bool bDoRotationLag,
float DeltaTime)
{
Super::OnRegister();
TargetRelativeRotation = RelativeSocketRotation.Rotator();
}
FRotator DesiredRot = GetTargetRotation();

void UCloud9SpringArmComponent::TickComponent(
float DeltaTime,
ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// Apply 'lag' to rotation if desired
if (bDoRotationLag)
{
if (bUseCameraLagSubstepping && DeltaTime > CameraLagMaxTimeStep && CameraRotationLagSpeed > 0.f)
{
const FRotator ArmRotStep = (DesiredRot - PreviousDesiredRot).GetNormalized() * (1.f / DeltaTime);
FRotator LerpTarget = PreviousDesiredRot;
float RemainingTime = DeltaTime;
while (RemainingTime > KINDA_SMALL_NUMBER)
{
const float LerpAmount = FMath::Min(CameraLagMaxTimeStep, RemainingTime);
LerpTarget += ArmRotStep * LerpAmount;
RemainingTime -= LerpAmount;

DesiredRot = FRotator(
FMath::QInterpTo(
FQuat(PreviousDesiredRot),
FQuat(LerpTarget),
LerpAmount,
CameraRotationLagSpeed)
);

PreviousDesiredRot = DesiredRot;
}
}
else
{
DesiredRot = FRotator(
FMath::QInterpTo(
FQuat(PreviousDesiredRot),
FQuat(DesiredRot),
DeltaTime,
CameraRotationLagSpeed
)
);
}
}
PreviousDesiredRot = DesiredRot;

auto NewRotation = TargetRelativeRotation;
if (RotationVerticalLagSpeed != 0.0f)
// Get the spring arm 'origin', the target we want to look at
FVector ArmOrigin = GetComponentLocation() + TargetOffset;
// We lag the target, not the actual camera position, so rotating the camera around does not have lag
FVector DesiredLoc = ArmOrigin;
if (bDoLocationLag)
{
const auto OldRotation = RelativeSocketRotation.Rotator();
NewRotation = FMath::Lerp(
OldRotation,
TargetRelativeRotation,
DeltaTime / RotationVerticalLagSpeed * RotationScale);
const auto LocationLag = CameraLagSpeed * CameraLagVector;

if (bUseCameraLagSubstepping
&& DeltaTime > CameraLagMaxTimeStep
&& LocationLag != FVector::ZeroVector)
{
const FVector ArmMovementStep = (DesiredLoc - PreviousDesiredLoc) * (1.f / DeltaTime);
auto LerpTarget = PreviousDesiredLoc;

float RemainingTime = DeltaTime;
while (RemainingTime > KINDA_SMALL_NUMBER)
{
const float LerpAmount = FMath::Min(CameraLagMaxTimeStep, RemainingTime);
LerpTarget += ArmMovementStep * LerpAmount;
RemainingTime -= LerpAmount;

DesiredLoc = UCloud9ToolsLibrary::VInterpTo(PreviousDesiredLoc, LerpTarget, LerpAmount, LocationLag);
PreviousDesiredLoc = DesiredLoc;
}
}
else
{
DesiredLoc = UCloud9ToolsLibrary::VInterpTo(PreviousDesiredLoc, DesiredLoc, DeltaTime, LocationLag);
}

if (const auto FromOrigin = DesiredLoc - ArmOrigin;
CameraLagMaxDistance > 0.f && FromOrigin.SizeSquared() > FMath::Square(CameraLagMaxDistance))
{
DesiredLoc = ArmOrigin + FromOrigin.GetClampedToMaxSize(CameraLagMaxDistance);
}
}

RelativeSocketRotation = NewRotation.Quaternion();
PreviousArmOrigin = ArmOrigin;
PreviousDesiredLoc = DesiredLoc;

// Now offset camera position back along our rotation
DesiredLoc -= DesiredRot.Vector() * TargetArmLength;
// Add socket offset in local space
DesiredLoc += FRotationMatrix(DesiredRot).TransformVector(SocketOffset);

// Do a sweep to ensure we are not penetrating the world
FVector ResultLoc;
if (bDoTrace && TargetArmLength != 0.0f)
{
bIsCameraFixed = true;
FCollisionQueryParams QueryParams(SCENE_QUERY_STAT(SpringArm), false, GetOwner());

FHitResult Result;
GetWorld()->SweepSingleByChannel(
Result,
ArmOrigin,
DesiredLoc,
FQuat::Identity,
ProbeChannel,
FCollisionShape::MakeSphere(ProbeSize),
QueryParams);

UnfixedCameraPosition = DesiredLoc;

ResultLoc = BlendLocations(DesiredLoc, Result.Location, Result.bBlockingHit, DeltaTime);

if (ResultLoc == DesiredLoc)
{
bIsCameraFixed = false;
}
}
else
{
ResultLoc = DesiredLoc;
bIsCameraFixed = false;
UnfixedCameraPosition = ResultLoc;
}

// Form a transform for new world transform for camera
FTransform WorldCamTM(DesiredRot, ResultLoc);
// Convert to relative to component
FTransform RelCamTM = WorldCamTM.GetRelativeTransform(GetComponentTransform());

// Update socket location/rotation
RelativeSocketLocation = RelCamTM.GetLocation();
RelativeSocketRotation = RelCamTM.GetRotation();

UpdateChildTransforms();
}
38 changes: 17 additions & 21 deletions Source/Cloud9/Character/Components/Cloud9SpringArmComponent.h
Original file line number Diff line number Diff line change
@@ -1,36 +1,32 @@
#pragma once

#include "CoreMinimal.h"
#include "Cloud9CharacterComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "Cloud9SpringArmComponent.generated.h"


UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class CLOUD9_API UCloud9SpringArmComponent
: public USpringArmComponent
, public ICloud9CharacterComponent
UCLASS(ClassGroup=(Camera), meta=(BlueprintSpawnableComponent))
class CLOUD9_API UCloud9SpringArmComponent : public USpringArmComponent
{
GENERATED_BODY()

public:
UCloud9SpringArmComponent();

virtual void TickComponent(
float DeltaTime,
ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction
) override;

virtual void OnRegister() override;
// TODO: CameraLagRotator

private:
static constexpr float RotationScale = 360.0f;
/**
* If bEnableCameraLag is true, controls how quickly camera reaches target position by each axis.
* Values of this vector multiplies with @CameraLagSpeed.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Lag, meta=(editcondition="bEnableCameraLag"))
FVector CameraLagVector;

/** Vertical rotation speed lag for camera*/
UPROPERTY(EditDefaultsOnly, Category=Lag,
meta=(ClampMin="0.0", ClampMax="10000.0", UIMin = "0.0", UIMax = "10000.0"))
float RotationVerticalLagSpeed;
public:
UCloud9SpringArmComponent();

FRotator TargetRelativeRotation;
virtual void UpdateDesiredArmLocation(
bool bDoTrace,
bool bDoLocationLag,
bool bDoRotationLag,
float DeltaTime
) override;
};
29 changes: 29 additions & 0 deletions Source/Cloud9/Tools/Cloud9ToolsLibrary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,32 @@ FRotator UCloud9ToolsLibrary::RadiansToDegrees(const FRotator Rotator)
FMath::RadiansToDegrees(Rotator.Roll)
};
}

FVector UCloud9ToolsLibrary::VInterpTo(
const FVector Current,
const FVector Target,
float DeltaTime,
const FVector InterpSpeed)
{
const auto ClampLerp = [](auto Current, auto Dist, auto Alpha, auto Target)
{
return Alpha <= 0.0f ? Target : Current + Dist * FMath::Clamp(Alpha, 0.0f, 1.0f);
};

// Distance to reach
const auto Dist = Target - Current;

// If distance is too small, just set the desired location
if (Dist.SizeSquared() < KINDA_SMALL_NUMBER)
{
return Target;
}

const auto Alpha = DeltaTime * InterpSpeed;

return {
ClampLerp(Current.X, Dist.X, Alpha.X, Target.X),
ClampLerp(Current.Y, Dist.Y, Alpha.Y, Target.Y),
ClampLerp(Current.Z, Dist.Z, Alpha.Z, Target.Z),
};
}
6 changes: 6 additions & 0 deletions Source/Cloud9/Tools/Cloud9ToolsLibrary.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,10 @@ class CLOUD9_API UCloud9ToolsLibrary : public UBlueprintFunctionLibrary
static void GetWidthHeightDepth(const FBox& Box, float& Width, float& Height, float& Depth);

static FRotator RadiansToDegrees(const FRotator Rotator);

static FVector VInterpTo(
const FVector Current,
const FVector Target,
float DeltaTime,
const FVector InterpSpeed);
};

0 comments on commit 040361a

Please sign in to comment.