GPiC++Stf Chapter 3 Part 4: Coding and Commenting The Engine and Project


Hello everybody, Earth Otherwise here to continue our journey into Game Programming in C++: Start to Finish by Erik Yuzwa.

A note before we begin. If you encounter errors while compiling what we're about to write then double check that you have correctly spelled everything in you options and code and that you have not used the wrong punctuation. It can be quite frustrating to realize that your problems are caused by a comma or the wrong capitalization.

For example, in an earlier video I used commas instead of semi-colons in the additional dependencies of my executable project. I addressed this in the addendum, but I'll also address it here.

Also, the subsystem in the system tab of our linker should be set to console for debug mode, but not release mode. And an additional problem is that I forgot to mention that you have to include your game engine directory in your include directories to use any of the files we're going to be making in our game engine.

Refer back to our UML document to remind us of what we're going to write. Recall that last time we created two different programs, one static library which is our game engine, mine called Otherwise and yours called something different, and our tutorial game program for testing our game engine's functionality and testing game concepts.

Building our programs reveals that they both succeed and build separately. There is an option, however, that will have visual studio build our engine every time we build our executable file. Right click on the executable and go into the build dependencies, project dependencies and check the box next to the name of your engine.

We begin coding with a class called (Your Engine Name)ErrorHandler. In the video I don't realize that we'll need both a .cpp and .h file for this, but here I'll tell you that we do need both. The .h will contain our function declarations and the .cpp will contain the definitions. I will not be posting the code chunks here, but I will be describing the functions. You can choose to watch the video if you need the specific code, but at this point you should be able to create functions based on written definitions.

We will be using the iostream, fstream, "InitFileReadWrite.h", SDL/SDL.h; and ctime. In order they let us; write to the console; write to a file; check the initialization file; use SDL_Quit() to exit the game; and get access to the system time.

Everything in our engine goes in a namespace with the same name as our engine to avoid accidentally defining a function with the same name as another function in another library.

Here we have four functions. None return anything. Function dateTime returns a string but takes no arguments. The rest of these function return void; throwError takes two strings, one for errorWord and one for errorString. Function throwConsoleError takes an errorString. throwFileError also takes both an errorString and an errorWord.

dateTime returns the system time in the format Day-Month-Year Hour-Minute-Second. I found the function with a quick google search. I don't fully understand it but it's a simple function. Do some research to figure out why it works or just copy it into your code.

throwFileError prints the errorString we pass into it into a .txt file in the Errors folder. The file name is the date and time followed by the errorWord. Then it closes the game. We leave a not for ourselves here that we need to pop up an error message for the user.

throwConsoleError prints the errorString in the console before asking the user if they would like to continue, exiting the game if they don't and continuing if they do.

throwError uses InitFileReadWrite to read the line of the initialization file that will tell us if we're in debug mode or not. If we are it calls throwConsole error and if not it calls throwFileError.

After this is all coded I comment the file. I use very verbose comments as this code is meant to be read by beginners and myself who is bad at remembering what I made something for.

New we're coding the InitFileReadWrite class. It requires string, fstream, and vector.

InitFileReadWrite contains the functions readInitFile, readInitFileLine, writeInitFile, and writeInitFileLine. As you might expect they read and write to the initialization file. The read functions return a vector of strings and a single string respectively. The line files take an integer for fileLine, writeInitFileLine takes the string that's going to be replacing the current line in the initFile and writeInitFile takes a vector of strings to write a new initialization file.

The read files and the writeFileLine all use getline to read the entire init file, or at least down to the line they want. writeFileLine reads the entire file, replacing the one string it wants to replace. Then you write a new initialization file, just like how we write a complete initialization file in writeInitFile.

Note that you replace the entire init File when you write to the initialization file, you're just deciding how many lines to change, so you have to also keep track of all the lines you don't change. Eventually we might want to write multiple new lines to the initialization file, but for now this will do.

Next is the InputHandler class, which, as you can guess, handles inputs using SDL. It includes unordered_map which is how we're going to store our key data.

Functions here include void pressKey, which takes an unsigned int keyID, and isKeyDown which returns a bool and takes the same keyID as pressKey. We also have inputQueue which will be called to check which keys are in which positions. In private we'll have our unordered_map of unsigned integers and booleans called mKeyMap.

For those who didn't watch Ben's tutorials over at MakingGamesWithBen on youtube, maps have a set of their first value, in this instance integers, that are attached to the second value, the booleans. So if we ask about number 34 then the map will tell us if it's true or false. In this instance we're going to be asking a a key corresponding to the number is currently down.

The definitions of our functions are simple. In pressKey we take the keyID and set the corresponding keyID in our mKeyMap to true, if the key isn't currently in the array it will be added to the array. isKeyDown will check if the key we asked for is down by iterating through our map looking for the key. If it does it'll return the associated boolean, if not it'll return false.

inputQueue is a bit more invloved. It uses an SDL_Event to get keystrokes. After defining an SDL_Event we stick it in a while loop that uses SDL_PollEvent that takes our event. We use a switch statement to check the event type. If it's a KEYDOWN event we call pressKey, passing in the event's corresponding number.

Next we move onto the Window class. We're going to need string, glew.h, and SDL.h. Remember that all of this is in your engine's namespace.

First is an enumerator with bitwise values for WindowFlags. Inside will be the bitwise values for INVISIBLE, FULLSCREEN, and BORDERLESS, which have values of 0x1 to 0x4.

Functions are create, swapbuffer, and our getters for screenWidth and screenHeight. create takes quite a few arguments including a windowName string, screenWidth, screenHeight, and an integer called flags for our window flats.

swapbuffer returns nothing and takes nothing. screenWidth and screenHeight return the member variables of the same name which are in the private section with an m at the beginning of their name. (example: unsigned int mScreenWidth) The final member variable in the private section is mSDLWindow which is an SDL_Window pointer.

The create function will set our screenWidth and screenHeight member variables as well as create a Uint32 for our window flags which is automatically set to SDL_WINDOW_OPENGL since we're using an openGL window. We check if any other flags were set and use bitwise operations to add them to our window flags. Link to the bitwise video here. Now in create we'll set our mSDLWindow by using SDL_CreateWindow and passing in our windowName in the form of a cString followed by the window positions which are SDL_WINDOWPOS_CENTERED for both. Then screenWidth and screenHeight and finally our flags.

Finally we return 0 as create returns an integer. I don't remember why.

swapBuffer literally only called SDL_GL_SwapWindow into which we pass our mSDLWindow. That's everything done in our game engine. Time to make our main.cpp file.

It'll need InputHandler, Window, SDL, etc. Our main function requires int argc and char** argv because SDL uses these in its main function. We'll use the SDL_Init function with SDL_INIT_EVERYTHING so that we initialize all of SDL. We can initialize specific parts of SDL later if we need to, but for now we'll just initialize everything.

Next we'll SDL_GL_SetAttribute to SDL_GL_DOUBLEBUFFER. Then we make a newWindow and call its create function, passing in arguments that fit your computer screen.

Finally we'll create an inputHandler, and put inputQueue, check for the w key, and swapbuffer in a while loop that will continue until we exit. we'll set it so that if the w key is pressed the while loop will be set to false so that the program exits.

And that's everything for this video. There's one final video for this chapter where we'll update our uml document, update our documentation using doxygen, and push our changes to the master branch of our github account. Then we'll move onto the next chapter. See you next time.


▶️ DTube
▶️ IPFS

Coin Marketplace

STEEM 0.28
TRX 0.13
JST 0.033
BTC 62916.93
ETH 3028.97
USDT 1.00
SBD 3.67