# ChrisVossen

Member

0

1341 Excellent

• Rank
Newbie

3. ## Building a First-Person Shooter: Part 1.6 Sound

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; i0.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; iRelease(); 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 ); }

5. ## Building a First-Person Shooter: Part 1.4 Mouse Inputs

Now it's time to let the player look around using mouse movements. We start again by adding variables for mouse controls into the constructor: sensitivity=1.0; cameralooksmoothing = 2.0; cameraypositionsmoothing = 3.0; smoothedcamerapositiony = 0; In UpdateControls we will need access to the games context which is the renderable area of the window, which is essentially the area of the game window inside the windows border. We use this context to find the center of the screen and store the coordinates as sx and sy. Context* context = Context::GetCurrent(); //Get the mouse movement float sx = context->GetWidth()/2; float sy = context->GetHeight()/2; Next we save the current mouse position and then return the mouse to the center of the screen: //Get the mouse position Vec3 mouseposition = window->GetMousePosition(); //Move the mouse to the center of the screen window->SetMousePosition(sx,sy); Now that we know where the center of the screen is and the current mouse position we can figure out the difference between the two which tells us which direction to look: //Get change in mouse position float dx = mouseposition.x - sx; float dy = mouseposition.y - sy; We want to set the mouse speed by smoothing between the previous mouse speed and the distance from the center of the screen. //Mouse smoothing mousespeed.x = Math::Curve(dx,mousespeed.x,cameralooksmoothing/Time::GetSpeed()); mousespeed.y = Math::Curve(dy,mousespeed.y,cameralooksmoothing/Time::GetSpeed()); Using the mouse speed we increment the player's rotation and scale it by sensitivity, thus the higher the sensitivity the quicker the mouse movements: //Adjust and set the camera rotation playerrotation.x += mousespeed.y*sensitivity / 10.0; playerrotation.y += mousespeed.x*sensitivity / 10.0; To prevent the player from inhuman neck movements we clamp the y rotation values at -90 and 90: //Prevent inhuman looking angles playerrotation.x = Math::Clamp(playerrotation.x,-90,90); In the player Update function we rotate the camera by the playerrotation value finally allowing for our character to look around: //Set camera rotation camera->SetRotation(playerrotation,true); At this point the player character can move around with keyboard inputs and look around using the mouse: #include "MyGame.h" using namespace Leadwerks; Player::Player() { //Create the entity entity = Pivot::Create(); entity->SetUserData(this); //Initialize values sensitivity=1.0; cameralooksmoothing = 2.0; move = 0.0; strafe = 0.0; cameraypositionsmoothing = 3.0; maxacceleleration = 0.5; movementspeed = 3.0; standheight=1.7; crouchheight=1.2; cameraheight = standheight; smoothedcamerapositiony = 0; //Create the player camera camera = Camera::Create(); camera->SetPosition(0,entity->GetPosition().y + cameraheight,0,true); //Set up player physics entity->SetPhysicsMode(Entity::CharacterPhysics); entity->SetCollisionType(Collision::Character); entity->SetMass(10.0); //Player position entity->SetPosition(0,0,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); //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; //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); entity->SetInput(playerrotation.y,normalizedmovement.z,normalizedmovement.x,0,false,maxaccel); //Set the camera position cameraposition = entity->GetPosition(); camera->SetPosition(cameraposition.x, cameraposition.y + cameraheight, cameraposition.z ); }
6. ## Building a First-Person Shooter: Part 1.3 Keyboard Inputs

Having a player that just stands watching the world go by isn't much of a game so let's add in some movement. We will start by adding a few new variables to the constructor: move = 0.0; strafe = 0.0; cameraypositionsmoothing = 3.0; maxacceleleration = 0.5; movementspeed = 3.0; The player UpdateControls function will be added to the start of the player Update and will manage user inputs via the keyboard and mouse: UpdateControls(); Inside UpdateControls we get the current window and detect if any of the W,A,S,D keys are currently pressed. We will track forward and backward movements in the variable called move and left and right movements in the variable called strafe. KeyDown() will return a Boolean value of true or false, but in C++ these values are essentially the integers 1 (true) and 0 (false). So if you look at the code for move pressing the 'W' key would translate to move = 1 - 0 which results in 1. Window* window = Window::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); Now that we have values for move and strafe we need to normalize them so that moving while strafing doesn't move the character faster than normal. Then after normalizing we scale the movement to the correct movespeed: float maxaccel = this->maxacceleleration; float movespeed = this->movementspeed; normalizedmovement.z = move; normalizedmovement.x = strafe; normalizedmovement = normalizedmovement.Normalize() * movespeed; We now call the entity SetInput with the normalizedmovement the z axis being forward and backwards movement and the x axis being left and right. We also make use of the maxaccel value set earlier: entity->SetInput(0,normalizedmovement.z,normalizedmovement.x,0,false,maxaccel); Finally we set the camera position to the same position as the entity and make sure to set it at the correct height of where a head would sit: cameraposition = entity->GetPosition(); camera->SetPosition(cameraposition.x, cameraposition.y + cameraheight, cameraposition.z ); Running the resulting code will allow you to move around using the W,A,S,D keys: #include "MyGame.h" using namespace Leadwerks; Player::Player() { //Create the entity entity = Pivot::Create(); entity->SetUserData(this); //Initialize values move = 0.0; strafe = 0.0; cameraypositionsmoothing = 3.0; maxacceleleration = 0.5; movementspeed = 3.0; standheight=1.7; crouchheight=1.2; cameraheight = standheight; smoothedcamerapositiony = 0; //Create the player camera camera = Camera::Create(); camera->SetPosition(0,entity->GetPosition().y+cameraheight,0,true); //Set up player physics entity->SetPhysicsMode(Entity::CharacterPhysics); entity->SetCollisionType(Collision::Character); entity->SetMass(10.0); //Player position entity->SetPosition(0,0,0,true); } Player::~Player() { if (camera) { camera->Release(); camera = NULL; } } void Player::UpdateControls() { Window* window = Window::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); } //Update function void Player::Update() { UpdateControls(); float maxaccel = this->maxacceleleration; float movespeed = this->movementspeed; //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 player input entity->SetInput(0,normalizedmovement.z,normalizedmovement.x,0,false,maxaccel); //Set the camera position cameraposition = entity->GetPosition(); camera->SetPosition(cameraposition.x, cameraposition.y + cameraheight, cameraposition.z ); }

8. ## Building a First-Person Shooter Part 1.2: The Player Class

The class is quite large, but when the goal is to teach the basics of FPS mechanics I'd rather spend the majority of the tutorial inside 1 class rather than jumping between multiple classes and interfaces.    entity-SetPosition explicitly sets the players position to the origin, which happens to be the center of the room created earlier.    Not having all members initialized in the constructor is extremely poor programming. When making these tutorials I started with the complete game, then worked my way backwards. My guess is that the un-initialized members are actually initialized in one of the later tutorials. (My bad)
9. ## Building a First-Person Shooter Part 1.1: Visual Studio Setup

No real reasons in particular just trying to stick to the basics.
10. ## Building a First-Person Shooter Part 1.1: Visual Studio Setup

It's very redundant I know, but it is an attempt to have the tutorials be inclusive to all levels of programming skill.    There should be a tutorial released for the next few Mondays!

12. ## Leadwerks 3 Tutorial: Building a First-Person Shooter

Leadwerks 3 doesn't compile CSG geometry into a BSP tree, rather the geometry gets collapsed to model geometry at load time and uses the scene octree for culling. So it scales gracefully with large environments.   The easiest way to test this claim is go in and quickly sketch out a huge level and click "Run". This sounded fun so so I went in and made a bunch of large boxes to represent a city and ran around my beast of a level. (This was purely just to show that large environments run at 60 fps, so I didn't add any materials that is why the scene only has 1 texture.)
13. ## Leadwerks 3 Tutorial: Building a First-Person Shooter

[color=rgb(0,0,0)][font=Verdana][/font][/color] [color=rgb(0,0,0)][font=Verdana]It may have taken a few weeks but the "Building a First-Person Shooter" tutorial series is finally up and running. In this multiple part series we will build a FPS from the ground up using Leadwerks 3 and Visual Studios 2010 C++. Part 1.0 is all about creating a room and covers:[/font][/color] Using brushes to sketch out a room Materials UV Manipulations Lights & Lightmaps [color=rgb(0,0,0)][font=Verdana]If you're interested check out the tutorial: http://www.leadwerks.com/werkspace/page/tutorials/_/building-a-first-person-shooter-part-10-creating-a-room-r22 Or if you just want to fool around with a trial version, you can download a 30 day demo here: http://www.leadwerks.com[/font][/color]
14. ## Native Code Tutorials

[color=rgb(0,0,0)][font=arial]Between the release of LE3 and the GDC, life at Leadwerks has been busy to say the least. Now that things have settled down (a little) it is time for something that I have been looking forward to for a while: Tutorials![/font][/color] [color=rgb(0,0,0)][font=arial]At GDC we were constantly talking about the power and flexibility that direct native code programming allows and how users are not locked into only component based programming. But then you look at the example game Darkness Awaits and see only component based programming and just Lua scripts....[/font][/color] [color=rgb(0,0,0)][font=arial]It's time to right these wrongs.[/font][/color] [color=rgb(0,0,0)][font=arial]A comprehensive series of tutorials are underway that will teach how to use the many tools of the engine as well as demonstrate the capabilities of direct native code programming.[/font][/color] [color=rgb(0,0,0)][font=arial]This series is currently in the design phase and I am working with universities to make sure that the tutorials not only teach the engine but also cover key game development topics.[/font][/color] [color=rgb(0,0,0)][font=arial]Here are a few high level design points for the tutorials:[/font][/color] Teach the engine and its tools Using native code with Leadwerks (aka how to use C++ with visual studios or Xcode along with the Leadwerks 3 engine) First Person Shooter [color=rgb(0,0,0)][font=arial]As always I'd love to hear suggestions and ideas! I'll keep updates coming as tutorial development ramps up![/font][/color]