Document character movement component code

This commit is contained in:
Maxime Maurin 2023-11-24 20:59:31 +01:00
parent 028e228c8f
commit aedd5ee35e
6 changed files with 79 additions and 45 deletions

Binary file not shown.

Binary file not shown.

View File

@ -7,6 +7,8 @@
#include "GameplayModes/Combat/PwnCombatPlatformerPath.h" #include "GameplayModes/Combat/PwnCombatPlatformerPath.h"
#include "Utils/EngineUtils.h" #include "Utils/EngineUtils.h"
constexpr float LineTraceDistance = 10000.0f;
UPwnCharacterMovementComponent::UPwnCharacterMovementComponent() { UPwnCharacterMovementComponent::UPwnCharacterMovementComponent() {
PrimaryComponentTick.bCanEverTick = true; PrimaryComponentTick.bCanEverTick = true;
} }
@ -45,19 +47,19 @@ bool UPwnCharacterMovementComponent::IsFalling() const {
return Super::IsFalling() || IsCustomMovementMode(SplineFalling); return Super::IsFalling() || IsCustomMovementMode(SplineFalling);
} }
void UPwnCharacterMovementComponent::OnMovementModeChanged(EMovementMode PreviousMovementMode, uint8 PreviousCustomMode) { void UPwnCharacterMovementComponent::OnMovementModeChanged(const EMovementMode PreviousMovementMode, const uint8 PreviousCustomMode) {
Super::OnMovementModeChanged(PreviousMovementMode, PreviousCustomMode); Super::OnMovementModeChanged(PreviousMovementMode, PreviousCustomMode);
// React to changes in the movement mode. // React to changes in the movement mode
if (MovementMode == MOVE_Custom && CustomMovementMode == SplineWalking) { if (MovementMode == MOVE_Custom && CustomMovementMode == SplineWalking) {
// make sure we update our new floor/base on initial entry of the walking physics // Make sure we update our new floor/base on initial entry of the walking physics
FindFloor(UpdatedComponent->GetComponentLocation(), CurrentFloor, false); FindFloor(UpdatedComponent->GetComponentLocation(), CurrentFloor, false);
AdjustFloorHeight(); AdjustFloorHeight();
SetBaseFromFloor(CurrentFloor); SetBaseFromFloor(CurrentFloor);
} }
} }
void UPwnCharacterMovementComponent::UpdateCharacterStateBeforeMovement(float DeltaSeconds) { void UPwnCharacterMovementComponent::UpdateCharacterStateBeforeMovement(const float DeltaSeconds) {
if (MovementMode == MOVE_Falling && IsFollowingSpline) { if (MovementMode == MOVE_Falling && IsFollowingSpline) {
SetMovementMode(MOVE_Custom, SplineFalling); SetMovementMode(MOVE_Custom, SplineFalling);
} else if (MovementMode == MOVE_Walking && IsFollowingSpline) { } else if (MovementMode == MOVE_Walking && IsFollowingSpline) {
@ -93,14 +95,14 @@ void UPwnCharacterMovementComponent::EnterSplineFollowMode() {
CombatPath = OutCombatPath; CombatPath = OutCombatPath;
DotDirection = CombatPath->Reversed ? -1.0f : 1.0f; DotDirection = CombatPath->Reversed ? -1.0f : 1.0f;
float ClosestInputKey = CombatPath->Spline->FindInputKeyClosestToWorldLocation(UpdatedComponent->GetComponentLocation()); const float ClosestInputKey = CombatPath->Spline->FindInputKeyClosestToWorldLocation(UpdatedComponent->GetComponentLocation());
FVector ClosestLocation = CombatPath->Spline->GetLocationAtSplineInputKey(ClosestInputKey, ESplineCoordinateSpace::World); FVector ClosestLocation = CombatPath->Spline->GetLocationAtSplineInputKey(ClosestInputKey, ESplineCoordinateSpace::World);
FHitResult Hit; FHitResult Hit;
if (GetWorld()->LineTraceSingleByChannel(Hit, ClosestLocation, ClosestLocation + FVector(0.0f, 0.0f, -10000.0f), if (GetWorld()->LineTraceSingleByChannel(Hit, ClosestLocation, ClosestLocation + FVector(0.0f, 0.0f, -LineTraceDistance),
ECC_Visibility, IGNORE_OWNER_PARAMS)) { ECC_Visibility, IGNORE_OWNER_PARAMS)) {
const float SplineKey = CombatPath->FlattenSpline->FindInputKeyClosestToWorldLocation(Hit.ImpactPoint); const float SplineKey = CombatPath->FlattenedSpline->FindInputKeyClosestToWorldLocation(Hit.ImpactPoint);
DistanceAlongSpline = CombatPath->FlattenSpline->GetDistanceAlongSplineAtSplineInputKey(SplineKey); DistanceAlongSpline = CombatPath->FlattenedSpline->GetDistanceAlongSplineAtSplineInputKey(SplineKey);
ClosestLocation.Z += CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(); ClosestLocation.Z += CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
UpdatedComponent->SetWorldLocation(ClosestLocation); UpdatedComponent->SetWorldLocation(ClosestLocation);
@ -117,8 +119,8 @@ bool UPwnCharacterMovementComponent::IsCustomMovementMode(const ECustomMovementM
return MovementMode == MOVE_Custom && CustomMovementMode == Mode; return MovementMode == MOVE_Custom && CustomMovementMode == Mode;
} }
void UPwnCharacterMovementComponent::RecomputeTangentAndAcceleration() { void UPwnCharacterMovementComponent::UpdateTangentAndAcceleration() {
Tangent2D = CombatPath->FlattenSpline->GetTangentAtDistanceAlongSpline(DistanceAlongSpline, ESplineCoordinateSpace::World).GetSafeNormal2D(); Tangent2D = CombatPath->FlattenedSpline->GetTangentAtDistanceAlongSpline(DistanceAlongSpline, ESplineCoordinateSpace::World).GetSafeNormal2D();
// Recalculate acceleration so the input is relative to the spline // Recalculate acceleration so the input is relative to the spline
Acceleration = Tangent2D * Acceleration.Size2D() * FMath::Sign(Acceleration.X) * DotDirection; Acceleration = Tangent2D * Acceleration.Size2D() * FMath::Sign(Acceleration.X) * DotDirection;
} }
@ -135,32 +137,31 @@ void UPwnCharacterMovementComponent::UpdatePawnVelocity(const float TimeTick) {
} }
VelocityDirection = FMath::Sign(VelocityDirection); // -1, 0 or 1 VelocityDirection = FMath::Sign(VelocityDirection); // -1, 0 or 1
float NewDistanceAlongSpline = DistanceAlongSpline + VelocityDirection * VelocitySize2D * TimeTick; DistanceAlongSpline = DistanceAlongSpline + VelocityDirection * VelocitySize2D * TimeTick;
NewDistanceAlongSpline = FMath::Clamp(NewDistanceAlongSpline, 0.0f, CombatPath->FlattenSpline->GetSplineLength()); DistanceAlongSpline = FMath::Clamp(DistanceAlongSpline, 0.0f, CombatPath->FlattenedSpline->GetSplineLength());
FVector TargetLocation = CombatPath->FlattenSpline->GetLocationAtDistanceAlongSpline(NewDistanceAlongSpline, ESplineCoordinateSpace::World); FVector TargetLocation = CombatPath->FlattenedSpline->GetLocationAtDistanceAlongSpline(DistanceAlongSpline, ESplineCoordinateSpace::World);
TargetLocation.Z = OldLocation.Z; TargetLocation.Z = OldLocation.Z;
const FVector Direction = (TargetLocation - OldLocation).GetSafeNormal2D(); const FVector Direction = (TargetLocation - OldLocation).GetSafeNormal2D();
Velocity = VelocitySize2D * Direction; Velocity = VelocitySize2D * Direction;
Velocity.Z = OldVelocity.Z; Velocity.Z = OldVelocity.Z;
DistanceAlongSpline = NewDistanceAlongSpline;
} }
void UPwnCharacterMovementComponent::UpdateDistanceAlongSplineAndLocation() { void UPwnCharacterMovementComponent::UpdateDistanceAlongSpline() {
// Maintain coherent distance along spline with location
FHitResult Hit; FHitResult Hit;
if (GetWorld()->LineTraceSingleByChannel(Hit, UpdatedComponent->GetComponentLocation(), if (GetWorld()->LineTraceSingleByChannel(Hit, UpdatedComponent->GetComponentLocation(),
UpdatedComponent->GetComponentLocation() + FVector(0.0f, 0.0f, -10000.0f), UpdatedComponent->GetComponentLocation() + FVector(0.0f, 0.0f, -LineTraceDistance),
ECC_Visibility, IGNORE_OWNER_PARAMS)) { ECC_Visibility, IGNORE_OWNER_PARAMS)) {
const FVector ImpactPoint = Hit.ImpactPoint;
FVector ImpactPoint = Hit.ImpactPoint; const float InputKey = CombatPath->FlattenedSpline->FindInputKeyClosestToWorldLocation(ImpactPoint);
const float InputKey = CombatPath->FlattenSpline->FindInputKeyClosestToWorldLocation(ImpactPoint); DistanceAlongSpline = CombatPath->FlattenedSpline->GetDistanceAlongSplineAtSplineInputKey(InputKey);
DistanceAlongSpline = CombatPath->FlattenSpline->GetDistanceAlongSplineAtSplineInputKey(InputKey); }
} }
FVector TargetLocation = CombatPath->FlattenSpline->GetLocationAtDistanceAlongSpline(DistanceAlongSpline, ESplineCoordinateSpace::World); void UPwnCharacterMovementComponent::UpdateLocationOnFlattenedSpline() const {
// Maintain coherent distance along spline with location
FVector TargetLocation = CombatPath->FlattenedSpline->GetLocationAtDistanceAlongSpline(DistanceAlongSpline, ESplineCoordinateSpace::World);
TargetLocation.Z = UpdatedComponent->GetComponentLocation().Z; TargetLocation.Z = UpdatedComponent->GetComponentLocation().Z;
UpdatedComponent->SetWorldLocation(TargetLocation); UpdatedComponent->SetWorldLocation(TargetLocation);
} }
@ -202,7 +203,7 @@ void UPwnCharacterMovementComponent::PhysSplineWalking(const float DeltaTime, in
const FVector OldLocation = UpdatedComponent->GetComponentLocation(); const FVector OldLocation = UpdatedComponent->GetComponentLocation();
const FFindFloorResult OldFloor = CurrentFloor; const FFindFloorResult OldFloor = CurrentFloor;
RecomputeTangentAndAcceleration(); /* -- PAWN MODIFICATIONS -- */ UpdateTangentAndAcceleration(); /* -- PAWN MODIFICATIONS -- */
Acceleration.Z = 0.f; Acceleration.Z = 0.f;
RestorePreAdditiveRootMotionVelocity(); RestorePreAdditiveRootMotionVelocity();
@ -362,7 +363,9 @@ void UPwnCharacterMovementComponent::PhysSplineWalking(const float DeltaTime, in
MaintainHorizontalGroundVelocity(); MaintainHorizontalGroundVelocity();
} }
UpdateDistanceAlongSplineAndLocation(); /* -- PAWN MODIFICATIONS -- */ /* -- PAWN MODIFICATIONS -- */
UpdateDistanceAlongSpline();
UpdateLocationOnFlattenedSpline();
} }
UE_DISABLE_OPTIMIZATION UE_DISABLE_OPTIMIZATION
@ -372,7 +375,7 @@ void UPwnCharacterMovementComponent::PhysSplineFalling(const float DeltaTime, in
return; return;
} }
RecomputeTangentAndAcceleration(); /* -- PAWN MODIFICATIONS -- */ UpdateTangentAndAcceleration(); /* -- PAWN MODIFICATIONS -- */
FVector FallAcceleration = GetFallingLateralAcceleration(DeltaTime); FVector FallAcceleration = GetFallingLateralAcceleration(DeltaTime);
FallAcceleration.Z = 0.f; FallAcceleration.Z = 0.f;
@ -676,7 +679,9 @@ void UPwnCharacterMovementComponent::PhysSplineFalling(const float DeltaTime, in
} }
} }
UpdateDistanceAlongSplineAndLocation(); /* -- PAWN MODIFICATIONS -- */ /* -- PAWN MODIFICATIONS -- */
UpdateDistanceAlongSpline();
UpdateLocationOnFlattenedSpline();
} }
UE_ENABLE_OPTIMIZATION UE_ENABLE_OPTIMIZATION

View File

@ -21,10 +21,10 @@ void APwnCombatPlatformerPath::BeginPlay() {
check(Subsystem); check(Subsystem);
Subsystem->RegisterCombatPath(this); Subsystem->RegisterCombatPath(this);
FlattenSpline = NewObject<USplineComponent>(this, TEXT("FlattenSpline")); FlattenedSpline = NewObject<USplineComponent>(this, TEXT("FlattenedSpline"));
FlattenSpline->SetClosedLoop(Spline->IsClosedLoop(), false); FlattenedSpline->SetClosedLoop(Spline->IsClosedLoop(), false);
FlattenSpline->ReparamStepsPerSegment = Spline->ReparamStepsPerSegment; FlattenedSpline->ReparamStepsPerSegment = Spline->ReparamStepsPerSegment;
FlattenSpline->ClearSplinePoints(false); FlattenedSpline->ClearSplinePoints(false);
const FVector FirstPointLocation = Spline->GetLocationAtSplinePoint(0, ESplineCoordinateSpace::World); const FVector FirstPointLocation = Spline->GetLocationAtSplinePoint(0, ESplineCoordinateSpace::World);
const float FirstPointZ = FirstPointLocation.Z; const float FirstPointZ = FirstPointLocation.Z;
@ -33,17 +33,17 @@ void APwnCombatPlatformerPath::BeginPlay() {
for (int i = 0; i < PointsCount; ++i) { for (int i = 0; i < PointsCount; ++i) {
FVector NewLocation = Spline->GetLocationAtSplinePoint(i, ESplineCoordinateSpace::World); FVector NewLocation = Spline->GetLocationAtSplinePoint(i, ESplineCoordinateSpace::World);
NewLocation.Z = FirstPointZ; NewLocation.Z = FirstPointZ;
FlattenSpline->AddSplinePoint(NewLocation, ESplineCoordinateSpace::World, false); FlattenedSpline->AddSplinePoint(NewLocation, ESplineCoordinateSpace::World, false);
FVector ArriveTangent = Spline->GetArriveTangentAtSplinePoint(i, ESplineCoordinateSpace::World); FVector ArriveTangent = Spline->GetArriveTangentAtSplinePoint(i, ESplineCoordinateSpace::World);
FVector LeaveTangent = Spline->GetLeaveTangentAtSplinePoint(i, ESplineCoordinateSpace::World); FVector LeaveTangent = Spline->GetLeaveTangentAtSplinePoint(i, ESplineCoordinateSpace::World);
ArriveTangent.Z = 0; ArriveTangent.Z = 0;
LeaveTangent.Z = 0; LeaveTangent.Z = 0;
FlattenSpline->SetTangentsAtSplinePoint(i, ArriveTangent, LeaveTangent, ESplineCoordinateSpace::World, false); FlattenedSpline->SetTangentsAtSplinePoint(i, ArriveTangent, LeaveTangent, ESplineCoordinateSpace::World, false);
} }
FlattenSpline->UpdateSpline(); FlattenedSpline->UpdateSpline();
Super::BeginPlay(); Super::BeginPlay();
} }

View File

@ -44,18 +44,47 @@ public:
UFUNCTION(BlueprintCallable, Category="CharacterMovement") UFUNCTION(BlueprintCallable, Category="CharacterMovement")
void ExitSplineFollowMode(); void ExitSplineFollowMode();
/**
* @return True if the movement component is in the specified custom movement mode.
*/
UFUNCTION(BlueprintCallable, Category="CharacterMovement") UFUNCTION(BlueprintCallable, Category="CharacterMovement")
bool IsCustomMovementMode(const ECustomMovementMode Mode) const; bool IsCustomMovementMode(const ECustomMovementMode Mode) const;
private: private:
void RecomputeTangentAndAcceleration(); /**
* Updates the tangent in function of the current distance along the spline. Then updates the acceleration so it will simulate an input in the
* direction of the tangent.
*/
void UpdateTangentAndAcceleration();
/**
* Updates the distance along the spline in function of the current velocity. Then updates the velocity so the Updated Component will move
* to the next location on the spline.
* @param TimeTick Delta time in seconds for this update
*/
void UpdatePawnVelocity(const float TimeTick); void UpdatePawnVelocity(const float TimeTick);
void UpdateDistanceAlongSplineAndLocation(); /**
* Updates the distance along the spline.
* Trace a line from the current location of the Updated Component to the ground. Retrieves the closest point on the flattened spline to the
* intersection point. The distance along the spline is then updated.
*/
void UpdateDistanceAlongSpline();
/**
* Updates the location of the Updated Component to match the current distance along the spline. Must be called once the distance along the spline
* has been updated.
*/
void UpdateLocationOnFlattenedSpline() const;
/**
* Custom movement update so the Updated Component follows the spline. Same behavior as walking on the ground.
*/
void PhysSplineWalking(const float DeltaTime, int32 Iterations); void PhysSplineWalking(const float DeltaTime, int32 Iterations);
/**
* Custom movement update so the Updated Component falls along the spline. Same behavior as falling.
*/
void PhysSplineFalling(const float DeltaTime, int32 Iterations); void PhysSplineFalling(const float DeltaTime, int32 Iterations);
public: public:

View File

@ -29,5 +29,5 @@ public:
bool SwapCamera; bool SwapCamera;
UPROPERTY(Transient, BlueprintReadOnly) UPROPERTY(Transient, BlueprintReadOnly)
TObjectPtr<USplineComponent> FlattenSpline; TObjectPtr<USplineComponent> FlattenedSpline;
}; };