# How to write jump logic

This topic is 2898 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi guys. I am trying to create a simple 3d platformer. I have written a simple Euler based physics intergrator. So far, all my characters, including the player, move by applying a force vector to their physics object. The intergrator then calculates how to displace the character. I am trying to write some jump logic right now but I am running into some issues.

Currently, if the player is on a platform and tries to jump, I add a force along the world's up vector. So long as he is NOT on a platform, I apply a gravitational force along the negative world up vector.
The problem I am seeing however is that the player seems to almost pop up in the sky. There is no gradual movement. I was just curious if you guys had any thoughts. Here is the logic that I wrote.

 // Physics integrator public void Initialize( GameObject go ) { mGameObject = go; ClearForceAccumulator(); mfMass = 1.0f; mfDamp = 0.1f; mvecVelocity = Vector3.zero; mfDisplacementTerminalSqr = 3.0f * 3.0f; mbOnPlatform = false; mbHitAbovePlatform = false; mfGravity = -700.0f; mbEnable = true; mvecOldPosition = go.transform.position; } public void OnUpdate() { if( !IsEnabled() ) { return; } mbHitAbovePlatform = false; Vector3 vecPos = mGameObject.transform.position; Vector3 vecFwd = mGameObject.transform.forward; Vector3 vecUp = mGameObject.transform.up; RaycastHit hitInfoFwd; RaycastHit hitInfoUp; Physics.Raycast( mGameObject.transform.position, -vecUp, out hitInfoUp, 0.007f ); // If we are not on a platform, apply gravity if( hitInfoUp.collider == null ) { AddForce( Vector3.up * mfGravity ); } Vector3 vecTempNewPosition = CalculateNewPosition(); Vector3 vecTempNewPositionNormal = vecTempNewPosition.normalized; float fDiffInY = Mathf.Abs(vecTempNewPosition.y - mGameObject.transform.position.y); float fProngUp1 = 0.01f; float fProngLength1 = fDiffInY + 0.0f; Debug.DrawRay(mGameObject.transform.position, -vecUp, Color.yellow, fProngLength1 ); if( fDiffInY > 0.0f ) { // If you are falling downward if( Vector3.Dot(vecTempNewPositionNormal, Vector3.up) <= 0.0f ) { Physics.Raycast( mGameObject.transform.position, -vecUp, out hitInfoUp, fProngLength1 ); // Check if the character landed on a platform... if( hitInfoUp.collider != null ) { mvecForceAccumulator.y = 0.0f; mvecVelocity.y = 0.0f; mbOnPlatform = true; Debug.Log("On Platform"); //mGameObject.transform.position = Vector3.Lerp(mGameObject.transform.position, vecTempNewPosition, 0.01f); mGameObject.transform.position = hitInfoUp.point + new Vector3(0,0.00f,0); } else { mbOnPlatform = false; } } } // Logic for moving the character along his forward and right vectors... vecTempNewPosition = CalculateNewPosition(); Vector3 vecDir = vecTempNewPosition - mGameObject.transform.position; vecDir.y = 0.0f; vecDir.Normalize(); float fProngUp = 0.01f; float fProngLength = 2.0f; Physics.Raycast( vecTempNewPosition + (vecUp * fProngUp), vecDir, out hitInfoFwd, fProngLength ); Debug.DrawRay(vecTempNewPosition + (vecUp * fProngUp), vecDir, Color.red, fProngLength ); if( hitInfoFwd.collider == null ) { mvecOldPosition = mGameObject.transform.position; mGameObject.transform.position = CalculateNewPosition(); } ClearForceAccumulator(); } public Vector3 CalculateNewVelocity() { Vector3 vecAcceleration = (mvecForceAccumulator / mfMass); Vector3 vecPotentialNewVelocity = mvecVelocity + vecAcceleration * Time.deltaTime; vecPotentialNewVelocity *= mfDamp; return vecPotentialNewVelocity; } public Vector3 CalculateNewPosition() { mvecVelocity = CalculateNewVelocity(); Vector3 vecNewPosition = mGameObject.transform.position + mvecVelocity * Time.deltaTime; return vecNewPosition; } public void AddForce( Vector3 vecForce ) { mvecForceAccumulator += vecForce; } public Vector3 GetTotalForces() { return mvecForceAccumulator; } public void ClearForceAccumulator() { mvecForceAccumulator = Vector3.zero; } public bool IsStandingOnPlatform() { return mbOnPlatform; } 

And the following is the code I wrote when entering the character's jump state...

 if( player.GetPhysicsObject().IsStandingOnPlatform() ) { mfCurrentForce = 3000.0f; Debug.Log("Added jump force"); player.GetPhysicsObject().AddForce( Vector3.up * mfCurrentForce); } 

Is the way I am going about it wrong? What are your suggestions? Thanks!

##### Share on other sites
Hidden
If the player gives command to jump, you apply a vertical force. For how long is that force staying active? This is not clear from the code and it seems it stays there forever - which would be wrong.

If you want the character to jump immediately as most games do (not simulating the legs acting like springs and aplying force during some time), you should apply just a force impulse, resulting in a sudden incease in the vertical velocity. In Havok (which is the only physics system I have experiences with), you can apply impulse. If you don't have such a feature, you should apply a force during one frame.

Could you better describe the behaviour you're getting?
Right now it seems (from the description) that you just have to lower the force value (3000.0f).

Btw, what's the reason of vecPotentialNewVelocity *= mfDamp with mfDamp being only 0.1f?
You calculate velocity from previous velocity and acceleration and then apply only 1/10 of it?

##### Share on other sites
Basically, if I start the player off in the air, he will smoothly fall to the ground at a constant speed. I would like the acceleration of the fall to gradually increase so that the character appears to be falling faster overtime but that is a different issue...

The issue I am seeing however is that when the player jumps, I see almost instantaneously appear 10 feet away from the floor. It should not be this way. The player should smoothly jump up and you should see him move up in the air and then fall back down.

As for the damping, I don't want velocity to remain constant otherwise the character would just slide like on an icey platform. So I am applying a damper to make sure the velocity goes back to 0 over time.

##### Share on other sites
The issue I am seeing however is that when the player jumps, I see almost instantaneously appear 10 feet away from the floor. It should not be this way. The player should smoothly jump up and you should see him move up in the air and then fall back down.

And what's he doing then? He appears 10 feet above in one (probably) frame and then he further ascends or is it the peek and he then starts to fall back down? Did you try decreasing the jumping force? What happened?
Btw, also fix the following first. You'll most probably notice that you have to modify your gravity A LOT.

Basically, if I start the player off in the air, he will smoothly fall to the ground at a constant speed. I would like the acceleration of the fall to gradually increase so that the character appears to be falling faster overtime but that is a different issue...

You should want a constant acceleration (gravity), causing increasing velocity. You said it a bit wrong, but I guess that was just a mistake and you know what's going on.
Anyway, your character is falling at a constant speed, you say. You code seems to be all right, because you have something like this (which is good):
v = v + a * dt
p = p + v * dt
But, between these two steps you multiply v by 0,1, almost nullify it. After mere two frames (timesteps), you get 0,1*0,1 = 1 %. What you really are doing here is almost completely (after few steps) ignoring the previous velocity and in fact turning this row (a right one):
v = v + a * dt
into this row (a bad one):
v = a * dt
Which explains why are you getting constant falling velocity.

The whole thing with 0,1 damping applied this way is wrong. Now the question is what do you really need it for:

As for the damping, I don't want velocity to remain constant otherwise the character would just slide like on an icey platform. So I am applying a damper to make sure the velocity goes back to 0 over time.

This applies when you are moving on a platform? A friction would be better in that case. Friction would mean to apply a force vector which would always point in the opposite direction from the current velocity vector, while the magnitude of the friction force vector is proportional to mass * gravity * friction_coefficient.
If you want to apply some resistance in all situations, even when flying in air, then you would need some king of air resistance. It also makes a force applied against the movement, but its magnitude now depends on the actual velocity, namely on its square. But the result is a force (thus acceleration), which is not the same as multiplying velocity by some constant lower than zero.

##### Share on other sites
Hey thanks a lot! That your advice totally fixed my issues. Now my code looks more like the following,

 public class PhysicsIntegrator : MonoBehaviour { private GameObject mGameObject = null; private Vector3 mvecForceAccumulator; private float mfMass; private float mfDamp; public Vector3 mvecVelocity; private float mfRunningVelocityThreshold; private float mfDisplacementTerminalSqr; private Vector3 mvecOldPosition; private bool mbOnPlatform; private bool mbHitAbovePlatform; private float mfGravity; private bool mbEnable; private GameObject mPushObject; public void Initialize( GameObject go ) { mGameObject = go; ClearForceAccumulator(); mfMass = 10.0f; mfDamp = 0.10f; mvecVelocity = Vector3.zero; mfRunningVelocityThreshold = 3.0f; mfDisplacementTerminalSqr = 3.0f * 3.0f; mbOnPlatform = false; mbHitAbovePlatform = false; mfGravity = -20.0f; mbEnable = true; mvecOldPosition = go.transform.position; mPushObject = null; } public void OnUpdate() { if( !IsEnabled() ) { return; } mbHitAbovePlatform = false; Vector3 vecPos = mGameObject.transform.position; Vector3 vecFwd = mGameObject.transform.forward; Vector3 vecUp = mGameObject.transform.up; RaycastHit hitInfoFwd; RaycastHit hitInfoUp; Vector3 vecForceNormal = mvecForceAccumulator.normalized; Vector3 vecVelocityNormal = mvecVelocity.normalized; // If the overall force is moving downard.... //if( Vector3.Dot( vecForceNormal, Vector3.up ) <= 0.0f ) //{ AddForce(Vector3.up * mfGravity); //} float fCharacterHeight = 2.50f; Vector3 fBottomPos = vecPos + (-Vector3.up * 0.50f); Debug.DrawRay(fBottomPos, Vector3.up * fCharacterHeight, Color.yellow); Physics.Raycast(fBottomPos , Vector3.up * fCharacterHeight, out hitInfoUp, LayerMask.NameToLayer("Character")); mbOnPlatform = false; if( hitInfoUp.collider && hitInfoUp.collider.gameObject != mGameObject && Vector3.Dot( vecVelocityNormal, Vector3.up ) <= 0.0f ) { Vector3 newPosition = fBottomPos; newPosition.y = (hitInfoUp.point.y - fBottomPos.y) + 0.50f; fBottomPos.y += newPosition.y; mGameObject.transform.position = fBottomPos; mvecForceAccumulator.y = 0.0f; mvecVelocity.y = 0.0f; mbOnPlatform = true; } Vector3 vecTempNewPosition = CalculateNewPosition(); Vector3 vecDir = vecTempNewPosition - mGameObject.transform.position; vecDir.y = 0.0f; vecDir.Normalize(); float fProngUp = 0.01f; float fProngLength = 2.0f; Physics.Raycast( vecTempNewPosition + (vecUp * fProngUp), vecDir, out hitInfoFwd, fProngLength ); Debug.DrawRay(vecTempNewPosition + (vecUp * fProngUp), vecDir, Color.red, fProngLength ); if( hitInfoFwd.collider == null ) { mvecOldPosition = mGameObject.transform.position; Vector3 vecNewVelocity = CalculateNewVelocity(); if( mvecVelocity.sqrMagnitude >= (mfRunningVelocityThreshold * mfRunningVelocityThreshold) ) { float fVelOldY = mvecVelocity.y; mvecVelocity = mvecVelocity.normalized * mfRunningVelocityThreshold; mvecVelocity.y = fVelOldY; } Vector3 vecNegVelocityNorm = -mvecVelocity.normalized; Vector3 vecFrictionForce = -mvecVelocity.normalized * (mfMass * 3.5f); vecFrictionForce.y = 0.0f; AddForce( vecFrictionForce ); mGameObject.transform.position = CalculateNewPosition(); mPushObject = null; } else { mvecVelocity.x = 0.0f; mvecVelocity.y = 0.0f; mPushObject = hitInfoFwd.collider.gameObject; } ClearForceAccumulator(); } public GameObject GetPushObject() { return mPushObject; } public void HACKSetJumpVelocity( float fVal ) { mvecVelocity.y = fVal; } public Vector3 CalculateNewVelocity() { Vector3 vecAcceleration = (mvecForceAccumulator / mfMass); Vector3 vecPotentialNewVelocity = mvecVelocity + vecAcceleration * Time.deltaTime; //vecPotentialNewVelocity *= mfDamp; return vecPotentialNewVelocity; } public Vector3 CalculateNewPosition() { mvecVelocity = CalculateNewVelocity(); Vector3 vecNewPosition = mGameObject.transform.position + mvecVelocity * Time.deltaTime; return vecNewPosition; } public Vector3 GetVelocity() { return mvecVelocity; } public Vector3 GetNormalizedVelocity() { return mvecVelocity.normalized; } public void SetVelocity( Vector3 vecVelocity ) { mvecVelocity = vecVelocity; } public void AddForce( Vector3 vecForce ) { mvecForceAccumulator += vecForce; } public Vector3 GetTotalForces() { return mvecForceAccumulator; } public void ClearForceAccumulator() { mvecForceAccumulator = Vector3.zero; } public float GetMass() { return mfMass; } public void SetMass( float fMass ) { mfMass = fMass; } public void SetRunningVelocityThreshold( float fThreshold ) { mfRunningVelocityThreshold = fThreshold; } public bool IsStandingOnPlatform() { return mbOnPlatform; } public bool HitAbovePlatform() { return mbHitAbovePlatform; } public void Enable( bool bEnable ) { mbEnable = bEnable; } public bool IsEnabled() { return mbEnable; } } 

This works really well for me. I even have pushable objects now! Thanks alot!

• ### Game Developer Survey

We are looking for qualified game developers to participate in a 10-minute online survey. Qualified participants will be offered a \$15 incentive for your time and insights. Click here to start!

• 15
• 12
• 9
• 11
• 15