In this tutorial, we will be covering the basics of how to use C++ to create a usable and controllable first-person player (FPP). More specifically, we will be creating a "Player Component" that can be configured directly within CRYENGINE by changing values such as the player’s height, walking speed, rotation speed, etc.
This "Player Component" will be written entirely in C++, and while we will be using either a First Person Shooter or Third Person Shooter project template for the animations, we will be building the movements ourselves from scratch. The player itself is comprised of four main components; the Camera, the Input, the Physics and the Animation. By the time you're done, your player character will have:
- A simple capsule collision model
- Basic WASD controls
- A mouse-look camera controller
Whether you are familiar with C++ or not, by the end of the lesson you will have learned valuable C++ basics while also introducing a simple character into your project, one which you can modify and reuse in any project.
- CRYENGINE 5.7 LTS
- Visual Studio (2017, 2019 or 2020 work just fine) or any other IDE or code editor of your choice
Before adding any kind of movement to our player, we first need to set a few things up; the most crucial being to create a Player that we can apply movement too. This Player is simply a combination of C++ based components (camera, mesh, inputs, velocity), that together become the foundation of a playable character. To begin, we first need to create these components, learn where they belong, and understand how this all works together with CRYENGINE.
Creating a New Project
To get started, the first step is to create a project. While the templates we will use contain player controls, we will be removing all of the code and building our own player with customizable variables, while preserving the animations that come with the character.
Begin by creating a new project in CRYENGINE 5.7 LTS. For a template, select either the Third Person Shooter or First Person Shooter template.
For more information on how to create a project, please see Creating, Importing & Upgrading Projects.
Make sure you select the CPP and not C# template, as we will be using C++.
Name your project and open it in CRYENGINE 5.7 LTS Sandbox. Upon the project loading, immediately close the Sandbox Editor. For this tutorial, we will mostly be working outside of the Sandbox Editor.
Open the CRYENGINE Launcher and navigate to the Projects tab. Once there, click the icon corresponding to our newly-created project and select Reveal in Explorer.If you're confused about the Launcher interface, please see CRYENGINE Launcher Reference.
Within our Project’s root directory, go to CRYENGINE Projects/<YourProjectName>/Code/Components and delete bullet.h, SpawnPoint.cpp and SpawnPoint.h - we will not be needing them.
The only files remaining within the Components folder should be Player.h and Player.cpp, which is where the bulk of our work will take place. While we will not delete these files, we will modify their contents later when we generate our solution.
Next, right-click the .cryproject file corresponding to the project we just created, and select Generate Solution from the dropdown menu.
The Generate Solution option
This will create a new folder named Solutions within your project’s root directory, where our "solution" file (.sln), will reside. The solution file is a database of all of the game’s .cpp and .h files that we can modify.
Within our new Solutions folder is a folder called win64. It contains quite a few files, but all you need to do is open the Game.sln file in your preferred version of Visual Studio, through which we can directly modify these .cpp and .h files.
Game.sln file in the win64 folder
From the CRYENGINE Launcher, you can also click the button corresponding to your project and select Open in Visual Studio to open this same file.
Cleaning up GamePlugin.cpp and GamePlugin.h
Now that we've generated our solution, we must go into it and remove certain lines of code that are included in the First/Third Person Shooter templates but which will not be needed since they cause bloat and are subject to removal; this is to ensure that there are no conflicts later on.
We will now remove unnecessary lines, beginning with those which relate mainly to networking.
Once Game.sln is open in Visual Studio, in the Solution Explorer, navigate to Project>Game>GamePlugin.cpp to begin. Proceed as indicated in the collapsible section below:GamePlugin.cpp
First at the top, delete:This line is used for networking, to get a reference to the player; it is not needed for our player.
Next, go into the destructor member section and remove the following:
Next, delete the network lines under
Post Initialization, as they are also networking lines:
ESYSTEM_EVENT_LEVEL_UNLOAD, delete the line:
Lastly for our GamePlugin.cpp, there are four functions at the end which can all be removed, again, since they relate to networking. The lines to delete are as follows:
That is all for cleaning up Gameplugin.cpp. Press Ctrl + S to save the progress, and now load up GamePlugin.h. Proceed as indicated in the collapsible section below:GamePlugin.h
As with GamePlugin.cpp, we will begin by removing lines relevant to networking. Delete the line:
Since we won’t be accessing the player from our GamePlugin.cpp, delete the existing
Player Componentclass line:
Delete the line:
INetworkClientListeningthere are several lines which need to be deleted:
For GamePlugin.h, delete the player map, which is the only line under the
- Finally, press Ctrl+ Shift + S to save the open GamePlugin.h and GamePlugin.cpp files.
Trying to build this will result in an error, so we now need to modify Player.cpp and Player.h.
Cleaning up Player.cpp and Player.h
Again, we need to remove not only the references to networking, but also the lines that we just removed from GamePlugin.cpp and GamePlugin.h.
Using the Solution Explorer, open Player.cpp. Proceed as indicated in the collapsible section below:Player.cpp
To start, we can delete these
#includelines at the top of our Player.cpp:
Delete everything below and including the following line:
Save your progress with Ctrl + Shift + S, and open Player.h. Proceed as indicated in the collapsible section below:Player.h
Delete line 111 and every line below it, which corresponds to the implementation of pure virtual methods introduced by
Delete the following networking lines:
Lastly, we have lines for
class, which also relate to networking and input flags, and can be deleted:
#includelines within Player.h:
And finally, delete the
- Press Ctrl + Shift + S to save your progress across all tabs, and Ctrl + Shift + B to build. You should receive the following error:
Unrecognized class error
The reason for this error is that it does not recognize the class, and we need to include it. To fix this, add this line to your Player.cpp:
- Save your progress and build your solution by pressing Ctrl + Shift + B. No errors should pop-up this time.
At this point, GamePlugin.cpp and GamePlugin.h are complete, and Player.cpp and Player.h are also ready to be built upon. You can now close the GamePlugin.cpp and GamePlugin.h as we will no longer be touching these.
Creating a Player
Now that we have removed the lines which we won’t be needing in our Game solution, we can start to add lines that will serve as the base interface for our playable character – we call these Components.
In Player.h, we will start forming our header.
It is common practice in headers to have a
public section at the top for your publicly-accessible members, a
protected section below for your inherited members, and a
private section last for member variables or functions you don’t want anything else to access.
Public, protected and private sections
Start by adding the
You may notice that after adding this line, our
CCameraComponentis underlined in red. That is because we need to add the relevant
#includelines at the top of the header:
We will follow these steps for each component. Next up, we will add our
CInputComponentby including this line underneath the
Now add the
#includeline corresponding to our
Next, add the
And the relevant
The last component we will add is the
Followed by the last
The final list of added Components with the corresponding #include lines
Adding Initialize to Components
A lot of things can get called throughout the run-time of a game, things like "Reset", "Awake", "GameplayStarted"; within our
IEntityComponent, which is the base interface of our current player. We can add a few of these, but first we need add an
Initialize, which is called at the very first initialization of the component: the creation time in game run-time.
Initialize is a great occasion to call upon some of the components, such that they are called upon immediately when the player is loaded in the game.
In order to use this method, we need to call upon the Initialize by adding this call in our Player.h:
- Since we declared it, we now need to define it in our Player.cpp. This is what the red underline in this newly added line is eluding to. Highlight and right-click
Initialize(), select Quick Actions and Refactorings, and select Create Definition of "Initialize" in Player.cpp.
Within the curly-brackets underneath our void
CPlayerComponent::Initialize, add these definitions for each of our components:
Now, when we add our
CPlayerComponentto an Entity in CRYENGINE, all of these components will be added to it.
Player Component - Adding Inputs
We will now add our Inputs, both keyboard and mouse, so that we can look and walk around as a player. We need to tell CRYENGINE what keys and mouse axes we want to use, and for what. Later on in the tutorial, we will tell CRYENGINE how we want these inputs to function and when to load them with EventFlags.
To get started, we can add Register Actions and Bind Actions to our added Input Component so that we can move.
First, in our Player.h, underneath the
privatesection, create the member variable:
Next in our Player.h, also underneath the
privatesection, we will also create a member variable and name it movementDelta:
- With our member variables defined in our header, we now need to go into Player.cpp and add both the
InitializeInput() in CPlayerComponent
Within the curly-brackets in our new
InitializeInput(), we will add our
RegisterActionline, and for the value at the end add our new movementaDelta member variable. The complete
Now that we have our
RegisterAction, we need something to call it: a key. This will be a
BindActionline, and we will add this right underneath our
RegisterAction,which will assign the W key as our forward movement input:
Now, we want to repeat this step for the rest of the keys we would like to register and bind, which would be the A, S, and D keys. For that, you can copy and paste the completed input line.
For each of the keys, change the name of the action and
eKIto the key you want to assign that action (ex. “movebackward” and “eKI_S”).
Another important difference for each of these input lines is that the
m_movementDeltawill now be
movementDelta.ydepending on the axis we want for the assigned key. (Right/left is X axis, forward/back is Y axis. While these changes will reflect the axis, we also want to change the value to assign which specific direction on that axis each key moves the player).
This can be set by changing the value to a negative. (ex. "moveforward’ is an =value on the Y axis, while ‘movebackward’ would be =-value.)
Once the keys are all added, add a
BindActionline for each of the mouse axes:
The complete input lines for the W, A, S and D keys.
The only difference between them, other than the names and key inputs, is the value is set to "-value"’, or negative value, so it provides the opposite m_movementDelta value.
Player Component – GetEventMask
After your inputs are in, we need to tell CRYENGINE which Events we want to use throughout game run-time, as well as what we would like to apply them to. We will be using
GameplayStarted, and applying it to our
Let's add the
publicmethod declarations that we will be adding to our Player.cpp later. Underneath the
publicsection of Player.h, add:
The added lines under the public section in Player.h
With these declared, we can now add the relevant lines into our Player.cpp. Within Player.cpp, underneath our inputs, create a new line and add the Events we will be using:
- The events we clarified we want to use are
Reset. So far we have only declared
Note that the events are separated by the “|” key, and not by the “:” or “;” keys which you may be used to seeingUnder
GameplayStartedwill be anything we want to get called on game start, under
Updatewill be anything we want called repeatedly and quickly, and under
Resetwe will call for certain variables to be reset upon quitting the game.
ProcessEvent – GameplayStarted
Now that we have specified the Events processed by the Component, we can begin to define how we want to implement them to give them purpose.
Before we add any of our Events, we need to create a
ProcessEventso that we can specify which actions should be done on a specific event for this instance of the Component. For this, we must clarify that we want to use
CPlayerComponentto process our Event by adding the following lines underneath the Event Masks in Player.cpp:
Then, within our
switchstatement, we can define our first Event, which is
GameplayStarted. We want to tell it to process our input via
(); at the start of our game. Within our
switch, we will add:
Defining GameplayStartedThese are the lines where we tell CRYENGINE which events we will be using, followed by adding this event and the details of what we want it to do, to call on our
();at the start of
CPlayerComponent, which is at the start of our game.
Preparing and finalizing the Player.h
Before we add anything to our
Update, we need to first create the members that we expect to be called within it. Things like player movement, look orientation, movement deltas and rotation speeds are a few of the members we will want to define.
Updatewill be the specific details on how our player moves through the world. While we defined what buttons we will press to do this, we still need to write the code on what actually happens when we trigger these inputs.
Add the full list of the members underneath the
privatesection in our Player.h (the names of these members can be whatever suits your intent):
The final list of what to add under the private section of Player.hWhile the names given to these members might tip off what we will be using them for, you may notice some are
Quat, some are
float. These are return types;
voidspecifies that the function doesn't return a value.
Quatis a quaternion rotation
Vec2represents a 2D vector.
Vec3represents a 3D vector.
floatdefines numeric values with floating decimal points.
privatesection updated in Player.h, the last thing we need to do is
AddMember. This will add our members to
CPlayerComponent, so that we can modify the values directly within CRYENGINE via the Properties tool when we add and select our Player Entity. Under the
ReflectTypein Player.h, add an
AddMemberline along with the name and a brief description for each of the members:
All the members in this list will be able to be modified directly in CRYENGINE by inputing custom values in the Properties of the Player Entity.
The final list of what to add under the private section of Player.h
Defining Update in Player.cpp
So far we have completed our Player.h, added all of the members we intend to use, and specified the first Event for Player.cpp. Now that we completed our Player.h and have seen how we can add Events, we will add the last two Events to our Player.cpp –
First, we want to add the case, and to specify that we want the Event to be updated. Add the following line:
Update, we want to add the
privatefunction which we intend to use. Anything under
Updatewill be called upon continuously throughout the game runtime, so makes sense for us to include our
It's easier to understand this code block if you notice that each
Ang3is defining one of the three axes – the
yawAngle, and the
With this setup, the camera will pivot up and down when looking up and down on the pitch axis independently and separate from the player’s body, but when looking left and right on the yaw axis, the player’s body will rotate with the camera. This is to prevent the player’s body from moving in strange ways when looking up or down by separating the
CCamerafrom the player only on. This is achieved by creating Matrix33 and Matrix34 functions named
finalCamMatrixand nullifying the
Note that the
CLAMPis only added to the
rotationAngle.y, since we only want to clamp the mouse when looking up or down.
Defining Reset in Player.cpp
Update, and now we need to add
Reset is what we want to reset to zero upon closing the game. Therefore, the members we want to reset in this case are
Start by adding the case line for
Within the curly-brackets, we can add the members we want to
Resetupon game close:
You may notice that the first two members are set to “
lookOrientationis set to “
IDENTITY" is zero rotation on all axis – which makes sense since
Quat, and so is on multiple axis.
Next, we want to make sure to reset the
camDefaultMatrixseen in the
pitchAngle,so that the camera is always reset.Skipping this step can result in issues such as spawning in and facing backwards.
To do this, add the relevant lines to the
- We want to end this last case with a break, and so our complete
Resetshould look like this:
Complete Reset section
Finalizing Player Movement
Our last step can be considered the missing link, as we have yet to define our player’s walking movement. To do this, we are going to call upon the
private function that we added in our Player.h, and define it with
First add the
CPlayerComponentby adding the line:
Within the curly-brackets of
(), we need to add the
Vec3for our velocity that will equal our movement by adding:
Next we need to normalize the movement. In older games, pressing “forward” and “left” or “right” at the same time would result in an overall higher running speed, as it would combine both of the movement values at the same time. With a normalize line added, it will do vector math to calculate the average speed when you press two movement keys at once, making it impossible to go faster than the movement speed you set for those keys.
The last line we will add to
Last line in PlayerMovement
- To finish everything off, press Ctrl + Shift + S to save all tabs, and Ctrl + Shift + B to build the completed solution.
The completed Player Movement section
Testing the Character
Once the solution is built, we can now test the character in CRYENGINE. Begin by launching the Sandbox Editor directly from Visual Studio:
- By default, clicking the Local Windows Debugger button at the top will launch our project in the Game Executable Launcher. We want to change this so that when the project is launched from Visual Studio, it runs CRYENGINE Sandbox in Editor Mode. To do this, go to the Solution Explorer and right-click on Editor → Set as StartUp Project.
Defining the Sandbox Editor as the default project launch application
- Once that is done, click Windows Local Debugger and launch the project.
- After opening a level, navigate to the Create Object panel and place an Empty Entity in your scene. For clarity, name it something like "Player".
- Within the properties of this new “Player” Entity, click +Add Component and select the CPlayerComponent that should now be available.
CPlayerComponent Entity Component properties
With the CPlayerComponent added to our “Player” Entity, you can now configure different variables such as walking speed, camera position, rotation speed, etc.
Player Movement Speed: Values between 3-5 correspond to a reasonable walking speed.
Player Rotation Speed: Rotation speed is very sensitive, so you should set it to a low value such as 0.002.
Camera Default Position: Modify the Camera’s height (position in the Z axis) to reflect that of the average human height. Any value between 1.5 and 2 can fit this criteria.
Camera Pitch Max/Min: Set the camera clamping to 1.5 (Max) and -0.85 (Min) - these values limit how far the player character can tilt its head back or forwards.
With these variables set for your Player Entity’s CPlayerComponent properties, you can now press Ctrl + G and test the game with your very own playable character.
This concludes the tutorial. To learn more about C++ in CRYENGINE and/or other topics, please refer to the CRYENGINE V Manual.
You can also follow this tutorial series in video form on our YouTube channel:
- No labels