Lesson 05
Improving the Codebase with Refactoring using Game class for Better Management
Video Transcript
This lesson will be dedicated to improving the codebase so it is better manageable.
As we have right now we have a file main.cpp and pretty much all the code is written inside
this file.
What we want to do is isolate all the logic outside the main function.
We can get out the logic outside the file main.cpp.
The ultimate goal for a game would be to modularize every single part of the game into individual
files that each have a very well-defined function, a specific responsibility.
If you want to think in that way you can look up the single responsibility principle.
That's a good start.
Since there are so many things we could modularize we are actually just going to do one thing
in this lesson.
And that is I am going to isolate all the logic.
All I want to do is remove the logic from main and move it to another class that I will
call the game class.
The game class will pretty much have all the code that we have right now and it will
be referenced from the main function.
But before we do that I want to improve the game map.
Because the game map doesn't really look like a maze and there is not many blocks that are
blocking the player from the entrance to the exit of the library.
So I went ahead and actually changed the field zeros to ones because if you remember from
the second line onwards they represent the tiles.
Another means it's a floor tile.
One means it's a wall tile.
So when I had and modified this is the before of the very first several first lines I replaced
with these.
That's the difference.
It's in green.
And I added a few walls so that the player can go from the entrance to the exit and there
is something blocking them on the sides.
You can do your own.
You can make your own map.
You can do it by hand by changing the zero to ones or the ones to zero if you want to
make a floor.
Of course ideally in the production environment out there you would have a map editor.
Map editor you could visually change the tiles and see it in real time how it looks like.
Let's go through this lesson and we can just do it manually.
So let's see what it looks like after the change that I made.
G++ main.cpp-lstl2.a.out.
So this is what it looks like.
Here's my game screen on the bottom left.
You can see there are wall tiles in white.
I reached the end right here.
The red tile.
So that's it.
So let's start the refactoring of the code.
Let's improve the code base.
So see everything is in main right now.
So let's create a new file.
Call it game.h.
This is going to be the header file.
So we're going to have two files right for the game class.
We're going to have game.h that will set some sort of contract for what the game class will
have.
And we're going to have game.cpp that will have all the implementations for each of those
contract clauses let's say.
So game.h is the blow print for the class.
All the signatures we're going to have attributes for the and we're going to have the member
functions.
Let's go back to the header file.
So let's declare a class game.
That's open brace and at the end after the closing brace put a semicolon.
That's the syntax.
Name of the class is game.
The key word is class here.
Now by default in C++ anything that goes here will be under the private school.
That means if you try to call a member function to try to access the attribute from the outside
right you instantiate to create a new object based on this class game.
And if you try to access that private member it will not work right.
So you have to make it public and that's with the explicit keyword public.
So I'm going to write public everything that goes after public here under public will be
public everything before be implicitly private.
Okay.
Let's save that.
Going back to main.cpp let's see what we have here that we can isolate.
We have main it's called it's initializing SDL and at the end of me it's cleaning up
and quitting SDL.
So we have some two basic operations here.
First you initialize something.
You initialize the game meaning you're going to do some stuff.
You're going to load some stuff set up the systems etc.
So we could make a function for that and we can call it initialize or init or whatever
you want to call it something start up to you choose the name.
And at the end we have this operation of cleaning up and closing all the systems that were
open and that can be called the shutdown operation or the closing operation whatever
you want to call it something to do similar to the word close or shut down or whatever.
Okay.
So let's start by doing that.
So I'm going to take this see out and the SDL init this part.
So I'm going to first I'm going to copy so we have it lingering there and I'm going to
remove later.
I'm going to copy lines 173 and 178.
Now I want to put it in game about CPP that where we'll have the implementation but I
need to put this somewhere.
I'm going to call a function.
Okay.
Let's call this function initialize.
This function will not return anything.
So void is the return type.
Inside initialize.
I'm going to put the logic to see out starting and then initialize the SDL init video.
Now I want initialize to be part of the game class.
How can I make it a member function of the game class?
You can prepend it with the name of the class colon colon.
Now this thing.
Okay.
I want to declare that in the header files.
We go game.h.
I want to call it void initialize here under the public line for.
So this is the header file for game.h.
We declare the initialize function here and then we can define its implementation in the
.cpp file.
Now in order to get the signature we have to include, so pound include game.h so that
it gets that blueprint for the class and we can associate the implementation to the initialize
member function.
Now since we're using here, we're using standard cout.
We need to also include that.
Remember the cout comes from IO stream.
So I'm going to include that.
Now for the next one we're calling sdl init.
This function is from the sdl.h header file.
So we need to include that as well.
So pound include sdl2 slash sdl.h.
That's the way it works for my system.
I don't know if your system will have the same include path to get to sdl.
And that's the same thing we had in main.cpp.
By putting in angle brackets that works as well.
Let me put it either way should work.
Let me just follow what I had before.
So sdl log also part of this header and this too.
So that's it for initialize.
Now that we have this, we can go back to main, back to the main function and replace the
code that we copied with the call to game initialize.
Now we have to create an object, an instance of the game class.
So I can just do game and I call any name you want for the variable.
I'm just going to call a game in lowercase.
It's almost similar to the class name.
And that will instantiate an object of the game class.
With this object I am going to go call game.initialize like that.
And that will call that function.
Because I'm referencing this type called game, it doesn't know about it from the main.cpp
file.
So we have to include game.h.
Okay so go all the way up in main.cpp.
Now we're going to include game.h.
So it knows about the game type.
Now let's test it out to see if I didn't miss anything.
So let's recompile.
And it's giving out a problem right because I only recompile with main.cpp.
It doesn't know, under find reference to game, call to initialize.
So it doesn't know about game.
So I have to add to the list of files that I'm going to compile.
So in addition to main.cpp I also have to pass game.cpp.
And because the header is already included from game.cpp that we don't have to specify
here.
And don't forget the link.
And now we have a member function for game initialize.
Return statement value.
And function return void.
Ah okay I see what's going on.
Back to game.cpp implementation for initialize.
We're returning one here.
This was meant to return from the main function.
Now you could change this to return to value or not or you could use the exit statement.
Some sort of function that can abort the program and stop it right away.
That would be one way of not having to return from here.
Okay especially if you have very, like if you have modules that are deep within the
code base and there's no way you can return from main with one level deep.
Maybe it's several levels deep and you would have to return the bull all the way up to
the top.
So maybe you want to do something else here.
You could use exit with the code.
Let me see that.
If that works.
Okay.
Seems to be working fine.
I want to simulate the error so I want to show you if it exits.
So instead of not equal to zero I'm going to place actually I'm going to place greater
than or equal to zero.
Try to simulate the error to see if you can see it is starting and it calls info fail
to initialize SDL and get error.
There's actually no error right.
But exit anyway right.
Nothing happens.
So that worked.
I leave it to you if you want to do other kinds of things for that case.
Also SDL log here is doing info.
Usually this kind of errors that crash the program is not really info.
It's error.
So maybe you want to look up SDL log error and you can specify the category as the first
argument of the second argument would be the message.
That's something extra credit you can do.
Okay.
I'll leave as is right now.
Anyway, let's revert that mistake.
It's not equal to zero again.
You know that working fine.
Let's cook.
Let's keep going and remember the next thing I told you we need something to shut down
or close.
So I'm going to add a new member function to the game class that is a public function.
I'm going to call it shut down.
And that's going to be to clean up anything that you have into shut down SDL.
Now I have the declaration.
Let's go to the game.cpp and add the void shut down.
That shut down is part of member function of game.
So we have to proceed the name of the function with game, call and call.
Name of the class, call and call.
Now let's go back to main.cpp.
And let me go to the end.
And let's copy these statements to free the surface, destroy the window and quit.
Actually there's a lot of work to do for the free surface of destroy window.
So let's leave these for later but they are going under shut down.
So I'm just going to take the SDL quit and move it to shut down.
And in place of where we had quit I'm going to call game.shutdown.
Now because game is in the scope for the main function here, this very long function, that
should work fine, right?
Remember I instantiated a game object online.
In line 174.
The shut down should be fine, right here.
Let's recompile and run a.out here to see if it works fine.
Seems to be okay.
Alright so what we did so far is add the class, the game class and then we declare the initialized
shut down functions.
They both return nothing so it's void.
And they're public to make sure we were able to call it, right?
Because if it were private, which is implicitly if it were defined in line 2, right, before
the public keyword with no keyword at all.
We would not be able to call game.whatever because the instance cannot access something
that is private to it.
Only from within the implementation function, member functions themselves, can they access
the private members of the class of that instance of the object.
Okay let's see what else we can do.
So right after initialize, we have this sdl create window.
So it creates the window checks for an error and then it gets the window surface.
We can isolate the logic to create the window.
So let's copy this.
Let's go to game.h.
I'm going to add another function.
I'm going to call it maybe let's we can call setup window create window.
So we're creating the window.
So that might be one, the same name, right?
Or we can say setup window.
Gaming is always a hard thing in programming, right?
So if you have a better name, you're free to choose it.
I'm just going to create window.
How about that?
Now it's void because it doesn't return anything.
Now let's go to game.cpp to define that.
Now I'm going to say void game colon colon create window.
No parameters.
Paste that code in here.
We're making a variable for a pointer to an sdl window.
We already have sdl included so that's fine.
Create window.
We use this constants.
This constants come from the sdl header.
Now we're using window width window height.
They are not in scope for this file so let's go back to main and see where they are defined.
I think they are at the top of global variables.
If you can recall window width window height they're here.
So I'm going to copy those.
I don't want to make them global anymore.
So we can place them under the game class as private variables.
So go to game.h.
I could have them here.
I can int width int height as private.
Right before public when there is implicitly private.
Of course you could write private but I'll leave it without.
Now that will be in scope so all the member functions have access to the private data
numbers of the object instance.
If you know sdl plus you probably want to add more keywords here to make this constant
on perhaps even static something like that.
I'll do that for later if you want to do it up to you.
Let's just see if it works as is.
Now I assume that will be available now and then we check if the window variable is null.
See error is from the IO stream.
We have it here.
And fail to create, get errors from the sdl header.
We have it included here.
Now we have the return to here.
We want to replace that if the exits or whatever means you devised to handle all those problems.
Now one thing that's of interest is the window variable here will lose like right after the
function create the window when it's complete window will no longer be in scope so it'll
be gone essentially.
But we're referencing this window throughout our main.cpp file right so we got right after
the creation of the window we're referencing it many times I think perhaps not many times
but we are referencing it.
So what's going to happen now is we are going to have the window as a private variable of
the game class the same way we did with the constants for the width and height of the
screen.
So here in game.h I have to add that window right so it's a pointer to an sdl window only
called window.
We have it here line four.
That's just the declaration of the variable to say it's going to be there so you don't
have to initialize.
The initialization or the setting up is in the create window function so we assume that
create window will eventually be called otherwise window will just hold something that's not
meaningful some memory garbage.
Anyway going back here to game.cpp just to make sure we have we no longer need to declare
the variable because it's already done line 19 we don't need the dive pointer here because
that variable is already declared as a private of the game class so I just need to say window
to define it.
Window equals sdl create window.
Now going back to main.cpp we have to replace everything there with game.create window but
the thing is right now as we have it we actually need the window otherwise it's going to fail
because of the window surface and all that stuff so we have to be patient until we could
get something working so I'm just going to remove this.
Alright now we have sdl surface window surface got window surface now this is something that
we're going to be referencing in the loop here and so on.
So that's something we have to think about.
Let's see I'm going to place this the window surface will also be a private member of the
game class so I'm going to take this I'm going to copy actually I'm going to place an in
create window to under create window add that sdl surface window surface but instead of
declaring it here it's going to be defined so no need for the type and the game.h you
actually place it here sdl surface pointer window surface.
So now this window surface variable will be available to any member function of game so
create window will have it access so we can set it here in line 32 and we can reference
it throughout any member function of game.
Now we can remove that.
Now we have the game map tiles be initialized here we can take that too.
Okay very mind that we're going to be cop cutting and pasting code so there might be
issues that will pop up later on as we try to compile this so please be patient so after
removing everything there to the game class we're going to compile and it's likely that
it's going to give us a lot of errors and we're just going to go one by one take it
one step at a time and fix each error right so let's let's take the game map tile so
this variable holds all the game map tiles we can take that and make it a private to
the game class entrance position exit position we can this seems like an initialization
stuff so we can also take that as private okay so let's cut and game.h I'm going to
place it here game map tiles and position and so on now this one seems okay I'm concerned
I don't know if C++ will like the initialization in here we'll find out later usually we initialize
things in the constructor okay we're going to do one later so just give me a moment so
we have these entrance position exit position we're using tile size where is that defined
let's go back to main.cpp we have tile size is a constant our global variable here I'm
going to copy it I'm going to also make it a private here in line 3 I'm going to make
a new line to line 4 and it's there now I want to make these two for now actually want
to make that definition in the constructor so usually when you have these private variables
in the class header right in the class sorry so the header file has the class declaration
right you define that you say that here's a class these are all the methods right the
member functions and these are all the attributes and in the cpp files where you implement those
so typically you want to keep the header file without any initialization so for these two
I don't want to initialize here I want to do that in the constructor so when the object
is instantiated that is when you create an object of the type game that's when you set
the the attributes to a default values initial values in this case we have an entrance position
with the initial value of zero zero tile size tile size same for exit position so this part
to the right hand side with the equal sign will be done in the constructor so to make
constructors simple you have the game class inside as a public just say the name of the
class and parentheses right and for the constructor that I want to make the default there's no
parameters so we don't need anything to instantiate so that's the constructor right here now to
implement the constructor go to the main let's go to game that's cpp I'm actually going to go up to
the top because I like putting it in the top so I'm going to say game the same way I wrote in
the header but I'm going to define the with the function block now because this construct is part
of the will be part of the game class I have to say game call and call prefix and this is the
constructor now in the constructor we're going to do the setting of entrance position and exit
position I'm going to copy the code here but before I do going there I'm going to remove the
definition so we only have the declaration as the L rack introsposition as the L rack exit position
back to game dot cpp I'm going to paste that code but since we already declared the variables we
don't need to type to the left hand side of the name of the variable and I just messed up the key
here like this all right tile size was it's not explicitly included here but it's included from
game dot h sorry it's not from the outside so tile size is private data member of game right of
private variable so it's here my actually define these inside in game class is probably want to
make that static but I'll leave that as is anyway we have access to that so that shouldn't should
not be a problem we have the tile size because it's a private data member we are inside a member
function or a constructor in this case so we have access to that okay let's keep on going so since
we already have these lines 12 through 14 in the main dot cpp it's already a private members of the
game class I'm going to remove it okay going down back to where we were after create window
so we had the entrance position exit position when the object is instantiated in line 170 it's
going to call the constructor and it's going to set an entrance position exit position to these
values so that's fine now the case for the what else did we have the game map tiles here it's
initialized here I don't know if this will work I have to find out later if it doesn't work probably
have to make a loop in the constructor so we set every single element to zero just to have
an default value a default value I'll leave that for later if that is a problem now we call load
game map now let's take this load game map function here and let's move it to the game class so we're
going to reference the an instance of the game class that calls the member function load game
map as so now let's go to the definition of load game map I'm going to just basically cut everything
and I'm going to game dot cpp and I'm going to paste it here now I want to make this a member
function of game so I'm going to put before the function name game colon code all right and before
we go to the header to declare there let's walk through it so to see if there are things we can
improve so the parameters we're going to see if we can make them private members of game so
first we're calling i f stream i have stream is part of the f stream so I need to include
at the top include f stream
game map file game map fine is open interest line and so so so we have the changing of the
interest position dot x and position dot y but remember just what we just did we now have interest
position in x position as a private attribute right it's an attribute of the game class
but now we have load game map as part of a member function of the game class so it should have
access to the private data member so we no longer need the parameters two and three right the
interest position of the exit position so that's no longer needed we can reference it directly it's
in scope and after that we have the tiles at index and so on but what is it what was it doing before
if you can recall going back to main dot cpp in the call to load game map uh remember you don't
need the exit and interest anymore so remove them you can see that I think we have game map tiles here
but these were actually moved as private data members of the game class
so actually we can reference it directly too so these tiles here that we have
we no longer need as parameter and because the variable name is styles here I have to rename it
in game dot h I'm just gonna say tiles okay like so so it corresponds to these tiles we're talking about
right here
okay
now going back to main dot cpp we simply call game dot load game map and we don't need anything
at all anymore because the tiles will have been stored in the instance of the game class as a private
variable mind you these is in scope as long as the object is in scope and the object game will
be in scope as long as main is running so that should be totally fine because the scope of the
game variable it will be alive up until the end of the main function which should be returned zero
which is going to finish the program and shut down so that should be totally fine so the
private members of game will be these will be still in memory there's no problem with memory leak
so far memory leak is when you allocate memory and then you forget to deallocate and then you
have that chunk of memory taken and you keep losing losing more memory space because you simply
forgot to deallocate
okay
now let's look at this debug function called print game map it's pretty much the same thing we
had the same arguments right so i'm just going to delete them and i'm going to move that to the game
instance the class so let's go to print game map we're just going to cut
go to game.cpp and mind you i forgot to declare the function names in the header so
let's put here game.colon colon before we do that let's just walk through print game map
so we have intiles we already have that it's private
interest position already private to the game instance exit position already private don't
need that we have c out we're already doing ios stream in the top so we include ios stream
so one so one we have the loop and so on so this should be totally okay now we need these two
functions in the header so game.h go there and add them void under the public right line 15m say
what was it load game map and print game map
and they're both void with no return void mean return nothing and no primers
save the file save the file save main okay looking everything's looking good
we're almost there so take a moment to rest a little bit
okay so let's go on
now we're saying game map surface to create game map surface we can also take the game map surface
and make it a private variable in the game class let's do that i'm going to copy the left hand side
go to the game.h under the private variables i'm going to declare game map surface like so
and mind you there's one thing i want to before i go on
i actually want to add includes here to correspond to everything we're using
so let's see int is built in we don't need sd albino we're using that so i want to include
sdl to slash sdl.h output angle brackets instead i like to do this way so include because we're using here
surface fine fine fine fine there's nothing else so because this is a red in game.h
if you go back to game.cpp you don't have to have it in my tree so i remove it
now i also want to add an if if not def guard okay that's something very common that we usually do
and
and that's pretty much enclosing the whole header file in the if and def if not defined
i usually give some sort of unique name you i have a pattern that i like to use is
underscore and the name of the class all uppercase game followed by the extension of the file
h like this or you can add more underscores up to you so it's a unique item in a fire so if this
is not defined we're going to pound define the same thing and we're gonna
and if here at the bottom
so essentially this is a guard so if you have if this file is included multiple times
you won't have a problem with the compiler it will only be included once so if this
identifier is not defined you define it and then here's the code and end the if okay and the if
and the next time you include this file it's going to check is this game h not defined because
the it is already defined because it passed once it will not execute any it will not place anything
that's enclosed so we only have the contents included once you can read more about it
uh it's the guards header guard files okay in any case that's gonna every single header file
gonna have that to protect us from those include problems okay let's go back to game dot cpp remember
we were doing the game map surface so i added it here go back to main dot cpp now this part i'm gonna do
what this is actually something i have to think about because you're creating the game map surface
essentially you are
setting the game map surface
so you load the game map then you create the surface with the tiles right
because map game map surface is now a private main private variable uh this setting can be done
within the game member the member function of the game class
and then immediately after have to paint up the entrance of the exit
so maybe these have one could be in one single function
what do you want to call it that's always the big challenge so create the game map
put paint we can call set up game map
surface or
or create game map server the same name
i don't really know what to call it let's say
uh create let's just use the same name
i'm gonna say game dot create game map surface
and this code is going to that new function cut let's go to game dot cpp
let's say void game create game map surface no parameters paste the code uh since we already
have this variable declared remove the type uh so we need this function here let's copy
actually that's the same no same name so that's gonna be a problem right
we have to change this let's call it
let's see
let's call set up game map surface for now
now let's go back to main let's copy this function
go back to cgame.cpp before set up game map surface paste that function
and add the prefix game colon colon before the name now we need to put these in the header
so let's go to game dot h and add for sdl surface star create game map surface
and there's an argument there right what was it it's all the it's the tiles but now because they are
private we have access to them so let's see in tiles i think we don't need it here but it
might generate problems because we gotta think about it let's see if i remove it we have the
surface oh we have this too and the surface create surface rectangle tiles is here
surface format field rack
uh surface return surface maybe it's okay
now it's called from here now we don't need that argument anymore right because it's already
accessible if it's null have some errors maybe we want to use exit here
set a return
and paint entrance the paint exit we need those let's grab it from main dot cpp
let's cut that go back to game you can paste it up here don't forget to add the
game colon colon prefix to both
we have these let's check the arguments we're ready
we have access to the entrance position so we don't need this
it should be there already because it's a private variable of the instance
same for paint exit don't need a second parameter
and it's being called from here with the game map surface
now let's add these functions to the header files so we have paint entrance
paint exit i'm just going to copy this
i'm going to remove the separate the prefix and these braces and i'm going to duplicate
and say paint exits now i didn't add the setup one right void setup game map surface that's there too
now let's go back to
let's go to main dot cpp what did we have here let's call this setup right remember to change
the name setup line 89
okay um let's see
we are kind of halfway done i think i know it's a long thing to
reflect for these and move to places but it's essentially the same approach
you just grab the function put in game dot cpp add the prefix see what variables are no longer
are already accessible because of the virtue of the instance of the class the private members
you can access from any member function that you have without any
just you just can access it there's no need to be passing stuff around
and just proceed like that i think we're going to take a break for this lesson
and i'll see you in the next one we're going to finish everything
and as i said before we have to be patient when we do this and there will likely be errors as we
compile after doing all the migration of the code and we're going to tackle one error at the time
starting with the error at the top and then we're going to address all the issues that the compiler
will raise we'll see you in the next lesson until then
No comments yet (loading...)
No comments yet (loading...)
Did you like the lesson? 😆👍
Consider a donation to support our work: