Jump to content

  • Log In with Google      Sign In   
  • Create Account

Like
6Likes
Dislike

Building a First-Person Shooter: Part 1.6 Sound

By ChrisVossen | Published Aug 23 2013 06:42 AM in Game Programming
Peer Reviewed by (ivan.spasov, Dave Hunt, Norman Barrows)

c++ fps leadwerks tutorial

For a final touch we are going to add in sound effects. We begin by declaring a few variables to deal with the footstep sound effects times and frequencies:

footsteptimer=Time::GetCurrent();
footstepwalkfrequency=400;
footsteprunfrequency=320;

Next we load in our sound effects which are located in the MyGame/Sound folder. For variety’s sake we have four separate footstep sounds, four landing sounds, and one jumping grunt sound effect:

//Load Sound files
for (int i=0; i<4; ++i)
{
    footstepsound[i] = Sound::Load("Sound/Player/Footsteps/footstep_shoe-softsole_metal_single_01_v0"+String(i+1)+".wav");
}
for (int i = 0; i < 4; ++i)
{
    landsound[i] = Sound::Load("Sound/Player/Landing/footstep_shoe-softsole_metal_jump_0"+String(i+1)+".wav");
}
for (int i = 0; i < 1; ++i)
{
    jumpsound[i] = Sound::Load("Sound/Player/Jumping/grunt_male01.wav");
}

Inside our Update function we had our code to deal with the jumping mechanic, to add in our sound effects we will go back and work inside the section of the if statement where in the player is currently on the ground. We first check if jump is greater than 0, remember that since we are currently on the ground it means that the jump button was just recently hit thus we can now play the jumping grunt sound as the player starts his jump. Please note that we do not play the jump sound in the UpdateControl function when the Space bar is hit because then the sound effect would be played every time the button was pressed even if you were already jumping. The other key thing we do is set the landing variable to be true:

if (jump>0.0)
{
    jumpsound[0]->Play();
    landing = true;
    
    if (velocity.z>movementspeed*0.85)
    {
        normalizedmovement.z *= jumpboost;
        normalizedmovement.z = min(normalizedmovement.z,movementspeed*runboost);
        maxaccel = 10;
    }
}

Directly above the previous code we now add in a check for the landing variable. If the player is landing then we play the landsound and immediately set landing to be false.

//Play landing sounds
if(landing)
{
    landsound[(int)Math::Random(0,3)]->Play();
    landing = false;
}

All that is left to do is implement the footstep sounds. We first need to calculate ground speed , this will help us determine how often to play the footstep sounds:

Vec3 velocity = entity->GetVelocity(false);
float groundspeed = velocity.xz().Length();

After determining the ground speed it is time for the footsteps code. We start off by checking if that a footstep sound is not currently playing by seeing if the current time is greater than the footsteptimer, we also make sure that the player is not jumping, and that the player is actually moving. If all of these cases are true then we check to make sure the player is moving fast enough to warrant a sound effect, so if the groundspeed is greater than 1.0 we play a footstep. Directly after we decide how much to increment the footsteptimer, if the player is running fast we will increment the timer more than if the player is walking, this is determined by whether or not groundspeed has surpassed a little bit more than movement speed:

//Play footsteps
if (Time::GetCurrent()>footsteptimer && jump==0.0 && normalizedmovement.Length()>0.0)
{
    if (groundspeed>1.0)
    {
        int soundindex = (int)Math::Random(0,3);
        footstepsound[soundindex]->Play();
        if (groundspeed>movementspeed*1.2)
        {
            footsteptimer = Time::GetCurrent() + footsteprunfrequency;
        }
        else
        {
            footsteptimer = Time::GetCurrent() + footstepwalkfrequency;
        }
    }
}

We finally have our complete player class. You can move around with the ‘W’ ‘A’ ‘S’ ‘D’ keys, run with Shift, jump with Space, and crouch with ‘C’. Hit F5 and see the game in action:

#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);
    
    //Load Sound files
    for (int i=0; i<4; ++i)
    {
        footstepsound[i] = Sound::Load("Sound/Player/Footsteps/footstep_shoe-softsole_metal_single_01_v0"+String(i+1)+".wav");
    }
    for (int i = 0; i < 4; ++i)
    {
        landsound[i] = Sound::Load("Sound/Player/Landing/footstep_shoe-softsole_metal_jump_0"+String(i+1)+".wav");
    }
    for (int i = 0; i < 1; ++i)
    {
        jumpsound[i] = Sound::Load("Sound/Player/Jumping/grunt_male01.wav");
    }
}

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
    {
        //Play landing sounds
        if(landing)
        {
            landsound[(int)Math::Random(0,3)]->Play();
            landing = false;
        }
        
        //Give an extra boost if jumping
        if (jump>0.0)
        {
            jumpsound[0]->Play();
            landing = true;
            if (velocity.z>movementspeed*0.85)
            {
                normalizedmovement.z *= jumpboost;
                normalizedmovement.z = min(normalizedmovement.z,movementspeed*runboost);
                maxaccel = 10;
            }
        }
        
        //Play footsteps
        if (Time::GetCurrent()>footsteptimer && jump==0.0 && normalizedmovement.Length()>0.0)
        {
            if (groundspeed>1.0)
            {
                int soundindex = (int)Math::Rnd(0,4);
                footstepsound[soundindex]->Play();
                if (groundspeed>movementspeed*1.2)
                {
                    footsteptimer = Time::GetCurrent() + footsteprunfrequency;
                }
                else
                {
                    footsteptimer = Time::GetCurrent() + footstepwalkfrequency;
                }
            }
        }
    }

    //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

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




PARTNERS