TIL: Learn to Code in C++ by Developing Your First Game (Section 2, Part 3)

Hoyo ~ It’s been a few days since I’ve done any sort of studying. It’s a long weekend, what can I say. I managed to binge watch through Better Call Saul though.

I feel like the first time I did programming, back in high school, I didn’t really “get it”. I instead made the decision to get into doing artwork, which over time evolved into visual effects. I’ve been lucky to get to dabble with scripting while doing a job that is largely creative, and now that I’m returning to some basic C++, I feel like I have a much better understanding of it. Ben is doing a great job teaching the course so far, and it’s enjoyable to follow the lessons. That said, time to jump in with the note taking.

Section: 2 – Bulls & Cows Word Game – Your First C++

Lecture 25: Instantiating Your Class

I don’t know why it never dawned on me until just now, that “instantiating” means “calling an instance”. It’s not a term I’ve heard tossed around except when programming, but it makes sense now that I’ve put two and two together.

I’d already #includ(ed) FBullCowGame.h last night, but the idea behind that is simply that we can create our functions in a separate file, and then call on them when we need them.

The goal here is that we want to create a BCGame instance. We can do this simply be typing: “FBullCowGame BCGame;” into the PlayGame(). The problem at the moment is that it’s uninitialized. The game has no values being entered into it, and so doesn’t yet really -do- anything. We don’t have a constructor, which is what’s used to determine our initial state.

Lecture 26: Writing & Using Getter Methods

The key is to set up our initial private variables from our header into something meaningful, and then access them from main.cpp using a getter. This will allow us to set up the max number of guesses, and then compare it with our current guess.

After we create “FBullCowGame BCGame;” we are able to access any of the methods simply by using a . at the end of our variable. So on a new line, if we enter “BCGame.” an auto complete list will come up that we can use to access methods such as the one we created in an earlier lecture called “GetMaxTries()”. Tab is used to auto complete. Once you’ve got it typed in, you can right click it and “Go To Definition” which will take you to the file that defines the method. Definitions take us to header files, declarations take us to .cpp files.

Methods may access Private Variables. If we “return MyMaxTries;” from the GetMaxTries() method, then we can get that variable from the header. As is, because we haven’t initialized our MyMaxTries value, it will return a value of -858993460. Not helpful. This is why we need to make sure we set it ourselves to a sane value. Something like 5. It’s ok to do this for now, but it would ideally get set in a constructor.

One thing to be aware of, is that compilers can sometimes fail. It can be tricky to debug what’s happening sometimes as values may just not appear as expected. It can be a good idea to get in the habit of “Building” the game when you make changes. It something looks wrong, try to rebuild the project.

Just be aware, that if you attempt to build, while you’ve already got the game open, no good can happen.

BuildNoGood

It’s better to create variables at the level that they are needed. If you only will ever need FBullCowGame BCGame; to appear in PlayGame(), then sure… put it there. But nothing else will be able to access it. If you need access from other places, place it up top, below any includes or usings.

Lecture 27: Const Keyword

Const has a different meaning depending on context, but it generally means that it’s constant and will not change. At the end of a member function (for example: int GetCurrentTry() const;) it prevents the function from modifying any member variables.

If you want to ensure that a function will be unable to modify data, you should always append “const” before the ;

We do this with int FBullCowGame::GetMaxTres() const { return MyMaxTries; } however keep in mind that a type qualifier is not allowed on a nonmember function. Meaning it will work inside a class declaration in a .h file, but not outside of class declarations.

We would make anything that is a “getter” something that just fetches without changing data, const. GetMaxTres() GetCurrentTry() and IsGameWon() are candidates.

Lecture 28: Constructors For Initialization

Every time an instance is created, we want to ensure that it is initialized correctly. A constructor does all the set-up we need. This allows us to know what state our game is in when it’s launched.

A constructor is created by taking the name of the class, and making a method with the same name. IE: if we use class FBullCowGame {} Then inside that class, we would also want public: FBullCowGame();

This means that when we instantiate the game in main.cpp, using “FBullCowGame BCGame;” it will look for the constructor, and run it if it can.

So after creating the constructor, select it, right click, and create the definition.

If we try to edit the private variables inside this new definition, it will be reflected in the program if we enter something like std::cout << BCGame.GetCurrentTry(); in our main loop.

What happens, is the FBullCowGame.h file allows us to set-up the private values at compile, while the constructor in FBullCowGame.cpp allows us to change those values at run-time. You really wouldn't have both... so it's ok to change the private settings to:

private:
// see constructor for initialization
int MyCurrentTry;
int MyMaxTries;

You still need to declare the variables here, otherwise you'll get errors when attempting to fetch the data with the getters we created in FBullCowGame.cpp. It's also important to initialize the values in FBullCowGame.cpp like so:

FBullCowGame::FBullCowGame() {
MyCurrentTry = 1;
MyMaxTries = 5;
}

If we declare using int in FBullCowGame.cpp, it seems to error out rather than accepting the new value.

The very first thing that we'll want to do when we PlayGame() is to reset it so that it has the default values we want. This is important since we can play the game multiple times.

In our case, we want FBullCowGame() to only have {Reset();} in it, and we'll actually move our initilization down into Reset(). Like so:

void FBullCowGame::Reset()
{
constexpr int MAX_TRIES = 8;
MyMaxTries = MAX_TRIES;

MyCurrentTry = 1;
return;
}

The reason for using a constexpr is for sanity. Magic Numbers should exist as constant expressions so that it gets known at compile time. It also helps to have them set at one place at the top of our method since as time goes on, we're likely to add more things to the code, and it can get difficult to find specific magic numbers without clean organization.

Lecture 29: Pseudocode Programming

It becomes useful to add notes such as "// TODO add a game summary" into your comments so that you can figure out things that you want to do later. TODO is accepted as code for "I need to do something". In visual studio, if you go View->Task List (ctrl + \ followed by a t) it will open up your TODO list!

TODO
This can be helpful for figuring out priorities. Under Tools->Options you can get other TasksList tokens to show up such as HACK.

The whole idea behind pseudo code, is we leave ourselves comments. These will explain what we intend to do, and allow us to maintain a train of thought when we return to the file to produce the rest later.