Download

Player "Jump" node has Z velocity limited.

To put things into context, I have a character class in my game Metal Heads, which when the player taps A, the character is given an impulse upwards…think Flappy bird, but it’s vertical.
I have had this mini-game working for the longest time in offline mode, but when it comes to online, using anything except the Jump node causes lag for clients, whenever they hit A, their character kind of teleports upward which is rather jarring and not enjoyable.
I recently tried the Jump node with a jump velocity of 600 and removed the jump count limit so that the player can continuously jump over and over.
The problem is that the Jump velocity seems to be the Z velocity limit whenever jumping. So it doesn’t matter if the player taps every 0.25 seconds, or every 0.025 seconds, the character will not be able to go faster than a Z velocity of 600.
Which means when the camera speeds up, the player can’t keep up with it, and players all end up dying.

Increasing Jump velocity is not an option…if anything, I actually want to decrease it, but allow each jump to add to the character’s upward momentum.
I do have my own Z velocity limiter of about 1200 to prevent players launching upward at Mach10 however :stuck_out_tongue: (this limiter has been disabled whilst testing the Jump node)

This is just using Add Impulse on the character movement component to move upward and this is the desired movement I am aiming for, however is bad for online play.
https://youtube.com/watch?v=or6oUScd8fk

I did look into the Character.cpp class and found the following code…however this is for **ACharacter::ApplyDamageMomentum **and I can’t find any limitations in velocity in relation to the character using the Jump function.


// limit Z momentum added if already going up faster than jump (to avoid blowing character way up into the sky)
{
FVector MassScaledImpulse = Impulse;
if(!bMassIndependentImpulse && CharacterMovement->Mass > SMALL_NUMBER)
{
MassScaledImpulse = MassScaledImpulse / CharacterMovement->Mass;
}

if ( (CharacterMovement->Velocity.Z > GetDefault<UCharacterMovementComponent>(CharacterMovement->GetClass())->JumpZVelocity) && (MassScaledImpulse.Z > 0.f) )
{
Impulse.Z *= 0.5f;
}

Removing the limiting code didn’t do anything as expected since it has nothing to do with the jump, but I thought I would try anyway since I can’t find anything else that is limiting momentum/Z velocity.

Am I missing something really obvious inside of my character’s blueprint? Or is there a better method of doing what I want to do? (bare in mind, once again I have tried launch, I have tried impulse, add force, set velocity etc to try and move the player up smoothly in an online game and none of them work well except the Jump function.

I’m betting it’s one of those annoying, stupid simple problems, but I’ve tried so many different things out over the last 5-6 hours now and my brain is fried trying to figure this out.

Does anyone have any ideas on how to resolve this problem?

Thank you in advance! :slight_smile:

UPDATE:
It seems that creating another Jump function actually doesn’t resolve the issue.
The Jump function seems to specifically be optimized, so rather than creating doJumpNoLimit() in UCharacterMovementComponent, I just updated the code within doJump to take in an extra parameter, and rather than JumpNoLimit() in ACharacter class being used, I use Jump() and just changed it to take in an extra parameter to determine if there’s a limit or not.
I kept the bJumpLimit variable, and use the Jump(bool Limited) function to change that variable in ACharacter, which is then passed through to doJump() in the CheckJumpInput function.
That probably makes no sense, but anyway, the point is, use the Jump() function, duplicate code won’t be network optimized…

For those wondering, I fixed the problem with a simple solution, but it took ME a while due to my lack of programming experience and re-learning C++ Syntax and flow again.

The limiter was found in the CharacterMovementComponent.cpp class.


bool UCharacterMovementComponent::doJump(bool bReplayingMoves)
{
if ( CharacterOwner && CharacterOwner->CanJump() )
{
// Don't jump if we can't move up/down.
if (!bConstrainToPlane || FMath::Abs(PlaneConstraintNormal.Z) != 1.f)
{
// Check the maximum of either Velocity.Z or JumpZVelocity and set the character Velocity.Z to that value. This is a limiter when jumping. No momentum added per jump however.
**Velocity.Z = FMath::Max(Velocity.Z, JumpZVelocity); <-----CHANGE THIS LINE**
SetMovementMode(MOVE_Falling);
return true;
}
}

return false;
}

Because I created 2 jump functions in the Character.cpp class, I changed the above to this:


bool UCharacterMovementComponent::d​oJumpNoLimit(bool bReplayingMoves)
{
if (CharacterOwner && CharacterOwner->CanJump())
{
// Don't jump if we can't move up/down.
if (!bConstrainToPlane || FMath::Abs(PlaneConstraintNormal.Z) != 1.f)
{
//Simply add to current velocity
**Velocity.Z = Velocity.Z + JumpZVelocity;**
SetMovementMode(MOVE_Falling);
return true;
}
}

return false;
}

I can make this better if I want to, by adding a parameter that takes in a limiting value, or if left at 0, would be unlimited, and I will probably actually try to implement this shortly.

I wanted to have 2 separate jump functions, one which is the standard jump function, another which is “DoJumpNoLimit”, purely just so I’m not destroying any original functions.

In order for me to do this, I created a JumpNoLimit() function in the Character.cpp class which uses the same code from the Jump() function.

I also have a boolean set up called “JumpLimited” inside of the Character class and by default this value is set to true.

I now have the following inside of Character.cpp:


void ACharacter::Jump()
{
bLimitJump = true;
bPressedJump = true;
JumpKeyHoldTime = 0.0f;
}

void ACharacter::JumpNoLimit()
{
bLimitJump = false;
bPressedJump = true;
JumpKeyHoldTime = 0.0f;
}

And in the same class, I changed the CheckJumpInput(); function so that it checks to see if there should be a limit or not, which is decided if the player uses Jump() or JumpNoLimit();


void ACharacter::CheckJumpInput(float DeltaTime)
{
if (CharacterMovement)
{
//IF this is a LIMITED Jump
if (bPressedJump && bLimitJump)
{
// If this is the first jump and we're already falling,
// then increment the JumpCount to compensate.
const bool bFirstJump = JumpCurrentCount == 0;
if (bFirstJump && CharacterMovement->IsFalling())
{
JumpCurrentCount++;
}

const bool bDidJump = CanJump() && CharacterMovement->DoJump(bClientUpdating);
if (bDidJump)
{
// Transition from not (actively) jumping to jumping.
if (!bWasJumping)
{
JumpCurrentCount++;
JumpForceTimeRemaining = GetJumpMaxHoldTime();
OnJumped();
}
}

bWasJumping = bDidJump;
}
//IF this is NOT a LIMITED jump
else if (bPressedJump && !bLimitJump)
{
// If this is the first jump and we're already falling,
// then increment the JumpCount to compensate.
const bool bFirstJump = JumpCurrentCount == 0;
if (bFirstJump && CharacterMovement->IsFalling())
{
JumpCurrentCount++;
}

const bool bDidJump = CanJump() && CharacterMovement->DoJumpNoLimit(bClientUpdating);
if (bDidJump)
{
// Transition from not (actively) jumping to jumping.
if (!bWasJumping)
{
JumpCurrentCount++;
JumpForceTimeRemaining = GetJumpMaxHoldTime();
OnJumped();
}
}

bWasJumping = bDidJump;
}
}
}

And…yeah that’s about it for my fix, I just hope that if someone is trying to do something similar, that they can refer to this and have a solution :slight_smile: