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

Published May 08, 2015 by Chris Vossen, posted by ChrisVossen
Do you see issues with this article? Let us know.
Advertisement
It's finally time to begin fleshing out our player class. This class will manage the first-person user controls. We will start by setting up player.h. This header file will contain the declarations of the player class as well as the various variables and functions that will be held within the player class. At this time copy and paste the following code into player.h: #pragma once #include "MyGame.h" using namespace Leadwerks; class Player: public Node { private: Camera* camera; float standheight; float crouchheight; float cameraheight; float move; float strafe; float movementspeed; float maxacceleleration; float sensitivity; float smoothedcamerapositiony; float cameraypositionsmoothing; float cameralooksmoothing; float runboost; float jump; float jumpforce; float jumpboost; int footstepwalkfrequency; int footsteprunfrequency; long footsteptimer; bool running; bool crouched; bool landing; Vec2 mousespeed; Vec3 normalizedmovement; Vec3 cameraposition; Vec3 playerrotation; Sound* footstepsound[4]; Sound* landsound[4]; Sound* jumpsound[1]; public: Player(); virtual ~Player(); virtual void UpdateControls(); virtual void Update(); };

Setting Up the Player Class

The player.cpp file will contain all the logic and code for setting up FPS player mechanics. We start with a basic player class that contains a constructor, destructor, and two empty functions: #include "MyGame.h" using namespace Leadwerks; Player::Player() { } Player::~Player() { } void Player::UpdateControls() { } void Player::Update() { } Since the player class is a child of the node class it will inherit an entity member from its parent. In the player constructor we assign a value to this entity member with a call to Pivot::Create(). A pivot is an invisible entity with no special properties, it is essentially an instantiation of an empty entity: entity = Pivot::Create(); We now want to setup the player physics properties for the entity: entity->SetPhysicsMode(Entity::CharacterPhysics); entity->SetCollisionType(Collision::Character); entity->SetMass(10.0); And finally position the player at the origin: entity->SetPosition(0,0,0,true); With the code additions our player class will now look as such: #include "MyGame.h" using namespace Leadwerks; Player::Player() { //Create the entity entity = Pivot::Create(); //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() { } void Player::UpdateControls() { } //Update function void Player::Update() { }

Adding in a Camera

In a FPS the player's camera acts as the player's head, in that it should be positioned at a height directly above the player's shoulders and be restricted to normal human movements. For the player's height we will create and initialize three separate variables in the constructor: standheight=1.7; crouchheight=1.2; cameraheight = standheight; We then create the camera, position it to the height of a standing player, and narrow the camera's field of view: camera = Camera::Create(); camera->SetPosition(0,entity->GetPosition().y + cameraheight,0,true); camera->SetFOV(70); We also don't want to forget to deal with the camera when an instance of the player class gets deleted. so in the destructor we add in: if (camera) { camera->Release(); camera = NULL; } After these changes the player class will now look like this: #include "MyGame.h" using namespace Leadwerks; Player::Player() { //Create the entity entity = Pivot::Create(); //Initialize values standheight=1.7; crouchheight=1.2; cameraheight = standheight; //Create the player camera camera = Camera::Create(); camera->SetPosition(0,entity->GetPosition().y + cameraheight,0,true); camera->SetFOV(70); //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() { } //Update function void Player::Update() { } Up next, we'll talk about movement with keyboard input.
Cancel Save
0 Likes 7 Comments

Comments

rozz666

I see several problem with the Player class:

1) The class is too big. It has 29 members including arrays! It has too many responsibilities: mouse speed, camera, physics, sound, etc.

2) Manual memory management. std::unique_ptr (or boost::scoped_ptr) would be a lot better.

3) It creates its own dependencies instead of using Dependency Injection. It creates coupling an block the possibility of unit testing the class.

4) The meaning of the last parameter of entity->SetPosition is not clear.

5) Not all members are initialized in the constructor.

6) Constructor is not exception safe (assuming ::Create can throw std::bad_alloc).

Such code smells in an article can teach people really bad habbits.

May 22, 2013 08:34 PM
0r0d

I see several problem with the Player class:

1) The class is too big. It has 29 members including arrays! It has too many responsibilities: mouse speed, camera, physics, sound, etc.

2) Manual memory management. std::unique_ptr (or boost::scoped_ptr) would be a lot better.

3) It creates its own dependencies instead of using Dependency Injection. It creates coupling an block the possibility of unit testing the class.

4) The meaning of the last parameter of entity->SetPosition is not clear.

5) Not all members are initialized in the constructor.

6) Constructor is not exception safe (assuming ::Create can throw std::bad_alloc).

Such code smells in an article can teach people really bad habbits.

I agree with most of this, and there's other issues with the above code... starting with the fact that anyone who doesnt use camelCase is not really interested in code readability, which makes me not interested in reading their code.

Dont really care about the exception safety though. I've never worked at a game studio where exception handling was even considered.

May 23, 2013 01:54 AM
ChrisVossen

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)

May 23, 2013 09:41 PM
0r0d

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.

I can definitely understand the motivation to make the tutorial easier to understand by coalescing code into a single object, but at some point it goes too far. For example, having all the camera stuff in the player class is really bad form, teaches bad practices, and will lead to dirtier code in your tutorial later on. It could be fixed by adding a separate class to control the camera without really much total added complexity. The complexity you add by having one more class gets balanced by reducing the complexity of the player class. Remember that smaller, more focused classes are easier to explain and understand.

Right now when someone looks at your player class they see a dauntingly large and complex monolith that incorporates all kinds of functionality. You cant just tell someone "the Player class represents a player"... because it doesnt. Now you've made more work for yourself in the tutorial because you have to explain why a thing doesnt do what it claims to do. And, when you add more stuff, it will presumably go right into the Player class, making things even worse.

May 24, 2013 02:50 AM
Dave Hunt

Since subsequent articles that make use of the Player class have been approved, I've decided to mark this one as peer reviewed as well.

I would like to see some future attention to better separation of functionality, but any changes to the Player class would likely have large impacts to subsequent articles that have already been approved.

August 19, 2013 04:41 PM
LoneDwarf

My god people. Does this mean Chris won't be getting paid? It's a wonder people even bother write anything with this kind of criticism.

May 11, 2015 01:27 PM
LoneDwarf

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

That is what I am seeing at the bottom of the page. The comments I am seeing are just uncalled for. Wasn't going to bother saying more until I saw the comment from d4n1. My grammar and spelling is terrible so I usually don't judge but I have no clue what you are saying. Don't criticize an article if you can't even write a clear comment. My god man, ever heard of a period :)

Rozz666 said the class was too long. I can't agree. You do realize it's a tutorial right? There are plenty of open source FPS on the net people can download and browse the code if they want. A tutorial should be stripped down and only include relevant material. It should be simple and very bare bones.

I use arrays all the time? WTF?

Manual memory management is exactly what I would expect. Why add smart pointers to the mix? Lots of people don't use them or don't know how to. Not relevant to the tutorial.

Good god man you're really going to complain about dependencies in a tutorial? Really?

The SetPosition is the only comment I can agree with.

Not all members are initialized in the constructor. Who cares? It takes less code and fits on the browser page better. I can see your point here as it's somewhat of a rule I follow. Again it's a tutorial and I would lean towards having the code stripped down.

Constructor is not exception safe. Again, who cares? You're pretty much screwed if you get a bad alloc. Did I mention this is a tutorial? Think about how you would handle a bad alloc if there was a member function that did the create. You're still screwed and will likely shutdown after showing the error message. Handling this case isn't trivial.

At this point I feel I am starting to be as bad as the people that are criticizing but it got my goat. These criticisms smell of '"let's see what I can pick at that makes me look smart". The guy took time to write it and shouldn't have to put up with this.

May 20, 2015 02:06 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement