Implementation:
Setup:
ALadder : public AActor, public IInteractable- We dynamically create the ladder by setting the
LadderHeightand then add an instanced mesh and fixing theLadderBox,TopPoint, andBottomPoint.
// HISM Ladder Mesh
FTransform InstanceTransform;
const FVector LadderMeshSize = HISLadder->GetStaticMesh()->GetBoundingBox().GetSize();
for (int8 i = 0; i < LadderHeight; ++i)
{
InstanceTransform.SetLocation(FVector(0.0, 0.0, LadderMeshSize.Z * i));
HISLadder->AddInstance(InstanceTransform);
}
// Sizing the Ladder Collision Box
if (LadderBox)
{
LadderMeshHeight = LadderMeshSize.Z * LadderHeight;
const double LadderBoxHeight = LadderMeshHeight / 2.0;
LadderBox->InitBoxExtent(FVector(LadderMeshSize.X, LadderWidthBuffer, LadderBoxHeight + LadderUpperBuffer));
LadderBox->SetRelativeLocation(FVector(0.0, LadderWidthBuffer, LadderBoxHeight + LadderUpperBuffer));
LadderBoxes->InitBoxExtent(FVector(LadderMeshSize.X, LadderWidthBuffer, 50.0));
LadderBoxes->SetRelativeLocation(FVector(0.0, -50.0, LadderMeshHeight + 25.0));
// Sets the UArrowComponent TopPoint and BottomPoint locations & rotations
TopPoint->SetRelativeLocation(FVector(0.0, -50.0, LadderMeshHeight + 15.0));
TopPoint->SetRelativeRotation(FRotator(0.0, -90.0, 0.0));
BottomPoint->SetRelativeLocation(FVector(0.0, 75.0, 0.0));
BottomPoint->SetRelativeRotation(FRotator(0.0, 90.0, 0.0));
}- While on the ladder, we want to limit the character’s ability to look left & right (yaw). This prevents the character from rotating 360 degrees around the ladder.
if (Character && Character->GetPlayerCameraManager())
{
Character->GetPlayerCameraManager()->ViewYawMax = MaxYaw;
Character->GetPlayerCameraManager()->ViewYawMin = MinYaw;
}Interacting:
- The
UInteractableComponentrequires an Actor to adhere to theIInteractableInterfacewhich then requires the Actor to implementOnInteract(). - So, when a
APawninteracts with theALadder, we have to determine what should happen:- If the Pawn is already on the ladder, then we dismount.
- I chose to dismount by “launching” the character off the ladder a bit.
if (Character)
{
const FVector LaunchDir = BottomPoint->GetForwardVector() * 100.0;
Character->LaunchCharacter(LaunchDir, true, false);
StopClimbing(Character);
}- If the Pawn is not on the ladder, then we check if the APawn can interact with said ladder, if so we “mount” the ladder.
- Attaching the Character to the Ladder:
const double AttachHeight = Hit.ImpactPoint.Z;
if ((AttachHeight > (BottomPoint->GetComponentLocation().Z + 25.0)) && (AttachHeight < LadderMeshHeight))
{
const FVector AttachPoint = CharacterDistanceFromLadder->GetComponentLocation();
const FVector TargetLoc = FVector(AttachPoint.X, AttachPoint.Y, AttachHeight);
const FRotator TargetRot = FRotator(0.0, GetActorRotation().Yaw - 90.0, 0.0);
Interpolate(Character, TargetLoc, TargetRot, InterpSpeed);
Character->bUseControllerRotationYaw = false;
return true;
}Moving Up & Down:
- As the character moves up & down the ladder, we want to check if the character is near the
TopPointorBottomPoint.- Which is simply done by calculating the distance between the character and the points.
- If the character is near either point, then we interpolate the character from its current location to the point it’s closest to.
if (DistFromTopPoint < MAXDISTANCEFROMTOPPOINT)
{
//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Can Dismount to Top"));
StopClimbing(Character);
const FVector LandingPoint = TopPoint->GetComponentLocation();
const FVector TargetLoc = FVector(LandingPoint.X, LandingPoint.Y, LandingPoint.Z + Character->GetDefaultHalfHeight());
const FRotator TargetRot = TopPoint->GetComponentRotation();
// Interpolate up
Interpolate(Character, FVector(Character->GetActorLocation().X, Character->GetActorLocation().X, LandingPoint.Z + Character->GetDefaultHalfHeight()), TargetRot, InterpSpeed / 2.0f);
// Interpolate over
Interpolate(Character, TargetLoc, TargetRot, InterpSpeed / 2.0f);
return true;
}
if (DistFromBottomPoint < MAXDISTANCEFROMBOTPOINT)
{
//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Can Dismount to Bottom"));
StopClimbing(Character);
const FVector LandingPoint = BottomPoint->GetComponentLocation();
const FVector TargetLoc = FVector(LandingPoint.X, LandingPoint.Y, LandingPoint.Z + Character->GetDefaultHalfHeight());
Interpolate(Character, TargetLoc, Character->GetActorRotation(), InterpSpeed);
return true;
}