Blog

Making a prototype game.

As part of our final assignment, we have to develop a game using the engine system component that we have developed and also use a component that another student has developed. So, to make this game along with my component, I am planning on using the sound system component made by my friend Shantanu Pandey.

Game Description:

I am planning on making a simple car racing game, in which you will be racing against AI cars in a race track. You can use both controllers and keyboard to move the car. The game ends after 3 laps around the course. There will be only one map. There will not be any collisions for the first version of the game, but might add them later.

I am planning to have a background music in game along with sounds for acceleration and deceleration using the sound system. I am still integrating the system, but I haven’t had any problems until now.

I will be adding pictures and link to download as I develop this game.

Engine Feature – Final Update

This is the final part of my Engine feature series. Previous posts are here and here. My main focus this week is to make my library fast and stable, with less bugs. Most of the code changes I’ve done are under the hood.

The major update this week is the support for specifying the trigger and stick deflection dead zones both in Lua and the launcher. I also added support for multiple controller remapping in the launcher.

Adding multiple controller and dead zone support was not much of a work, but i had to think about the way i have to arrange my binary data, so that the file size is not large and it is also easy to retrieve data from it. Since reading binary data is reading from the memory and incrementing the pointer to the next address to read that data, we can inadvertently access data that might not belong to the file. Also, in the case of deadzones, if the player does not enter any data, we do not write anything to the file, but we must have some way of checking that while reading, otherwise we would be reading garbage values. Keeping in mind these things, i’ve decided to add the controller number in front of the controller data and have 1 byte before each deadzone to store where there is data or not in that.

The 0 above yellow line is the controller number and the 0’s above red line indicate that there is no dead zone data.

The updated launcher looks like below.

What the project does:

The project allows you to

  1. Check when a button is pressed on a controller.
  2. Get the actual and normalized deflections for both triggers and sticks
  3. Register and deregister functions that are assigned to specific buttons, which are called when that button is pressed.
  4. Set vibration effects for the motors in the buttons.

How to use the library:

    1. Download the project files for controller input and launcher.
    2. Add code to initialize and clean up the controller input in cbApplication.cpp
    3.  Include the header “Engine/ControllerInput/ControllerInput.h”
    4. All the functions are present in eae6320::UserInput::ControllerInput  namespace
    5. All the functions require passing of the controller keys which are an enum.
    6. Some examples of using the library
if (GetNormalizedStickDeflection(ControllerKeyCodes::LEFT_STICK, 0).x != 0)
	{
		m_Camera->SetAngularSpeed(GetNormalizedStickDeflection(ControllerKeyCodes::LEFT_STICK, 0).x);
	}
	if (IsKeyPressed(ControllerKeyCodes::RIGHT_TRIGGER))
	{
		m_Camera->SetCameraVelocity(Math::sVector(0, GetNormalizedTriggerDeflection(ControllerKeyCodes::RIGHT_TRIGGER), 0));
	}
	if (IsKeyPressed(ControllerKeyCodes::LEFT_TRIGGER))
	{
		m_Camera->SetCameraVelocity(Math::sVector(0, -GetNormalizedTriggerDeflection(ControllerKeyCodes::LEFT_TRIGGER), 0));
	}
    1. To register and deregister functions:
RegisterFunctionForCallback(ControllerKeyCodes::B, [this] {return Test(); }, 0);
RemoveKeyFromCallback(ControllerKeyCodes::B);

  1. To Use the launcher:
    1. Add it as a build dependency to the Game project so that it will build before the game does in the same folder.
    2. If your game name is different from “MyGame.exe”, you need to change it in “MainWindow.xaml.cs” at this line
    3. gameProcess.StartInfo.FileName = "MyGame.exe";
  2. Enjoy!!
  3. Contact me at my email if you encounter any bugs!

What I’ve learned from this project/What I have used in this project that I have learned over the past 6 months:

  1. Creating platform independent interfaces for others to use.
  2. Working with a third party  library. In this case XInput.
  3.  Working with both Lua and binary files.
  4.  Working on binary files in C# and WPF and calling the game from the launcher.

What gave me lot of trouble ;(

  1. Working with function pointers and std::function to use with the callback  feature.
  2. Creating separate paths for different builds in WPF. Since WPF by default builds in a platform agnostic way. I had to make a lot of changes to the project settings to get the launcher to build in the same folder as the game.

Where to go from here:

I am planning to continue working on this project in the future. Since me and hopefully some others in my class have to use this project in the upcoming tech demo game, I might encounter some bugs which i will be patching and updating here. I am also planning on continuing the work i was doing with audio during the winter break whose progress I will updating in future blog posts.

Downloads:

The code provided below is released under the 2-Clause BSD license.

ControllerProject MyGame_SampleController

Engine feature – Progress Update – 2

Updated to the engine feature that I’m developing, detailed here.

As discussed in my previous post , I’ve completed the work on the basic features of my engine and started working on stretch goals and overall making the library better.

Updates for this week:

  1. Button remapping now works with both Lua and Binary files

Users can specify which buttons are to be mapped to which other buttons in the settings.ini file that is present in the game directory. The remapping syntax looks like the following. I had to make sure that it is still easily accessible to the users, while being fast to read in code.

I also created a launcher application in WPF using C#. Which shows a GUI for remapping buttons. The application then writes out a binary file with the new mappings when the user selects OK or keeps the current mappings when cancelled and then opens the game.

My library checks for the existence of both the lua and the binary files and compares the timestamps for these files and opens the file which has the latest edited timestamp. My launcher currently supports remapping of one controller, where as the Lua file supports remapping of all four controllers.

2. Support for function callbacks of subscribed functions.

Users can register functions which are to be called when a particular button is pressed. This is especially useful for buttons such as menu, since the game does not have to check for button press on every update. The library provides functions to register and de-register function callbacks. All the buttons and sticks are supported along with multiple controller support. The register function accepts a function pointer to the required callback function. The syntax looks like the following.

And registering a function looks like the following.

All the callbacks happen asynchronously using std::async. Hence the library does not expect any return values from the callback functions. I have worked with asynchronous calls before using C#, but this is the first time i used it in C++.

Since i have to check the controller state for every tick to check for changes, instead of calling my update function in the core application thread, I have the core update function of my library running on a new thread and use a while loop to continuously check for changes in controller state. Creating a new thread, helps in not bogging down the application thread and better check for changes.

I am using windows specific “CreateThread” function to create thread, and wait for the thread to exit safely before closing the application instead of using “ExitThread“, since it is preferred to return from the function than using ExitThread for C++ applications as discussed in the article.

 

Things I’m planning on working for next week:

  1. Polish the library
  2. New “really far” Stretch goal: Create a sound library to playback audio from the controller’s headphone jack.

Engine feature – Progress Update -1

Posting updates on the Engine feature that I’m adding, as discussed here

Updates on features:

  1. Able to check when a button is pressed.

Completed. The user can check which buttons are pressed including the triggers.

  1. Get the amount of deflection of triggers and sticks.

Completed. The used can get the actual and normalized (between 0 and 1) values of deflection for both the triggers and sticks.

  1. Built in support for dead zones.

Completed. Dead zones are supported for both triggers and sticks.

  1. Support for haptic vibration. Every xbox 360 controller contains 2 vibration motors. Support to set vibration levels for each individual motors.

Completed. User can set individual vibration speeds for the motors or set the same speed for both.

You can see the interfaces for the above features in the below screenshot:

Updates on stretch goals:

  1. Support for multiple controllers. XInput API supports upto four controllers.

Completed. The functions that are used to get the above data also accept an optional controller number which can be between 0 and 3 and defaults to 0. The library also polls the XInput API for new controllers every 5 seconds.

  1. Support for button remapping using Lua/Binary files.

Working on this feature now. Also planning on adding support to define custom dead zones in the Lua files. Expecting to have a working version of this feature by next week.

  1. Support for pub-sub for events, where instead of the game polling for button presses, the engine will raise an event and call a specific function when the button is pressed.

Yet to start working on this feature. Expecting to finish a basic version of this feature by next week.

Things I’m planning on doing this week:

  1. Have a working version of Lua/Binary file remapping.
  2. Have a working version of event system.
  3. Make the library more performant, by identifying bottlenecks.

Learnings:

This is my first time working with low level APIs and it has been pretty straightforward until now. The XInput API has well written documentation that made writing the code easier. I haven’t faced any roadblocks for this version. But as I develop this module, I expect a few to present themselves on the way.

My main goal in creating this library is to create something that I want to use myself in a game, hence most of the features that I am implementing are those that I have used in the games. For example, having multiple controller support which can be used for local multiplayer. To support this all functions accept a controller input value that defaults to the first controller if the developer does not want to support multiple ones.

Game Engineering – Adding New feature to Engine

Engine Feature Proposal

I am planning on implementing XInput in the engine, so that the engine can support Xbox controllers. It will have the following features

  1. Able to check when a button is pressed.
  2. Get the amount of deflection of triggers and sticks.
  3. Built in support for dead zones.
  4. Support for haptic vibration. Every xbox 360 controller contains 2 vibration motors. Support to set vibration levels for each individual motor.

This information is provided through functions. There will be an enumeration of all the button present on the controller. Functions which poll for a specific button or trigger require the game to send the enum value as a function parameter. There will be no need to create data files in the first iteration of the feature (See stretch goals).

Implementation Details:

I am planning on creating an platform independent interface which exposes all these functionalities as functions. Since XInput is windows specific API, the windows specific code will be hidden behind platform independent functions which will be public. There will not be any data that will be written to files and then read from them for the first version of implementation. So the user will have a set of functions that they can call to get the data from the library.

Stretch Goals:

  1. Support for multiple controllers. XInput API supports upto four controllers.
  2. Support for button remapping using Lua/Binary files
  3. Support for pub-sub for events, where instead of the game polling for button presses, the engine will raise an event and call a specific function when the button is pressed.

Game Engineering – Building Game Engine – Part 9

Creating Human readable and Binary Files for Effects

As discussed in the previous posts, having a human readable file to edit data and having a binary file to load that data is more useful, compared to hardcoding the data in code. In this assignment, we have to create a Human readable format for effects and then convert it into binary format during build time and then load the required data from the binary file during runtime.

Human Readable effect file:

The file contains the locations to vertex and fragment shader and the type of Render state which can be “AlphaTransparency”, “DepthBuffering” or “DrawBothTriangleSides”. The file looks

return
{
Effect =
{
VertexShaderLocation = "Shaders/Vertex/standard.shader",
FragmentShaderLocation = "Shaders/Fragment/standard.shader",
RenderState = "DepthBuffering",
},
}

 

Binary File:

The binary file saves the above information, but also saves the length of the path to Vertex Shader. I am storing this value to find where the path to fragment shader starts in the binary file. If I do not include that info, I have to iterate through every byte to check where the null termination character is and then point the next byte as fragment shader location. I am also adding a null terminator after the vertex and fragment shader locations, so that we can directly load the values as strings.

The input shaders are stored to our $(GameInstallDir)/data folder, but our working directory for game is the $(GameInstallDir) folder. I Chose to append “data/” in front of the file names, so that we do not have to add that during runtime. The upside of this method is that it is faster to load the paths during runtime, while the downside is increased file size. I think the improvements in load speeds offset the increase in file size, hence I included those.

 

The red rectangle is the Renderstate. The blue one is the length of path for vertex shader and the violet rectangles show the null terminators after each path.

Size

RenderState = uint8_t = 1Byte

Length of Path = uint8_t = 1Byte

Vertex Shader Path = 35 characters = 35 Bytes

null character = 1 Byte

Fragment Shader Path = 37 Characters  = 37 Bytes

null character = 1 Byte

Total = 76 Bytes

Extracting data:

The above screenshot shows the code to extract data from the file. First we are extracting renderstate, length of vertex path, the vertex path. To know where the path to fragment shader starts. We add the length of vertex path plus 1, to account for the null termination character, to the offset.

 

Since most code is changed in the backend, there are no visible changes to the output.

 

Output:

Downloads:

MyGame_x64_1101 MyGame_x86_1101

Game Engineering – Building Game Engine – Part 8

Converting Maya exported files to Binary files for loading into the game.

This assignment is focused on creating a binary mesh format and converting the existing mesh files which are exported from Maya in our file format to this binary file format during the build time, so that we can load the binary files during runtime.

Advantages of Binary Files over Human Readable files.

  1. Binary files are smaller compared to human readable files: The size taken by binary files on the disk is many times smaller than comparative human readable files. This difference can be huge when the source human readable file is very big. Below is the table outlining the difference in sizes between binary and human readable files.
Name Human Readable File Size Binary file size Difference
Plane 459 Bytes 80 Bytes 379 Bytes
Donut 154KB 29.6KB 124.4KB
Gas Can 1.37MB 262KB 1108 KB
Lambo 2.12MB 403KB 1717 KB
Helix (Index count 62436) 4.15MB 772KB 3378 KB

 

  1. Faster load and read times from Disk : Since binary files are small and contains information in binary format, the time taken to load the file and the time taken to read the file after loading are very small compared to Human readable files where we are using Lua to convert data from the file into binary format to use in the game.
Name Time to load and read in Lua Time to load and read binary files Difference ( in Seconds)
Plane 0.002427 0.000095 0.002332
Chair 0.006533 0.000081 0.006452
Helix 0.117267 0.000528 0.116739

 

As seen from the data above, there is a small but considerable difference between load times using Lua and binary files even for simple meshes like a plane. The time taken to load the Human readable mesh also increases with increasing mesh complexity, but the times for binary files are still considerably less.

Building different Mesh files for different Platforms:

As discussed in previous posts, Direct3D and OpenGL use opposing winding order to render a given triangle. Since its easier to have a common winding order across the engine, mine uses the OpenGL one as the default and until now I have been switching the winding order when initializing the mesh. Even though this process still works for binary files, to take the advantage of the gain in speed, I moved the part of switching the winding order to the place where I am building the binary file. This allows me to have two different binary files for the different platforms and I do not have to worry about switching the winding order during loading the mesh.

Order of data in Binary files:

The above picture shows the plane mesh in the Binary file format. The red highlighted bytes are the Index Count, the purple is the Vertex Count, Yellow is the Index array and Blue is Vertex Array. The light blue underlined part is the vertex positions in the vertex array and the brown underlined one is the position of color data of a vertex.

Sizes of different parts :

Index count: uint16_t = 2 Bytes

Vertex Count: uint16_t = 2 Bytes

Index Array: each is uint16_t = 2Bytes * number of indices

Vertex Array: each is 3 * floats for position + 4 * uint8_t for Color = 16 Bytes * Number of vertices

Extraction of data:

The data is extracted in the same order as the file format.

To extract Index and Vertex arrays, we just have to create pointer to the correct location from where the corresponding array starts, since the data is already binary and is loaded in memory.

Optional Challenges:

  1. Using a different file extension for the binary files: To differentiate the Human readable files from the binary files, I changed the extension of my binary files to “.meshbinary”.
  2. Loading a recognizable object: I am using a Gas can and a Chair which I have used in my previous assignment for this one. Meshes are courtesy of Chris.

 

Final game output:

Downloads:

MyGame_x86_1024 MyGame_x64_1024

Game Engineering – Building Game Engine – Part 7

Importing Meshes from Maya

Maya is one of the most common software used to create 3D models which are to be put in games. Maya also provides documentation to create plugins to extend the capabilities of the software. In this project, we are creating a maya plugin to export the mesh files in the file format that can be read by our game engine as described in the previous blog post here.

After downloading and setting up the Maya SDK, we have to enable the plugin in Maya to be able to export to our format. One we export to our format, we have to make sure that our engine builds the mesh files and then specify that this the particular mesh we have to use. The file to which we are exporting the data only contains the vertex and index information and comments which show which vertex is at what index. Apart from that we are not importing anything from maya since, the file should be human readable and ediatable and if we include parameters such as normals which we are not using in our game, it would create confusion when someone opens the file.

Output:

Importing Color: First we need to change our maya importer to import color to our files. Then we need to change code in lua, so as to import color to our vertex buffers and after importing, we need to tell both OpenGL and Direct3D that we are adding color to our vertices. After this is done, the output should look like the following.

 

Debugging Maya: Maya also provides debugging symbols (.pdbs) along with the SDK. This allows us to debug the Maya importer we built. The way we do it is to run Maya first and in visual studio, attach the process  to debug by going to ‘Debug->Attach To Process’.

The above screenshot shows Visual Studio breakpoint when first loading the plugin we build in Maya.

The maya mesh  importer is a standalone project which does not depend on any other projects. Hence no need to add references to other projects or add the maya mes importer as reference to other projects.

Opening meshes with a greater number of vertices:

Currently we are storing the total number of vertices that can be in our game in a uint16_t which can hold upto a maximum of 65535 vertices. If the count exceeds this, it would overflow. To mitigate this, we are limiting our vertex count to this value and if a mesh exceeds that, we will not render that mesh.

Controls

  1. WASD to move the Donut
  2. IJKL to move the Plane
  3. F3 to swap the Donut to a Chair
  4. Left and Right arrow to move the camera in X axis.
  5. Up and Down arrows to move the camera in Z axis.
  6. Z and X to rotate the camera around itself
  7. Ctrl and Alt to move camera along the Y axis

Thanks to Chris Cheung for providing me with some Maya meshes like the chair that I could then import into my game. You can find more of his word at his artstation.

Downloads:

MyGame_x86_1017 MyGame_x64_1017

 

Game Engineering – Building Game Engine – Part 6

Creating a human readable mesh file

Till this point in the class, we were submitting meshes from the code as vertex and index buffer arrays in the game. Even though this method is better compared to when we were submitting the data in the engine, this has its drawbacks. Chief among them is we have to modify the code every time the meshes have to change. Having meshes load from a separate file removes the need for that, as we can load the vertex and index buffer from the file and render the mesh. There can be two different approaches to how the data is stored in files, storing them as binary files or storing in a human readable format.

Storing the files in a binary format can be very beneficial as loading binary files is considerably faster compared to loading plain text files. The downside is that they are not human readable and there is no easy way of editing binary files. The second is having them stored in a human readable format. The main advantage of such files is that they can be easily editable. The downside of this is that they can be slow to load.

Example Mesh File:

return
{
	VertexBuffer =
	{
		{
			Position = {0,0.5,0},
			Color = {0,0,0,0},
		},
		{
			Position = {1,0.5,0},
			Color = {0,0,0,0},
		},
		{
			Position = {0.5,1,0},
			Color = {0,0,0,0},
		},
		{
			Position = {0,-0.5,0},
			Color = {0,0,0,0},
		},
		{
			Position = {1,-0.5,0},
			Color = {0,0,0,0},
		},
	},
	IndexBuffer = {1,0,3,4,1,3},
}

The above code shows a mesh file. It has two parts the vertex buffer and index buffer. Each table in the vertex buffer represents one vertex. Each vertex contains a position and color for that position. The position array is contains the x,y,z coordinates in that order. The color has r,g,b and alpha in the that order. Since these files will be used by people with basic knowledge about the arrangement of position and color, they do not have labels associated with them while still being readable. The color attribute is currently not being used but has been added to future proof the mesh format.

Handles:

The file is processed with lua and the data is fed into the existing mesh class to create a mesh. Instead of creating a new mesh every time the game wants, we implemented a handle system. In this system, the mesh class contains a manager which tracks all the meshes being created and the game gets a handle to a mesh instead of the mesh itself. Whenever the game wants to use the mesh, it asks for the mesh using the handle and the manager will either create the mesh or return the mesh if it has been already created.

Ouput:

Even though visually the previous output and the current one look almost the same (hello again tree), there have been significant changes under the hood

Controls:

  1. WASD to move the rectangle
  2. IJKL to move the triangle
  3. F2 to swap the effects
  4. F3 to swap the triangle to a house
  5. Left and Right arrow to move the camera in X axis.
  6. Up and Down arrows to move the camera in Z axis.
  7. Z and X to rotate the camera around itself
  8. Ctrl and Alt to move camera along the Y axis

Downloads:

MyGame_x64_1003MyGame_x86_1003

Game Engineering – Building Game Engine – Part 5

Adding a representation for camera
A camera is an object which is used to specify the view that has to be shown on screen. A game can have multiple cameras and the game developer must be able to specify which camera is used to specify a particular frame and the engine must be able to render to the screen   the view from the perspective of that camera. To achieve this, we first have to convert the world space to the camera space and then map the camera space to the screen or perspective space. These are matrices which are stored in the per frame constant buffer. The vertex shader then takes the information that is in these constant buffers to render on screen.
We also added movement to camera to move around the world space in the X and Z directions.
Adding a representation from a game object.
A game object can be thought as the basic element to represent an object in the game. A game object can be anything in the game, but it has certain properties. My implementation of the game object has a rigidbody to keep track of the position, rotation and apply forces. It also contains which mesh it is made of and which effect is to be used to draw the mesh.

The class also contains member functions to set the position, rotation and also to update the mesh and the effect that the game object uses. Instead of storing the mesh and effect separately, I am using a struct which contains a mesh and effect pair.

Submitting Meshes to render
Renderers are low level engine components and we do not want to let the low level engine components know about the high level ones such as the game objects. Since meshes are now part of the game object, when we need to render a mesh we can either send the whole game object to the renderer or we can just send the details that are necessary for the renderer which are the effect and the mesh.
But since we are moving from rendering in a 2D space to a 3D space, we need some way to translate the local co ordinate space to the world co-ordinates. We can do this by creating a translation matrix using the orientation and the position of the game object. So, my final interface to submit mesh effect pairs looks like
1. void eae6320::Graphics::SetEffectsAndMeshesToRender(sEffectsAndMeshesToRender i_EffectsAndMeshes[eae6320::Graphics::m_maxNumberofMeshesAndEffects], eae6320::Math::cMatrix_transformation i_LocaltoWorldTransforms[m_maxNumberofMeshesAndEffects], unsigned int i_NumberOfEffectsAndMeshesToRender)

To move the objects around the world, we need to have a constant buffer which can be updated every draw call instead of every frame. We also need to store the data corresponding to this constant buffer, which stores the local to world transformation of every game object in the scene. After integrating this constant per draw call data. The size of my struct which stores data required render a frame increased to 1768 Bytes in OpenGL and 1936 Bytes in Direct3D.

Movement of camera and Game objects and the importance of Prediction:

After adding movement to both camera and various game objects in the scene, I noticed that the movement appeared jerky. This is because the simulation is running at a much faster rate compared to the game. To smoothen the movement, we have to predict where the camera or the game object would be in the based on the velocity and the time and update render the predicted position instead of the actual position. It is also due to this reason that we update the velocity of the body instead of directly manipulating the position.

Key Bindings:

1. WASD to move the rectangle
2. IJKL to move the triangle
3. F2 to swap the effects
4. F3 to swap the triangle to a house
5. Left and Right arrow to move the camera in X axis.
6. Up and Down arrows to move the camera in Z axis.


Final Output:

MyGame_x64       MyGame_x86