Jump to content

  • Log In with Google      Sign In   
  • Create Account


Like
6Likes
Dislike

Building a First-Person Shooter: Part 1.5 Running, Jumping, & Crouching

By Christopher Vossen | Published Aug 19 2013 08:20 AM in Game Programming
Peer Reviewed by (jbadams, Aldacron, ivan.spasov)

leadwerks c++ fps tutorial

At this point making the player run at a faster speed is a simple two step process, we need to detect if the shift key is hit and then multiply movespeed. We start off by adding in two variables to detect if the player is running and a multiplier value for increasing movespeed:

running=false;
runboost = 2.0;

In the UpdateControls function we add in a line to check if the shift key has been hit:

running = Window::GetCurrent()->KeyDown(Key::Shift);

Then in the player Update, multiply movespeed by the runboost factor and increase maxaccel:

//Run if shift key is pressed
if (running) movespeed *= runboost;
if (running) maxaccel *= 2.0;

Your player class should now look like the following:

#include "MyGame.h"

using namespace Leadwerks;

Player::Player()
{
    //Create the entity
    entity = Pivot::Create();
    entity->SetUserData(this);
    
    //Initialize values
    standheight=1.7;
    crouchheight=1.2;
    cameraheight = standheight;
    move = 0.0;
    strafe = 0.0;
    movementspeed = 3.0;
    maxacceleleration = 0.5;
    sensitivity=1.0;
    smoothedcamerapositiony = 0;
    cameraypositionsmoothing = 3.0;
    cameralooksmoothing = 2.0;
    runboost = 2.0;
    running=false;
    
    
    //Set up player physics
    entity->SetPhysicsMode(Entity::CharacterPhysics);
    entity->SetCollisionType(Collision::Character);
    entity->SetMass(10.0);
    
    //Player position
    entity->SetPosition(0,0,0,true);
    
    //Create the player camera
    camera = Camera::Create();
    camera->SetPosition(0,entity->GetPosition().y + cameraheight,0,true);
}

Player::~Player()
{
    if (camera)
    {
        camera->Release();
        camera = NULL;
    }
}

void Player::UpdateControls()
{
    Window* window = Window::GetCurrent();
    Context* context = Context::GetCurrent();
    
    //Get inputs from the controller class
    move = window->KeyDown(Key::W) - window->KeyDown(Key::S);
    strafe = window->KeyDown(Key::D) - window->KeyDown(Key::A);
    running = Window::GetCurrent()->KeyDown(Key::Shift);
    
    //Get the mouse movement
    float sx = context->GetWidth()/2;
    float sy = context->GetHeight()/2;
    
    //Get the mouse position
    Vec3 mouseposition = window->GetMousePosition();
    
    //Move the mouse to the center of the screen
    window->SetMousePosition(sx,sy);
    
    //Get change in mouse position
    float dx = mouseposition.x - sx;
    float dy = mouseposition.y - sy;
    
    //Mouse smoothing
    mousespeed.x = Math::Curve(dx,mousespeed.x,cameralooksmoothing/Time::GetSpeed());
    mousespeed.y = Math::Curve(dy,mousespeed.y,cameralooksmoothing/Time::GetSpeed());
    
    //Adjust and set the camera rotation
    playerrotation.x += mousespeed.y*sensitivity / 10.0;
    playerrotation.y += mousespeed.x*sensitivity / 10.0;
    
    //Prevent inhuman looking angles
    playerrotation.x = Math::Clamp(playerrotation.x,-90,90);
}

//Update function
void Player::Update()
{
    UpdateControls();
    
    float maxaccel = this->maxacceleleration;
    float movespeed = this->movementspeed;
    
    //Run if shift key is pressed
    if (running) movespeed *= runboost;
    
    //Make sure movements are normalized so that moving forward at the same time as strafing doesn't move your character faster
    normalizedmovement.z = move;
    normalizedmovement.x = strafe;
    normalizedmovement = normalizedmovement.Normalize() * movespeed;
    
    
    //Set camera rotation
    camera->SetRotation(playerrotation,true);
    
    //Set player input
    if (running) maxaccel *= 2.0;
    entity->SetInput(playerrotation.y,normalizedmovement.z,normalizedmovement.x,0,false,maxaccel);
    
    //Set the camera position & smooth
    cameraposition = entity->GetPosition();
    camera->SetPosition(cameraposition.x, cameraposition.y + cameraheight, cameraposition.z );
}

Jumping and Crouching


Since jumping and crouching both affect camera height we are going to implement both of them at the same time. Like normal we add in the needed variables into the constructor:

jump = 0.0;
jumpforce = 6.0;
jumpboost=2.0;
crouched = false;

Now we turn our attention to the UpdateControl function. We check if the space key has been hit and then multiply it by a jump force. We also look to see if the ‘C’ button is hit, but only if the player is not in the air:

jump = window->KeyDown(Key::Space) * jumpforce;
if (!entity->GetAirborne())
{
	crouched = window->KeyDown(Key::C);
}

Player Update is our next focus and we begin to set the camera to its proper height. If the player is crouching we increment the camera height by the crouchheight but if the player is standing normally we increment it by standheight:

if (entity->GetCrouched())
{
	cameraheight = Math::Inc(crouchheight,cameraheight,0.1);
}
else
{
	cameraheight = Math::Inc(standheight,cameraheight,0.1);
}

To implement the jumping mechanic we continue our work in the Update function. If the entity is in the air we set jump to 0 to prevent the player from in-air jumps. To achieve the long jump feeling we want from our FPS we decided to boost the player's velocity while in-air:

if (entity->GetAirborne()) //Player is in the air
{
    //if the player is in the air don't let them jump
    jump = 0.0;
}
else //Player is on the ground
{
    //Give an extra boost if jumping
    if (jump>0.0)
    {
        if (velocity.z>movementspeed*0.85)
        {
            normalizedmovement.z *= jumpboost;
            normalizedmovement.z = min(normalizedmovement.z,movementspeed*runboost);
            maxaccel = 10;
        }
    }
}

Now that we have a jump and crouch value we have to remember to change our SetInput function to the correct values:

entity>SetInput(playerrotation.y,normalizedmovement.z,normalizedmovement.x,jump,crouched,maxaccel);

With all the jumping and crouching going on, our camera’s y position will be changing often, this means that we should now smooth the camera’s y movements:

//Set the camera position & smooth
cameraposition = entity->GetPosition();
if (cameraposition.y>smoothedcamerapositiony)
{
    smoothedcamerapositiony = Math::Curve(cameraposition.y, smoothedcamerapositiony, cameraypositionsmoothing / Time::GetSpeed());
    cameraposition.y=smoothedcamerapositiony;
}
else
{
	smoothedcamerapositiony = cameraposition.y;
}
camera->SetPosition(cameraposition.x, cameraposition.y + cameraheight, cameraposition.z );

Our quickly growing player class will now look like so:

#include "MyGame.h"

using namespace Leadwerks;

Player::Player()
{
    //Create the entity
    entity = Pivot::Create();
    entity->SetUserData(this);
    
    //Initialize values
    standheight=1.7;
    crouchheight=1.2;
    cameraheight = standheight;
    move = 0.0;
    strafe = 0.0;
    movementspeed = 3.0;
    maxacceleleration = 0.5;
    sensitivity=1.0;
    smoothedcamerapositiony = 0;
    cameraypositionsmoothing = 3.0;
    cameralooksmoothing = 2.0;
    runboost = 2.0;
    jump = 0.0;
    jumpforce = 6.0;
    jumpboost = 2.0;
    footstepwalkfrequency=400;
    footsteprunfrequency=320;
    footsteptimer=Time::GetCurrent();
    running=false;
    crouched = false;
    landing = false;
    
    //Set up player physics
    entity->SetPhysicsMode(Entity::CharacterPhysics);
    entity->SetCollisionType(Collision::Character);
    entity->SetMass(10.0);
    
    //Player position
    entity->SetPosition(0,0,0,true);
    
    //Create the player camera
    camera = Camera::Create();
    camera->SetPosition(0,entity->GetPosition().y + cameraheight,0,true);
}

Player::~Player()
{
    if (camera)
    {
        camera->Release();
        camera = NULL;
    }
}

void Player::UpdateControls()
{
    Window* window = Window::GetCurrent();
    Context* context = Context::GetCurrent();
    
    //Get inputs from the controller class
    move = window->KeyDown(Key::W) - window->KeyDown(Key::S);
    strafe = window->KeyDown(Key::D) - window->KeyDown(Key::A);
    jump = window->KeyDown(Key::Space) * jumpforce;
    
    if (!entity->GetAirborne())
    {
	    crouched = window->KeyDown(Key::C);
    }
    running = Window::GetCurrent()->KeyDown(Key::Shift);
    
    //Get the mouse movement
    float sx = context->GetWidth()/2;
    float sy = context->GetHeight()/2;
    
    //Get the mouse position
    Vec3 mouseposition = window->GetMousePosition();
    
    //Move the mouse to the center of the screen
    window->SetMousePosition(sx,sy);
    
    //Get change in mouse position
    float dx = mouseposition.x - sx;
    float dy = mouseposition.y - sy;
    
    //Mouse smoothing
    mousespeed.x = Math::Curve(dx,mousespeed.x,cameralooksmoothing/Time::GetSpeed());
    mousespeed.y = Math::Curve(dy,mousespeed.y,cameralooksmoothing/Time::GetSpeed());
    
    //Adjust and set the camera rotation
    playerrotation.x += mousespeed.y*sensitivity / 10.0;
    playerrotation.y += mousespeed.x*sensitivity / 10.0;
    
    //Prevent inhuman looking angles
    playerrotation.x = Math::Clamp(playerrotation.x,-90,90);
}

//Update function
void Player::Update()
{
    UpdateControls();
    
    if (entity->GetCrouched())
    {
    cameraheight = Math::Inc(crouchheight,cameraheight,0.1);
    }
    else
    {
    cameraheight = Math::Inc(standheight,cameraheight,0.1);
    }
    
    float maxaccel = maxacceleleration;
    
    Vec3 velocity = entity->GetVelocity(false);
    float groundspeed = velocity.xz().Length();
    float movespeed = this->movementspeed;
    
    //Run if shift key is pressed
    if (running) movespeed *= runboost;
    
    //Make sure movements are normalized so that moving forward at the same time as strafing doesn't move your character faster
    normalizedmovement.z = move;
    normalizedmovement.x = strafe;
    normalizedmovement = normalizedmovement.Normalize() * movespeed;
    
    if (entity->GetAirborne()) //Player is in the air
    {
        //if the player is in the air don't let them jump
        jump = 0.0;
    }
    else //Player is on the ground
    {
        //Give an extra boost if jumping
        if (jump>0.0)
        {
            if (velocity.z>movementspeed*0.85)
            {
                normalizedmovement.z *= jumpboost;
                normalizedmovement.z = min(normalizedmovement.z,movementspeed*runboost);
                maxaccel = 10;
            }
		}
    }
}

//Set camera rotation
camera->SetRotation(playerrotation,true);

//Set player input
if (running) maxaccel *= 2.0;

entity->SetInput(playerrotation.y,normalizedmovement.z,normalizedmovement.x,jump,crouched,maxaccel);

//Set the camera position & smooth
cameraposition = entity->GetPosition();
if (cameraposition.y>smoothedcamerapositiony)
{
    smoothedcamerapositiony = Math::Curve(cameraposition.y, smoothedcamerapositiony, cameraypositionsmoothing / Time::GetSpeed());
    cameraposition.y=smoothedcamerapositiony;
}
else
{
	smoothedcamerapositiony = cameraposition.y;
}
camera->SetPosition(cameraposition.x, cameraposition.y + cameraheight, cameraposition.z );
}



License


GDOL (Gamedev.net Open License)




Comments

you may want to do a follow up article that implements a more realistic flight model for jumping instead of the usual "just fake it" stuff seen in shooters. 

 

however i understand this is just a tutorial, and such exercises are usually considered beyond the scope of such articles and therefore left to the reader.


Note: Please offer only positive, constructive comments - we are looking to promote a positive atmosphere where collaboration is valued above all else.




PARTNERS