Thanks for replying!
When the Jump Counter is implemented in the Jump() function, it gets incremented before doing the CanJumpInternal_Implementation() function. This causes any jump button press to increment the Jump Counter regardless of whether you CAN or DO jump or not. This is why it seemed to make the most sense to be placed inside the OnJumped_Implemented() event, as this event would only get triggered when the character actually goes into the “Jumping” movement mode.
However, it looks like there is also a slight “bug” (Could be 100% intentional, but I don’t really see the reason at the moment) with the ACharacter::CheckJumpInput() function that requires it to be overridden to get this working properly. JumpKeyHoldTime is incremented before CanJump() or CharacterMovement->DoJump() is done and is incremented even when jumping is not possible or does not occur. This causes IsJumpProvidingForce() to always return true when jump is pressed. As this function is also where OnJumped() is called, the Jump Counter can just be incremented here.
As both ACharacter::Jump and ACharacter::StopJumping both set JumpKeyHoldTime = 0.0f, neither function has to be overridden.
The CanJumpInternal_Implementation() code you provided works perfectly once the change to CheckJumpInput() is made.
Here is the final working code for variable jump height and multiple jumps.
void ACharacter::CheckJumpInput(float DeltaTime)
const bool bWasJumping = bPressedJump && JumpKeyHoldTime > 0.0f;
const bool bDidJump = CanJump() && CharacterMovement && CharacterMovement->DoJump(bClientUpdating);
// Increment our timer ONLY when jumping occurs, so calls to IsJumpProvidingForce() will return true only when jumping is actually occurring
JumpKeyHoldTime += DeltaTime;
if (!bWasJumping && bDidJump)
// Increment Jump Count here so that it only happens when a new jump actually occurs
bool ACharacter::CanJumpInternal_Implementation() const
const bool bCanHoldToJumpHigher = GetJumpMaxHoldTime() > 0.0f && IsJumpProvidingForce();
const bool bHasRemainingJumps = CurrentJumpCount < MaxJumpCount;
return !bIsCrouched && CharacterMovement && (CharacterMovement->IsMovingOnGround() || bCanHoldToJumpHigher || bHasRemainingJumps) && CharacterMovement->CanEverJump() && !CharacterMovement->bWantsToCrouch;
void ACharacter::Landed(const FHitResult& Hit)
CurrentJumpCount = 0;
Thanks for putting me on the right track to get this working!