Loading
Lesson 06
Courses / Build a Game with SDL - Make a Labyrinth with C++ from Scratch - Beginner Tutorial
Wraping Up The Migration of Logic from Main to the Game Class

Summary

# Refactoring Game Logic to Game Class

In this session, we continued refactoring the logic from `main.cpp` into a dedicated `game` class, focusing on organizing the game loop and related functionalities.

## Key Changes and Steps

1. **Game Loop in the Game Class**:
   - Moved the game loop from `main.cpp` to a new `run` function inside the `game` class.
   - Updated `main.cpp` to call `game.run()`.

2. **Private Variables**:
   - Defined `quit` as a private member of the `game` class.
   - Moved the polling event and player position variables into the `game` class.

3. **Handle Player Input**:
   - Created a new member function `handlePlayerInput()` in the `game` class to manage player inputs.

4. **Player Position Management**:
   - Moved the player position variable to the `game` class as a private member.
   - Implemented a function `setPlayerPosition()` that sets the initial position of the player after loading game maps.

5. **Initialization**:
   - Set initial values for variables like player position and window surface in the constructor of the `game` class to avoid garbage data.

6. **Code Organization**:
   - Created a new header file `tileType.h` to define tile constants used throughout the game.
   - Ensured all function declarations and definitions were consistent and accessible.

7. **Error Handling**:
   - Compiled the code multiple times to identify and fix errors, ensuring all changes were integrated properly and functions called correctly.

## Compiling and Testing

After refactoring, the game was compiled with several iterations to fix any errors:
- Missing semicolons and undeclared functions were rectified.
- Optimized header inclusions in `game.h`.

The final test confirmed that the game logic worked as intended, with smooth gameplay and functional quitting mechanisms.

## Conclusion

The entire game logic has now been successfully refactored into the `game` class, enhancing code organization and readability. This change allows for better maintainability and scalability of the game's architecture.

Thank you for watching, and see you in the next lesson!

Video Transcript

Let's continue our refactoring of the logic in main.cpp so it goes to the game class. So let's pick up from 991. We have the event. We have player position. We have the game loop. And right after game loop we have the cleaning up of the surface and window. So let's see what we can do here. So let's think about this. Well the game loop right here, we can make the game loop and place that into a function in the game class. So this function in game class will be responsible for running the game loop. So we can just call it run. So game.run would be the function where you are going to call. And then we can finish off, wrap it up with putting the free surface and destroy window in the shutdown of the game. And these variables go as private variables. How about that? Okay so let's start doing that. So first I'm going to take the while loop. I'm going to cut. I'm going to go to game.cpp. I'm going to paste. Oops, actually I want to make a function for that. So before I paste go to game.h. Add a new public member function. Call it run. No parameters. No return types. It's void. Go back to game.cpp. Let's call void game colon colon run. Let's paste the code for the game loop here. Now we need the variable quit. So let's go back to main.cpp. We can cut line 93 and put it before line 147. And we can have the variable there as local. You probably could have it as a private member later on if you want to reference the quit from any other member function. But for the time being I can leave it here. So we while quit. So we have the polling of the event. Now we need the event as well. So go back to main.cpp. Cut line 91. Move that to here line 148. And we pull the event. If the type is as deal quit. Quit is true and the loop will finish. Else if event.type then we call the handle player input. Now we need this function handle player input. Let's define it as a member function of the game class game.h go back. Let's call it void handle player input. And for the parameters you probably won't need them. Let's take a look. Go back to game.cpp. We're calling it the event actually reminding we are going to need the event here if it's a local here. But you could also place this as a private variable. So let's first just deal with the ones we don't have yet. So event is here. So that's fine. Player position we don't have it. Let's go back to main.cpp. We have a player position right here. Let's take this player position because something that's of interest to perhaps more than just one member function. So I'm going to cut this and I'm going to go back to game.cpp. Now for that part actually I want to set it somewhere else. Let me backtrack a little bit. So the player position here is using the interest position dot x and interest position dot oi. So we need to we're going to do the following. We're going to take left hand side which is the declaration of the variable. We're going to make that a private. And then the right hand side will be the definition that's going to happen as we call game a new function. Let's call this new function to set the player position. The problem is we have to do it right after these because the entrance position is only set when we load the game maps right so we cannot just initialize this in the constructor of the game class. So let's take that. So I'm going to cut actually I'm going to copy the left hand side. I'm going to make it go to the game header file as a private SDL wreck player position right there. Now going back to main I'm going to take this except the tie and I'm going to remove it and I'm going to create a new function. I'm going to call it set player position. Okay, so let me go back to game.h and add that function avoid set player position. No parameters and game.cpp. That's actually the initial position or is it the player? Yeah, that would be set initial position. That's fine. Let me add here void game.coloncollinset player position and I'm going to paste that code saying that the player position equals the entrance position.x and suppose it's the y 32 32. So we assume the assumption is that this function will only be called after these coordinates coordinates where the interest position have been set. So set if that assumption and go back here to main it's been called go back to game.cpp we have that here and you probably want to set some initial value in the constructor if you want for the rack otherwise might have the garbage data. So usually when you have all these variables you want to set some default value just in case. So if the player position for example let's go to game.cpp. I'm going to say player position equals I'm going to say x 0 y 0 with the style size hide the style size. So in case there doesn't something happens I don't know the play position not set at least it will be in the very first column in line. I can also do that for the others if you want a game map surface. Maybe you can set that to null because it's a pointer. No exit position entrance position is here. The tiles are going to deal that later so I'll leave it there. Window surface and window. Window is null. Window surface is null. Just to have an initial value. The other ones these are supposed to be constants I would say a static constant but let's do it. Let's do it later. It don't have to be that if you want anyway. I forgot the game loop right so I have to say game.run. So the loop around here. And we can move these free surface and destroy window to the shutdown function. Right before the quits. And let's check the variable names game map surface here. Destroy window window window is here game.h. It seems like everything is in place and we have set player position here. The run event select backtrack what we had. So we had a handle player input right. We don't need player position. We don't need game map tiles. So let's go to this function. Is it here yet? It's not. We have to go back to main.cpp and cut the handle player position function. And base any game.cpp. Let me put it at the end. And put the prefix game colon colon before the function name handle player input. And move the second and third because they are already as accessible as private data members. Let's add this to the header. I just copy this part and remove the prefix. Oh, it's already here. Sorry. There you go. Now since you have this event here, actually you can also make that a private so you don't have to have it passed. So I'll remove that from the handle player input and I'll cut and paste into the private member of game. So we have an event here. Now make sure to go back to the game loop in the game run and remove this declaration because it's already been... It's already instantiated, right? It's already done in here. Now going back to the game run. We have the polling of the event and we have player input. We don't need an event anymore here. After that, this is the input part. We have the split surface game map surface. Okay, window surface fine. Now we have this return. Maybe you want to crash. For now, player color, surface... Actually, this game player color doesn't have to be set. Does it have to be set in the loop here? So perhaps I could take this and make it a private member as well. So let's do it the following. I'm going to copy this, go to game.h. I'm going to paste it here. Event 32 player color. And then I go back to game.cvp. And in the definition, I'm going to do that in... Well, I could do it right after setting up the window surface. Let's take this out of here. Let's see if we can work this out. Where is the window surface made? Set in create window right here. Right after that, maybe from made, instead of after creating the window, we can set the player color. We can say set player color here. And I'm going to make this function. Go to game.h, white set player color. And then go to game.cvp and define it. White set player color. I forgot the game colon colon. And I just paste that code and remove the type because it's already declared. And it takes the window surface format, assuming it's already... window surface already exists. And it sets that color. You know what I'm using? Like the set, setter name you could mention. Even though it's not proper, like usually it would take parameter and that stuff. So I'm not thinking in terms of setter here, okay? I'm just making a void function to do something without returning anything. Okay, so that looks great. And then we don't have that in the loop anymore. So it's already done only once. I don't know if it's going to cause problems. We'll see later. So we're done here. Let's go back to handle player input and take a look. So we're calling this, that, you know, something, map coordinate to title ID, that's something we don't have here. So let's go back to main.cpp. We have map coordinate, title ID. We also have is blockable, right? So maybe we want to take all of these and let's see, put in a game.cpp, add the prefix game, colon, colon, game, colon, colon to the function names, then you just copy the function names to the header file. But before I do that, let me check if we need these parameters. Map coordinate style ID, let's see. It's giving a new position in the game map tiles. Game map tiles are right accessible. So I don't need that. I remove that. So we'll go back to map coordinate, remove the second argument from the calls. And finally, go back to the function and remove the second parent. Oh, sorry. Is it second? Yeah, it's the second. We already have tiles as a private of game.h. Game class has a private variable here. Where is it? Here, line 12. Go back to where we were. Let me remove all these spaces for me. Go back to game.cpp. Okay, so we're good with this function. Now go back to the calls. We're also referencing is walkable tile. That's also okay. Let's reference in this thing called floor. We don't have that. Okay, so let's go back to main.cpp. So we have this style type. I could take that and put in game.h. That's totally fine, but I'm actually going to make a new file. I'm going to say tile type.h. And I'm going to place it here. And then I'm going to put a guard. I'm going to say if and death. Okay, so we're going to go back to game. underscore underscore tile underscore type underscore h underscore underscore. Then define the very same symbol. So the next time we won't do anything. We'll include it again. And that's the enum. And then we can include this from wherever we're referencing. I think it was in game.cpp right floor. So we can go all the way up there and include the tile type.h. When we compile game.cpp we already included. So we don't have to add this to the g++ command statements. So we got that. And seems like we are done with everything. We isolated all the logic outside of main to the game class. We created a new object called game instance of the game class. And we run all these commands to initialize the game. And at the end shut down and in between create the windows set to player color. Load the game map for debugging purposes, print the game map. Then we set up the game app surface and then set the player position initially. And then we run the game loop. Now it's time for us to try compiling it and see if we have any problems that we have to address. So I'm going to close this and do it again. And it's a lot of problems. So let's go to the very first one. As I told you before, there are likely to be problems and we just handle one by one. Let's go to the very first one at the top. And file included from main to cpp. Game.h. So it's saying from main to cpp line 5 include game.h. Let's go to game.h. Game.h line 31 column 26 line 31 column 26. I'm missing a semicolon. So I add a semicolon here. And the second problem is the same thing. We can just rerun if we want. And then go back and see now game.cpp 150 game.cpp 150 line 150. It's saying what no matching function to call page entries. Because of the second argument, we don't need that anymore because it's already a private member of the class. Right. We can see here line 13 14 we're ready to have that and we can just remove it and we can check the call the declaration. The definition here X signature doesn't have it. So we fix that problem. Let's compile it again. Next problem is map coordinate to title ID is not declaring the scope. Okay. So did I add that to the we have these two that add that to the header? Seems like I didn't write to let's copy this. Let me add it here to the public member function of game plus and do the same for the walkable tile is walkable tile. Back to game.h on public at this new function remove the prefix game.com. See that. And it finally compiled. Let's see if we're in the right place. It works. There's collision go up, go down to the right, go to the left, go down. Looking good. Let's go through the bottom here. It seems like everything's working fine. And that's our whole refactoring. And then click exit and it works just fine. Okay. So in this lesson we finished the refactoring to move all the logic from the main dot CPP file. And I forgot to save that. So let me check again. Sorry. Yeah, that's fine. Yeah, so we moved all the game logic to the game class. We created this game dot h file. And we have all these private members that are accessible by every single member function under the public, not necessarily on the public, but could be private too. The public just because we want to be able to call all these methods from the outside as we create an instance of the game object in class. And that object we can call these functions because they're public and these functions in turn they themselves can access the private data members. Because the game class is a local variable to main, its scope will be until main is over. So for the duration of the game, as long as it's running, there will be no problems with variables going away due to scoping being closed. So the game will persist. The object will exist as long as the function main is running. So until the end, we can do initializing up to the end shut down the game to make sure as the L is closed. And in between you have all these things setting up of the game until the game look of the run method. So the run is where the game look is. We do all this stuff, checking for input first, and then we do the repainting and update the Windows service. And we created this style type.h2.2 just to make things more organized. Just one thing I want to do before we move on is check these headers game class game header check for C of the includes here. So we have all these includes for using SDL. That's fine. So none of these use anything unknown here. So that's fine. Now, since I have this here, let me make sure it's not there in the game implementation. It's not here. That's fine. As of main.cpp, let's go back here. All we have to know here is the game. So it's here. Do we need to have SDL here? No. Do we need to have string here? No, we're not using. We need to have IOS stream now. F string, no. So remove all those. Let's recompose, see if nothing messed up. All right. So I think we're good here. Thank you so much for watching and see you in the next lesson. Goodbye.
No comments yet (loading...)
No comments yet (loading...)
Did you like the lesson? 😆👍
Consider a donation to support our work: