Adding a path builderFirst, get menu-background-0x114.bmp, export it as a .bmp with GIMP like I have explained in the previous tutorial and paste it in your res/images folder. You can delete the test.bmp from the previous tutorial.
Think of every time we are going to load an image. Isn't it boring to hard code the entire image path? What if in the future we decide to change the images folder location? Man, what a mess to have to change every single hard coded image path! Wouldn't it be better to have a function that given the image name, would build the entire image path? Yes it would! So, let's do it!
Add the getImagePath function to Utilities, just like shown below.
Now edit line 24 in FlappyNix.c, we should now be able to load images like this. Pretty useful, right?
Compile, install and run the program. This is how it looks like now:
Implementing a state machineWe've got to a point where we will start dealing with states. Every program/game has different states: main menu state, play state, pause state, game over state, etc. In order to manage each state correctly the state machine was invented. It is responsible for state initialization, update, drawing and deletion. So, let's make a few changes and implement our own state machine.
Creating the main menu stateFirst, let's create the main menu state structure.
Create MainMenuState.c and MainMenuState.h and do not forget to declare it in the Makefile.
To simplify button representation, we will need a Rectangle class. Declare it in the Makefile as well. Here is how it looks, it is pretty simple:
Now let's implement the main menu. Here is MainMenuState.h:
Long story short: there is an integer to tell if the state is complete/done; a background image; two buttons - play and exit - each with an integer that tells if the mouse is hovering them and a Rectangle representing their boundaries; an integer representing the action performed when the state became done - either play or exit could have been pressed.
Below is the implementation of the main menu. Paste it to MainMenuState.c:
Contained in the above code snippet are function calls like: mouseInsideRect(state->exitButton) and drawRect(state->playButton, YELLOW)
The first is implemented in Mouse.c:
The second is implemented in Graphics.c:
Ok, now that we have implemented the main menu state, we need to make a lot of changes to FlappyNix and transform it in a state machine.
Modifying FlappyNix to implement a state machine
This is how FlappyNix.h looks like now:
We have deleted the test image, as well as our previous yellow rectangle coordinate. Make sure you get rid of anything directly associated to those things from FlappyNix.c as well.
We now have a void* state; which is a generic pointer to the current state of our program. What is a generic pointer? If you have just read the information on that link you might now know that, since there are different states - game over, main menu, etc - but they all fit under the "state" category, we can represent an abstract state with a void pointer/generic pointer. That state can either become the main menu state or the game over state, get it?
Since there is no way to know the type of that state - because it is just a void pointer - I have added an enumerator that indicates which state is the current one. Every time the state changes, we will update this currentState indicator - this will enable us to call the right methods on the right state and not make any stupid mistakes like calling drawGameOverState() when the active state is MainMenuState. I hope this was not too confusing and you understood everything.
Since I have made a lot of changes to FlappyNix.c I am going to paste the whole source file here and try to explain it after.
So, I will try to explain what is going on...
The prototypes at lines 13 and 14 are there just for visibility purposes. Their actual implementation is near the bottom of the file, but they are called throughout the file inside other functions, so in order to make them visible for the whole file, I have placed their declarations at lines 13 and 14.
startFlappyNixThe previous yellow rectangle related stuff was removed.
The current state and the actual state pointer initialization has been added.
updateFlappyNix and drawFlappyNixNot much has changed, we now have a switch dependent on the current state enumerator. Again, this enables us to call the right methods for the currently active state. I have explained this just a couple of lines above.
changeStateThis method is called when we want to change from one state to another.
In the next tutorial we will be adding the play state. When we implement it and then press the play button on the main menu, this method will be called and what will happen is the following:
the active state (in this case, main menu state) will be deleted, the current state identifier will be updated and then, according to this identifier, the new state will be created. Since we pressed the play button, the play state is expected to be generated.
checkIfStateIsDoneThis method is called right at the end of the update method. It checks if the active state is done/finished.
If it is, depending on the active state, a certain action is performed: the program can be terminated or there can take place a change of state. And yes, if you are wondering if this is the only place where changeState() - the method previously described - can (or should) be called, you are indeed correct.