Building a First-Person Shooter: Part 1.5 Running, Jumping, & Crouching
In this tutorial we will expand our player class by adding in inputs to allow the player to run, jump, and crouch.
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 ); }
Related Tutorials
Balancing Game Development and Creative Direction in Indie Production
A practical look at how indie developers can balance creative direction with hands-on game development. This article co…
My Unreal Engine Development Process: From Core Idea to Playable Build
A practical overview of my Unreal Engine development process, covering how I move from a core game idea to a playable b…
Introducing LaneGraph: The Ultimate Road Network Solution for Unity
Discover the power of LaneGraph, a lightweight and flexible lane-based navigation system for Unity. LaneGraph makes it…
Retargeting Mixamo Characters with Root Motion In Unreal Engine 5.4.
I have always found Retargeting Mixamo Characters To have Root Motion is a serious lengthy Tast, Recently I stumbled up…
How To Make A SIMPLE Main Menu In Unity
In this tutorial for unity, i go over how to make a simple main menu for unity, it's an unlisted video because i do not…
Guide to Gameplay Balance
A perspective on competitive gameplay balance, from a background of "shooter" sandbox design.
Discussion
More from Christopher Vossen
Building a First-Person Shooter Part 1.2: The Player Class
In this tutorial we will begin to flesh out our player class by adding in a camera and setting up character physics.
Building a First-Person Shooter Part 1.1: Visual Studio Setup
This is the second lesson in a set of tutorials that demonstrate how to build a complete first-person shooter game, fro…
Building a First-Person Shooter: Part 1.6 Sound
In this tutorial we will finish off this First-Person Shooter Tutorial 1 by adding in sound effects into the game.
Discussion