The NeverFall is First Personal Shooter mod for Half-Life 2. 7 years ago The Combines was expelled. Now new life started. Alyx and Gordon live together now. But something goes not as good as should. New danger threatens humanity...

Forum Thread
  Posts  
Analog Jumping (Games : Half-Life 2 : Mods : NeverFall : Forum : Coding : Analog Jumping) Locked
Thread Options
Feb 11 2009 Anchor

We are going to use analog jumping in our mod.
The idea behind this jump style is that the longer you hold down jump, the higher you rise into the air.
this is tutorial, i'll follow to make this: Articles.thewavelength.net


To continue Ibutsu's series on Game Movement, I take a look at setting up an analog jumping system.

This tutorial details how to incorporate analog "float" jumping into your mod. The idea behind this jump style is that the longer you hold down jump, the higher you rise into the air. After a specified period of time, you can no longer go up, and you will start to fall. As you are falling, if you release the jump key and hit it again, you will begin to decelerate until you reach a minimum fall velocity, at which point you will sustain that fall rate as long as jump is held down. By changing one line of code, you can alternate between allowing the user to "catch" his/her self once or as many times as he/she wants.

First we will overload CGameMovement::CheckJumpButton function in the CSDKGameMovement class.

1. In sdk_gamemovement.cpp, declare the function bool CheckJumpButton(); in the public section of the class.

CODE
class CSDKGameMovement : public CGameMovement
{
public:
DECLARE_CLASS( CSDKGameMovement, CGameMovement );

CSDKGameMovement();
//overload CGameMovement::CheckJumpButton
bool CheckJumpButton();


2. Add the function body below the CSDKGameMovement constructor.

CODE
bool CSDKGameMovement::CheckJumpButton( void )
{
}


Ok, that parts done. Now we need to start adding some actual code.

3. First, we need to declare some variables we are going to be using for our jump code. Add the following variables to the private section of the CSDKGameMovement class declaration in sdk_gamemovement.cpp:

CODE
private:

float m_fLastJumpTime; //the last jump time (seconds)
float m_fLastTimeThrough; //the last time we went through this code
int m_iNumJumps; //the number of times we've hit jump in this series of jumps
bool m_bFirstTimeThrough; //is this the first time through this func for this press of jump

bool currInAir; //are we currently in the air
bool prevInAir; //were we in the air last time through?
bool newJump; //is this a new jump?
bool supressJump; //should i shortcircuit out of this jump?


The comments attempt to describe the purpose of each of the member variables, but dont worry about it that much quite yet.

4. Next we want to initialize all that stuff in the CSDKGameMovement constructor. So still in sdk_gamemovement.cpp, edit the constructor to look like this:

CODE
CSDKGameMovement::CSDKGameMovement()
{
//initialize stuff
m_iNumJumps = 0;
m_bFirstTimeThrough = true;
m_fLastJumpTime = -1;
m_fLastTimeThrough = -1;
currInAir = false;
prevInAir = false;
newJump = false;
supressJump = false;
}


5. Ok, the next step is to actually start coding this thing. First we need some basic jump handling logic (ie, what to do if we're dead, in water, on a ladder, etc). Basically, we're copying and pasting this code from CGameMovement::CheckJumpButton(), but here it is in a slightly more compressed format.

CODE
bool CSDKGameMovement::CheckJumpButton( void )
{
//if dead, kill this jump
if(player->pl.deadflag)
{
mv->m_nOldButtons |= IN_JUMP;
m_iNumJumps = 0;
return false;
}

// See if we are waterjumping. If so, decrement count and return.
if (player->m_flWaterJumpTime)
{
player->m_flWaterJumpTime -= gpGlobals->frametime;
if (player->m_flWaterJumpTime < 0)
player->m_flWaterJumpTime = 0;

m_iNumJumps = 0;
return false;
}

// If we are in the water most of the way...
if ( player->GetWaterLevel() >= 2 )
{
// swimming, not jumping
SetGroundEntity( (CBaseEntity *)NULL );

if(player->GetWaterType() == CONTENTS_WATER) // We move up a certain amount
mv->m_vecVelocity[2] = 100;
else if (player->GetWaterType() == CONTENTS_SLIME)
mv->m_vecVelocity[2] = 80;

// play swiming sound
if ( player->m_flSwimSoundTime <= 0 )
{
// Don't play sound again for 1 second
player->m_flSwimSoundTime = 1000;
PlaySwimSound();
}
m_iNumJumps = 0;
return false;
}

// Don't allow jumping for the following
if ( player->m_Local.m_bSlowMovement
|| (player->m_Local.m_bDucking && ( player->GetFlags() & FL_DUCKING ))
|| ( player->m_Local.m_flDuckJumpTime > 0.0f ))
{
m_iNumJumps = 0;
return false;
}

}


The comments explain what is going on in there. Essentially, we are preventing jumping in some cases, handling jumps from underwater and in the water, and when you're in the proccess of ducking.

6. Next, there are a few things we need to be able to do to make float jumping work right.

-We need to know which calls to this function are the result of a new press of the jump button, and we need to know what time these calls occur at, AND we need to know how many times jump has been pressed. At the end of the CheckJumpButton function, add the following code:

CODE
//if it has been more than two frames since the last time this function was called, this is a new jump
if(gpGlobals->curtime - m_fLastTimeThrough > 2*gpGlobals->frametime)
newJump = true;
else
newJump = false;

//if this was a new jump, incrememt the jump count, make it first time through, and log the time
if(newJump)
{
m_iNumJumps++;
m_bFirstTimeThrough = true;
m_fLastJumpTime = gpGlobals->curtime;
m_fLastTimeThrough = gpGlobals->curtime;
}


-We need to know the current and previous state of the player being on the ground or in the air. Right after the above code, add the following:

CODE
//Determine the inAir state
prevInAir = currInAir;
currInAir = (player->GetGroundEntity() == NULL);


7. Ok, now its time to sort out the various jump cases. Here it is with tons of comments and an explanation of whats happening at the end:

CODE
// are we in the air?
if (currInAir)
{
//if we are still rising, and its past our time limit for rising, then stop rising
if((m_iNumJumps == 1 && gpGlobals->curtime - m_fLastJumpTime > JUMPLIMIT))
{
mv->m_nOldButtons |= IN_JUMP;
return false;
}
}
else //we are on the ground so we can jump!
{
if(prevInAir)
{
if(!newJump)
{
//if we were in the air and this isnt a new jump, you're gonna pogo jump
//unless we do something about it. So we will supress the next jumps
mv->m_nOldButtons |= IN_JUMP;
supressJump = true; //tells us to supress the pogo jumping
m_iNumJumps = 0;
prevInAir = false;
return false;
}
}

//sanity check. if we're not in the air and you're jumping then this is your first jump
m_iNumJumps = 1;
prevInAir = false;

//play sound and animation for jumping off ground
player->PlayStepSound( mv->m_vecAbsOrigin, m_pSurfaceData, 1.0, true );
MoveHelper()->PlayerSetAnimation( PLAYER_JUMP );
}

//if we were supposed to supress pogo jumps
if(supressJump)
{
//only supress until the last button wasnt jump
if(mv->m_nOldButtons & IN_JUMP)
return false;
}
//when the user lets go of jump and hits it again, the code will fall through to here and we wont supress anymore
supressJump = false;

// In the air now, so we dont have a ground entity
SetGroundEntity( (CBaseEntity *)NULL );

//figure out if the ground will effect our jump
float flGroundFactor = 1.0f;
if (m_pSurfaceData)
flGroundFactor = m_pSurfaceData->game.jumpFactor;

// If we are ducking, only allow a normal jump
float startz = mv->m_vecVelocity[2];
if ( ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) )
{
//only allow jumping once
if(m_iNumJumps <= 1)
mv->m_vecVelocity[2] = flGroundFactor*JUMP;
}
else
{
//we arent ducking, this is the second time we hit jump, so go into float mode
if(m_iNumJumps > 1)
{
//if we get within +-50 of our terminal velocity, just start going that speed
float diff = MAXFALLRATE - mv->m_vecVelocity[2];
if(diff < 50 && diff > -50)
{
mv->m_vecVelocity[2] = MAXFALLRATE;
}
//only reduce fall rate if we're falling faster than the max fall rate
else if(mv->m_vecVelocity[2] < MAXFALLRATE)
{
//decelerate us, taking the tick rate into consideration (cpu speed wont effect rate of fall/rise)
//the *100 is just cause frametime is REALLY small
mv->m_vecVelocity[2] += (FALLDECEL*gpGlobals->frametime)*100;
//dont want to shoot up over maxfallrate
if(mv->m_vecVelocity[2] > MAXFALLRATE)
mv->m_vecVelocity[2] = MAXFALLRATE;
}
}
else
{
//if this is the first jump vs the sustaining jumps
if(m_bFirstTimeThrough)
mv->m_vecVelocity[2] += (flGroundFactor * JUMP); //regular jump to get us up quick
else
mv->m_vecVelocity[2] += (RISEACCEL*gpGlobals->frametime)*100; //accelerate up
}
}

//allow gravity to do its thing
FinishGravity();

//stick the results of our meddling into the jump results vector
mv->m_outJumpVel.z += mv->m_vecVelocity[2] - startz;
mv->m_outStepHeight += 0.15f;

// Flag that we jumped.
mv->m_nOldButtons |= IN_JUMP;
//its no longer our first time through
m_bFirstTimeThrough = false;
m_fLastTimeThrough = gpGlobals->curtime;

return true;


Ok, so what we're doing is checking to see if we're in the air or not. If we are in the air we're either going to be continuing upward, or floating downward. If we're going up, and we're past the time limit on going up, we cant keep going up. If we're on the ground, then we are allowed to start jumping from scratch. Pogo jumping is very irritating, so there is special code in there to handle it.

So thats it, now you need to do a few things to get this thing to compile, so onward!...

8. We need to declare CSDKGameMovement a friend class of C_BasePlayer so that we can access the private members (/me chuckles) of the C_BasePLayer class . To do this, go into c_baseplayer.h and search for statement "friend class CGameMovement" Right below that series of friend class declarations, add CSDKGameMovement as a friend class.

CODE
friend class CSDKGameMovement;


9. Finally, you have to stick the different vars I was using for determining acceleration, deceleration, jump limit, etc. So go to the top of sdk_gamemovement.cpp and add the following. The values associated with each #define are the ones I have been playing with lately, but feel free to experiment.

CODE
#define JUMP 200.0 //how much to jump, normal is a bit more than this (~260?)
#define RISEACCEL 15.0 //rise acceleration
#define MAXFALLRATE -240.0 //max fall rate, true fall rate will be a little more than this because of gravity
#define FALLDECEL 25.0 //deceleration, units? haha...
#define JUMPLIMIT 0.8 //time limit in seconds


Well congratulations. Thats it. You're done. I have to go now... I think I have a... Food... in the oven.

-Hopper-
Mind-Body-Power, a HL2 mod
www.mind-body-power.net



Wavelength version: 3.0.0.9

Edited by: nuxdie

Reply to thread
click to sign in and post

Only registered members can share their thoughts. So come on! Join the community today (totally free - or sign in with your social account on the right) and join in the conversation.