bool UCustomCharacterMovementComponent::TryVault()
{
// Location of the base of the capsule
FVector BaseLocation = UpdatedComponent->GetComponentLocation() + FVector::DownVector * GetCapsuleHalfHeight();
// Forward vector
FVector Forward = UpdatedComponent->GetForwardVector().GetSafeNormal2D();
// Actors to ignore
auto Params = MainCharacter->GetIgnoreCharacterParams();
FHitResult FrontHit;
// Scale the distance in which we check for a possible hit with the velocity of the character
float CheckDistance = FMath::Clamp(Velocity | Forward, GetCapsuleRadius() + 30, VaultMaxDistanceCheck);
// The starting point of the line trace takes into account the step height and a customisable offset
FVector FrontStart = BaseLocation + FVector::UpVector * (MaxStepHeight - 1);
for (int i = 0; i < 10; i++)
{
DRAW_LINE(FrontStart, FrontStart + Forward * CheckDistance, FColor::Yellow)
if (GetWorld()->LineTraceSingleByProfile(FrontHit, FrontStart, FrontStart + Forward * CheckDistance,
"BlockAll", Params)) break;
FrontStart += FVector::UpVector * (GetCapsuleHalfHeight() - (MaxStepHeight - 1)) / 6;
}
if (!FrontHit.IsValidBlockingHit()) return false;
DRAW_POINT(FrontHit.Location, FColor::Red)
// CHECK OBSTACLE HEIGHT
TArray<FHitResult> HeightHits;
FHitResult SurfaceHit;
// Project the UP vector onto the normal vector of the object hit and normalize it
// This give us a vector that goes UP in the direction of the wall
FVector WallUpVector = FVector::VectorPlaneProject(FVector::UpVector, FrontHit.Normal).GetSafeNormal();
FVector TraceStart = FrontHit.Location + Forward + WallUpVector * (VaultMaxPossibleHeight - (MaxStepHeight - 1));
DRAW_LINE(TraceStart, FrontHit.Location + Forward, FColor::Orange);
if(!GetWorld()->LineTraceMultiByProfile(HeightHits, TraceStart, FrontHit.Location + Forward, "BlockAll", Params))
{
return false;
}
for(const FHitResult& Hit : HeightHits)
{
// The tag is for the level designer to specify specific objects that should not be "vaultable"
if (Hit.IsValidBlockingHit() && !Hit.GetActor()->ActorHasTag("NotVault"))
{
SurfaceHit = Hit;
break;
}
}
float Height = (SurfaceHit.Location - BaseLocation) | FVector::UpVector;
PRINT_SCREEN(FString::Printf(TEXT("Height: %f"), Height));
DRAW_POINT(SurfaceHit.Location, FColor::Blue);
if (Height > VaultMaxPossibleHeight) return false;
// CHECK CLEARANCE
// The point the capsule should pass while vaulting
FVector ClearanceCapsuleLocation = SurfaceHit.Location + Forward * GetCapsuleRadius()
+ FVector::UpVector * GetCapsuleHalfHeight();
FCollisionShape CapsuleShape = FCollisionShape::MakeCapsule(GetCapsuleRadius(), GetCapsuleHalfHeight());
if (GetWorld()->OverlapAnyTestByProfile(ClearanceCapsuleLocation, FQuat::Identity, "BlockAll", CapsuleShape, Params))
{
DRAW_CAPSULE(ClearanceCapsuleLocation, FColor::Red)
return false;
}
// Check if there are any obstacles between the player and the final point
if (GetWorld()->OverlapAnyTestByProfile(GetActorLocation() + FVector::UpVector * (GetCapsuleHalfHeight()),
FQuat::Identity, "BlockAll", CapsuleShape, Params))
{
DRAW_CAPSULE(ClearanceCapsuleLocation, FColor::Black)
return false;
}
FVector CapsuleFinalLocationOffset = SurfaceHit.Location + Forward * (GetCapsuleRadius() + VaultMaxPossibleWidth)
+ FVector::UpVector * (GetCapsuleHalfHeight() / 2);
// Check if there is enough space on the other side of the obstacle
if (GetWorld()->OverlapAnyTestByProfile(CapsuleFinalLocationOffset, FQuat::Identity, "BlockAll", CapsuleShape, Params))
{
DRAW_CAPSULE(CapsuleFinalLocationOffset, FColor::Silver)
return false;
}
// If on the other side of the object the ground is at a similar height than the starting point
FHitResult GroundHit;
TArray<AActor*> ActorsToIgnore;
VaultMiddleLocation = ClearanceCapsuleLocation - Forward * GetCapsuleRadius();
if (UKismetSystemLibrary::CapsuleTraceSingleByProfile(GetWorld(), CapsuleFinalLocationOffset,
CapsuleFinalLocationOffset + FVector::DownVector * (GetCapsuleHalfHeight() / 2 + 15),
GetCapsuleRadius(), GetCapsuleHalfHeight(), "BlockAll", false,
ActorsToIgnore, EDrawDebugTrace::None ,GroundHit, true))
{
bFallingVault = false;
DRAW_CAPSULE(VaultMiddleLocation, FColor::Purple)
VaultLocation = GroundHit.Location;
}
else
{
if (Velocity.IsZero()) return false;
bFallingVault = true;
VaultLocation = CapsuleFinalLocationOffset;
DRAW_CAPSULE(VaultLocation, FColor::Blue)
}
bCanVault = true;
return true;
}