7
\$\begingroup\$

I am a beginner and am almost done with my 2nd C++ project. It is just another breakout clone. I recently refactored a good deal of the code in response to this question. However I have left myself with a huge God Object class and don't even know how to begin to dissect it.

This is my main:

#include "game.h"
int main()
{
 game game;
 game.start();
 return 0;
}

From what I understand this small manageable main is fairly desirable if perhaps achieved partly because I just shoved everything into my massive Manager class. Part of my difficulty in chopping up the class is because I instantiate all of the necessary game and gui objects within this class. I understand that dependency injection might help me here but am not quite sure how to go about it.

I am happy to hear any and all feedback as a beginner eagerly trying to learn and am seeking specific advice on how to refactor and redesign this huge class.

game.h

#ifndef GAME
#define GAME
#include "ball.h"
#include "bat.h"
#include "boss.h"
#include "brick.h"
#include "button.h"
#include "clickableRect.h"
#include "coin.h"
#include "collision.h"
#include "level.h"
#include "metaData.h"
#include "upgrade.h"
#include <vector>
#include <SFML/Graphics.hpp>
class game
{
private:
 // constants
 const unsigned int window_width = 700;
 const unsigned int window_height = 900;
 const float brick_top_margin = 100.f;
 const float brick_left_margin = 1.f;
 const float brick_width = 70.f;
 const float brick_height = 20.f;
 const size_t brick_collumns = 10;
 const size_t brick_rows = 8;
 const unsigned int NUM_SCREENS = 4;
 const unsigned int NUM_SUBSCREENS = 3;
 const unsigned int MENU_BUTTONS = 3;
 const unsigned int LEVEL_BUTTONS = 4;
 const unsigned int UPGRADE_BUTTONS = 3;
 const unsigned int GAME_BUTTONS = 3;
 const unsigned int SETTINGS_BUTTONS = 4;
 const unsigned int POST_BUTTONS = 4;
 const unsigned int CREDITS_BUTTONS = 1;
 const unsigned int NUM_UPGRADES = 12;
 const unsigned int UPGRADE_COLLUMNS = 3;
 const unsigned int UPGRADE_ROWS = 4;
 const unsigned int MAX_COINS = 80;
 const unsigned int WORLDS = 10;
 const unsigned int LEVELS = 9;
 const float MARGIN = 25.f;
 const float TOP_MARGIN = 3.f;
 const float BUTTON_WIDTH = 100.f;
 const float BUTTON_HEIGHT = 70.f;
 const float MENU_BUTTON_INDENT = 150.f;
 const float MENU_BUTTON_WIDTH = 400.f;
 const float SUB_BUTTON_INDENT = 180.f;
 const float SUB_BUTTON_WIDTH = 330.f;
 const float UPGRADE_MARGIN = 100.f;
 const float UPGRADE_BOX = 186.f;
 // variables
 enum screenChoice : unsigned int {MAIN_MENU = 0, LEVEL_SELECT = 1, UPGRADES = 2, GAME_SCREEN = 3, SETTINGS = 0, POSTGAME = 1, CREDITS = 2};
 enum upgradeTypes: unsigned int {BAT_SPEED = 0, BAT_SIZE = 1, MAX_SPEED = 2, BALL_SIZE = 3, BALL_STRENGTH = 4, HOMING = 5, VALUE = 6, MAGNET = 7, LIVES = 8, BOSS_DMG = 9, PHANTOM_DET = 10, TBD = 11};
 unsigned int screenIndex;
 unsigned int subScreenIndex;
 unsigned int world;
 unsigned int subWorld;
 unsigned int lives;
 unsigned int tempLoot;
 bool subScreenOpen;
 bool overWorld;
 // window
 sf::Vector2u resolution;
 sf::RenderWindow window;
 // assets
 // game objects
 ball ball;
 bat bat;
 std::vector<brick> bricks;
 std::vector<boss> bossBricks;
 std::vector<brick> displayBricks;
 metaData data;
 level level;
 std::vector<coin> coinPool;
 std::vector<upgrade> upgrades;
 // images
 sf::Texture textureMap;
 sf::Sprite introLogo;
 sf::Sprite coinMarker;
 // sounds
 // buttons
 std::vector<button> mainMenuButtons;
 std::vector<button> levelButtons;
 std::vector<button> upgradeButtons;
 std::vector<button> gameButtons;
 std::vector<button> settingsButtons;
 std::vector<button> postGameButtons;
 std::vector<button> creditsButtons;
 std::vector<std::vector<button>> screen;
 std::vector<std::vector<button>> subScreen;
 sf::RectangleShape subScreenBackground;
 void positionBosses();
 void assignLevel();
 void resetLevel();
 bool checkWin();
 void input();
 void update();
 void draw();
public:
 game();
 void start();
};
#endif // !GAME

and

game.cpp

#include "game.h"
game::game() :
 lives(2), // initialize on deserialization
 world(1),
 subWorld(1),
 screenIndex(MAIN_MENU),
 subScreenIndex(SETTINGS),
 tempLoot(0),
 subScreenOpen(false),
 overWorld(true)
{
 // create window
 resolution.x = window_width;
 resolution.y = window_height;
 window.create(sf::VideoMode(resolution.x, resolution.y), "Breakout");
 window.setFramerateLimit(60);
 // load local save data
 ball.deserialize();
 bat.deserialize();
 data.deserialize();
 // load assets
 // load font
 textureMap.loadFromFile("textureMap.png");
 introLogo.setPosition(MARGIN, MARGIN);
 introLogo.setTexture(textureMap);
 introLogo.setTextureRect(sf::IntRect(0, 0, 650, 400));
 //coinMarker.setPosition();
 coinMarker.setTexture(textureMap);
 //coinMarker.setTextureRect();
 // create buttons
 // main menu
 for (size_t i = 0; i < MENU_BUTTONS; ++i)
 {
 mainMenuButtons.push_back(button());
 mainMenuButtons[i].setSize(sf::Vector2f(MENU_BUTTON_WIDTH, BUTTON_HEIGHT));
 mainMenuButtons[i].setTexture(&textureMap);
 }
 mainMenuButtons[0].setPosition(MENU_BUTTON_INDENT, 475.f); // level select
 mainMenuButtons[0].setTextureRect(sf::IntRect(0, 400, MENU_BUTTON_WIDTH, BUTTON_HEIGHT));
 mainMenuButtons[1].setPosition(MENU_BUTTON_INDENT, 580.f); // upgrades
 mainMenuButtons[1].setTextureRect(sf::IntRect(0, 470, MENU_BUTTON_WIDTH, BUTTON_HEIGHT));
 mainMenuButtons[2].setPosition(MENU_BUTTON_INDENT, 685.f); // settings
 mainMenuButtons[2].setTextureRect(sf::IntRect(0, 540, MENU_BUTTON_WIDTH, BUTTON_HEIGHT));
 // level select
 for (size_t i = 0; i < LEVEL_BUTTONS; ++i)
 {
 levelButtons.push_back(button());
 levelButtons[i].setSize(sf::Vector2f(BUTTON_WIDTH, BUTTON_HEIGHT));
 levelButtons[i].setTexture(&textureMap);
 }
 levelButtons[0].setPosition(3.f, TOP_MARGIN); // main menu
 //levelButtons[0].setTextureRect(sf::IntRect());
 levelButtons[1].setPosition(108.f, TOP_MARGIN); // upgrades
 //levelButtons[1].setTextureRect(sf::IntRect());
 levelButtons[2].setPosition(213.f, TOP_MARGIN); // settings
 //levelButtons[2].setTextureRect(sf::IntRect());
 levelButtons[3].setPosition(318.f, TOP_MARGIN); // world
 //levelButtons[3].setTextureRect(sf::IntRect());
 //rectangle shape x = 423.f width = 275.f height = buttonheight
 //for displaying world or level info
 // upgrade
 for (size_t i = 0; i < UPGRADE_BUTTONS; ++i)
 {
 upgradeButtons.push_back(button());
 upgradeButtons[i].setSize(sf::Vector2f(BUTTON_WIDTH, BUTTON_HEIGHT));
 upgradeButtons[i].setTexture(&textureMap);
 }
 upgradeButtons[0].setPosition(3.f, TOP_MARGIN); // level select
 //upgradeButtons[0].setTextureRect(sf::IntRect());
 upgradeButtons[1].setPosition(108.f, TOP_MARGIN); // settings
 //upgradeButtons[1].setTextureRect(sf::IntRect());
 upgradeButtons[2].setPosition(213.f, TOP_MARGIN); // main menu
 //upgradeButtons[2].setTextureRect(sf::IntRect());
 //rectangle shape x = 318.f width = 380.f height = buttonheight
 //for displaying upgrade info
 // game
 for (size_t i = 0; i < GAME_BUTTONS; ++i)
 {
 gameButtons.push_back(button());
 gameButtons[i].setSize(sf::Vector2f(BUTTON_WIDTH, BUTTON_HEIGHT));
 gameButtons[i].setTexture(&textureMap);
 }
 gameButtons[0].setPosition(387.f, TOP_MARGIN); // pause (settings)
 //gameButtons[0].setTextureRect(sf::IntRect());
 gameButtons[1].setPosition(492.f, TOP_MARGIN); // reset
 //gameButtons[1].setTextureRect(sf::IntRect());
 gameButtons[2].setPosition(597.f, TOP_MARGIN); // level select
 //gameButtons[2].setTextureRect(sf::IntRect());
 // settings
 for (size_t i = 0; i < SETTINGS_BUTTONS; ++i)
 {
 settingsButtons.push_back(button());
 settingsButtons[i].setSize(sf::Vector2f(SUB_BUTTON_WIDTH, BUTTON_HEIGHT));
 settingsButtons[i].setTexture(&textureMap);
 }
 settingsButtons[0].setPosition(SUB_BUTTON_INDENT, 300.f); // resume
 settingsButtons[0].setTextureRect(sf::IntRect(0, 610, SUB_BUTTON_WIDTH, BUTTON_HEIGHT));
 settingsButtons[1].setPosition(SUB_BUTTON_INDENT, 410.f); // controls?
 settingsButtons[1].setTextureRect(sf::IntRect(0, 890, SUB_BUTTON_WIDTH, BUTTON_HEIGHT));
 settingsButtons[2].setPosition(SUB_BUTTON_INDENT, 520.f); // main menu
 settingsButtons[2].setTextureRect(sf::IntRect(0, 820, SUB_BUTTON_WIDTH, BUTTON_HEIGHT));
 settingsButtons[3].setPosition(SUB_BUTTON_INDENT, 630.f); // credits
 settingsButtons[3].setTextureRect(sf::IntRect(0, 960, SUB_BUTTON_WIDTH, BUTTON_HEIGHT));
 // post game
 for (size_t i = 0; i < POST_BUTTONS; ++i)
 {
 postGameButtons.push_back(button());
 postGameButtons[i].setSize(sf::Vector2f(SUB_BUTTON_WIDTH, BUTTON_HEIGHT));
 postGameButtons[i].setTexture(&textureMap);
 }
 postGameButtons[0].setPosition(SUB_BUTTON_INDENT, 350.f); // replay/next
 postGameButtons[1].setPosition(SUB_BUTTON_INDENT, 460.f); // upgrades
 postGameButtons[1].setTextureRect(sf::IntRect(35, 470, SUB_BUTTON_WIDTH, BUTTON_HEIGHT));
 postGameButtons[2].setPosition(SUB_BUTTON_INDENT, 570.f); // level select
 postGameButtons[2].setTextureRect(sf::IntRect(35, 400, SUB_BUTTON_WIDTH, BUTTON_HEIGHT));
 postGameButtons[3].setPosition(SUB_BUTTON_INDENT, 680.f); // main menu
 postGameButtons[3].setTextureRect(sf::IntRect(0, 820, SUB_BUTTON_WIDTH, BUTTON_HEIGHT));
 // credits
 for (size_t i = 0; i < CREDITS_BUTTONS; ++i)
 {
 creditsButtons.push_back(button());
 creditsButtons[i].setSize(sf::Vector2f(SUB_BUTTON_WIDTH, BUTTON_HEIGHT));
 creditsButtons[i].setTexture(&textureMap);
 }
 creditsButtons[0].setPosition(SUB_BUTTON_INDENT, 700.f); // settings
 creditsButtons[0].setTextureRect(sf::IntRect(0, 610, SUB_BUTTON_WIDTH, BUTTON_HEIGHT));
 // create upgrades
 for (size_t i = 0; i < UPGRADE_ROWS; ++i)
 {
 for (unsigned int j = 0; j < UPGRADE_COLLUMNS; ++j)
 {
 upgrades.push_back(upgrade(UPGRADE_MARGIN + (j * UPGRADE_BOX), UPGRADE_MARGIN + (i * UPGRADE_BOX)));
 upgrades[(i * UPGRADE_COLLUMNS) + j].setTexture(&textureMap);
 }
 }
 for (size_t i = 0; i < WORLDS; ++i)
 {
 bossBricks.push_back(boss(i + 1));
 }
 for (size_t i = 0; i < LEVELS; ++i)
 {
 displayBricks.push_back(brick());
 displayBricks[i].setPosition(100.f + (50.f * i), 750.f - (60.f * i));
 }
 screen.push_back(mainMenuButtons);
 screen.push_back(levelButtons);
 screen.push_back(upgradeButtons);
 screen.push_back(gameButtons);
 subScreen.push_back(settingsButtons);
 subScreen.push_back(postGameButtons);
 subScreen.push_back(creditsButtons);
 subScreenBackground.setPosition(window_width * .05f, window_height * .05f);
 subScreenBackground.setSize(sf::Vector2f(window_width * .9f, window_height * .9f));
 subScreenBackground.setFillColor(sf::Color(54, 69, 79, 210));
}
void game::positionBosses()
{
 if (screenIndex == LEVEL_SELECT)
 {
 if (overWorld)
 {
 for (size_t i = 0; i < bossBricks.size(); ++i)
 {
 bossBricks[i].setPosition(100.f + (50.f * i), 750.f - (60.f * i));
 }
 }
 else
 {
 bossBricks[(world - 1)].setPosition(550.f, 210.f);
 }
 }
 else if (screenIndex == GAME_SCREEN)
 {
 bossBricks[(world - 1)].setPosition(286.f, 100.f);
 }
}
void game::assignLevel()
{
 bricks.clear();
 size_t index = ((world - 1) * 10 + subWorld - 1);
 size_t k = 0;
 for (size_t j = 0; j < brick_rows; ++j)
 {
 for (size_t i = 0; i < brick_collumns; ++i)
 {
 if (level.getLevel(index)[k] > 0)
 {
 bricks.push_back(brick(level.getLevel(index)[k], brick_left_margin + brick_width * i, brick_top_margin + brick_height * j));
 }
 ++k;
 }
 }
 if (subWorld == 10)
 {
 positionBosses();
 }
}
void game::resetLevel()
{
 bat.reset();
 ball.reset();
 lives = data.maxLives;
 coinPool.clear();
 if (subWorld == 10)
 {
 bossBricks[(world - 1)].rollBack();
 }
}
bool game::checkWin()
{
 for (size_t i = 0; i < bricks.size(); ++i)
 {
 if (bricks[i].getLiving())
 {
 return false;
 }
 }
 return true;
}
void game::input()
{
 if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
 {
 window.close();
 } // end escape key exit
 if (!subScreenOpen)
 {
 if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
 {
 bat.moveLeft();
 } // left
 else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
 {
 bat.moveRight();
 } // right
 else if (screenIndex == GAME_SCREEN && sf::Keyboard::isKeyPressed(sf::Keyboard::Space))
 {
 ball.launch();
 } // space
 }
 sf::Event ev;
 while (window.pollEvent(ev))
 {
 if (!subScreenOpen)
 {
 if (screenIndex == MAIN_MENU)
 {
 if (screen[screenIndex][0].click(window, ev))
 {
 screen[screenIndex][0].reset();
 bat.reset();
 screenIndex = LEVEL_SELECT;
 positionBosses();
 } // start button
 else if (screen[screenIndex][1].click(window, ev))
 {
 screen[screenIndex][1].reset();
 bat.reset();
 screenIndex = UPGRADES;
 } // upgrade button
 else if (screen[screenIndex][2].click(window, ev))
 {
 screen[screenIndex][2].reset();
 subScreenIndex = SETTINGS;
 subScreenOpen = true;
 } // settings button
 } // main menu screen
 else if (screenIndex == LEVEL_SELECT)
 {
 if (screen[screenIndex][0].click(window, ev))
 {
 screen[screenIndex][0].reset();
 overWorld = true;
 screenIndex = MAIN_MENU;
 } // main menu
 else if (screen[screenIndex][1].click(window, ev))
 {
 screen[screenIndex][1].reset();
 overWorld = true;
 screenIndex = UPGRADES;
 } // upgrade screen
 else if (screen[screenIndex][2].click(window, ev))
 {
 screen[screenIndex][2].reset();
 subScreenIndex = SETTINGS;
 subScreenOpen = true;
 } // settings screen
 else if (!overWorld && screen[screenIndex][3].click(window, ev))
 {
 screen[screenIndex][3].reset();
 overWorld = true;
 positionBosses();
 } // overworld toggle
 if (overWorld)
 {
 for (unsigned i = 0; i < data.highWorld; ++i)
 {
 if (bossBricks[i].click(window, ev))
 {
 bossBricks[i].reset();
 world = i + 1;
 overWorld = !overWorld;
 positionBosses();
 }
 }
 }
 else
 {
 if (world == data.highWorld)
 {
 for (size_t i = 0; i < data.highSubWorld; ++i)
 {
 if (displayBricks[i].click(window, ev))
 {
 overWorld = true;
 displayBricks[i].reset();
 subWorld = i + 1;
 assignLevel();
 screenIndex = GAME_SCREEN;
 }
 }
 if (data.highSubWorld == 10 && bossBricks[(world - 1)].click(window, ev))
 {
 overWorld = true;
 subWorld = 10;
 screenIndex = GAME_SCREEN;
 assignLevel();
 }
 }
 else
 {
 for (size_t i = 0; i < displayBricks.size(); ++i)
 {
 if (displayBricks[i].click(window, ev))
 {
 overWorld = true;
 displayBricks[i].reset();
 subWorld = i + 1;
 assignLevel();
 screenIndex = GAME_SCREEN;
 }
 }
 if (bossBricks[(world - 1)].click(window, ev))
 {
 overWorld = true;
 subWorld = 10;
 screenIndex = GAME_SCREEN;
 assignLevel();
 }
 }
 }
 } // level select screen
 else if (screenIndex == UPGRADES)
 {
 if (screen[screenIndex][0].click(window, ev))
 {
 screen[screenIndex][0].reset();
 bat.reset();
 screenIndex = LEVEL_SELECT;
 positionBosses();
 } // level screen
 else if (screen[screenIndex][1].click(window, ev))
 {
 screen[screenIndex][1].reset();
 subScreenIndex = SETTINGS;
 subScreenOpen = true;
 } // settings screen
 else if (screen[screenIndex][2].click(window, ev))
 {
 screen[screenIndex][2].reset();
 bat.reset();
 screenIndex = MAIN_MENU;
 } // main menu
 if (upgrades[BAT_SPEED].click(window, ev))
 {
 upgrades[BAT_SPEED].reset();
 }
 else if (upgrades[BAT_SIZE].click(window, ev))
 {
 upgrades[BAT_SIZE].reset();
 }
 else if (upgrades[MAX_SPEED].click(window, ev))
 {
 upgrades[MAX_SPEED].reset();
 }
 else if (upgrades[BALL_SIZE].click(window, ev))
 {
 upgrades[BALL_SIZE].reset();
 }
 else if (upgrades[BALL_STRENGTH].click(window, ev))
 {
 upgrades[BALL_STRENGTH].reset();
 }
 else if (upgrades[HOMING].click(window, ev))
 {
 upgrades[HOMING].reset();
 }
 else if (upgrades[VALUE].click(window, ev))
 {
 upgrades[VALUE].reset();
 }
 else if (upgrades[MAGNET].click(window, ev))
 {
 upgrades[MAGNET].reset();
 }
 else if (upgrades[LIVES].click(window, ev))
 {
 upgrades[LIVES].reset();
 }
 else if (upgrades[BOSS_DMG].click(window, ev))
 {
 upgrades[BOSS_DMG].reset();
 }
 else if (upgrades[PHANTOM_DET].click(window, ev))
 {
 upgrades[PHANTOM_DET].reset();
 }
 else if (upgrades[TBD].click(window, ev))
 {
 upgrades[TBD].reset();
 }
 } // upgrade screen
 else if (screenIndex == GAME_SCREEN)
 {
 if (screen[screenIndex][0].click(window, ev))
 {
 screen[screenIndex][0].reset();
 subScreenIndex = SETTINGS;
 subScreenOpen = true;
 } // pause / settings
 else if (screen[screenIndex][1].click(window, ev))
 {
 screen[screenIndex][1].reset();
 resetLevel();
 assignLevel();
 } // reset level
 else if (screen[screenIndex][2].click(window, ev))
 {
 screen[screenIndex][2].reset();
 resetLevel();
 screenIndex = LEVEL_SELECT;
 positionBosses();
 } //level screen
 } // game screen
 } // pausable events
 else
 {
 if (subScreenIndex == SETTINGS)
 {
 if (subScreen[subScreenIndex][0].click(window, ev))
 {
 subScreen[subScreenIndex][0].reset();
 subScreenOpen = false;
 } // resume
 else if (subScreen[subScreenIndex][1].click(window, ev))
 {
 subScreen[subScreenIndex][1].reset();
 // control screen (new main screen?)
 } // control screen
 else if (subScreen[subScreenIndex][2].click(window, ev))
 {
 subScreen[subScreenIndex][2].reset();
 resetLevel();
 overWorld = true;
 screenIndex = MAIN_MENU;
 subScreenOpen = false;
 } // main menu
 else if (subScreen[subScreenIndex][3].click(window, ev))
 {
 subScreen[subScreenIndex][3].reset();
 subScreenIndex = CREDITS;
 } // credit screen
 } // settings and pause screen
 else if (subScreenIndex == POSTGAME)
 {
 if (subScreen[subScreenIndex][0].click(window, ev))
 {
 subScreen[subScreenIndex][0].reset();
 resetLevel();
 assignLevel();
 subScreenOpen = false;
 } // replay or next level
 else if (subScreen[subScreenIndex][1].click(window, ev))
 {
 subScreen[subScreenIndex][1].reset();
 resetLevel();
 screenIndex = UPGRADES;
 subScreenOpen = false;
 } // upgrade
 else if (subScreen[subScreenIndex][2].click(window, ev))
 {
 subScreen[subScreenIndex][2].reset();
 resetLevel();
 screenIndex = LEVEL_SELECT;
 positionBosses();
 subScreenOpen = false;
 } // level select
 else if (subScreen[subScreenIndex][3].click(window, ev))
 {
 subScreen[subScreenIndex][3].reset();
 resetLevel();
 screenIndex = MAIN_MENU;
 subScreenOpen = false;
 } // main menu
 } // postgame screen
 else if (subScreenIndex == CREDITS)
 {
 if (subScreen[subScreenIndex][0].click(window, ev))
 {
 subScreen[subScreenIndex][0].reset();
 subScreenIndex = SETTINGS;
 } // resume
 } // credits screen
 } // events during pause (or any subscreen)
 if (ev.type == sf::Event::Closed)
 {
 window.close();
 } // end close event
 } // end event listener
}
void game::update()
{
 if (!subScreenOpen)
 {
 for (std::size_t i = 0; i < coinPool.size(); ++i)
 {
 checkCollision(coinPool[i], bat);
 }
 checkCollision(ball, bat);
 for (std::size_t i = 0; i < bricks.size(); ++i)
 {
 checkCollision(ball, bricks[i]);
 }
 checkCollision(ball, bossBricks[world - 1]);
 // update
 bat.update();
 ball.update(bat);
 //update bricks and bosses accordingly
 for (std::size_t i = 0; i < coinPool.size(); ++i)
 {
 coinPool[i].update();
 }
 if (!ball.getLiving())
 {
 lives--;
 if (lives > 0)
 {
 bat.reset();
 ball.reset();
 coinPool.clear();
 }
 }
 if (lives == 0)
 {
 postGameButtons[0].setTextureRect(sf::IntRect(0, 680, SUB_BUTTON_WIDTH, BUTTON_HEIGHT));
 subScreenIndex = POSTGAME;
 subScreenOpen = true;
 }
 if (screenIndex == GAME_SCREEN && checkWin())
 {
 if (world == data.highWorld && subWorld == data.highSubWorld)
 {
 if (subWorld < 10)
 {
 data.highSubWorld++;
 }
 else
 {
 data.highSubWorld = 1;
 data.highWorld++;
 }
 }
 if (subWorld < 10)
 {
 subWorld++;
 }
 else
 {
 subWorld = 1;
 world++;
 }
 postGameButtons[0].setTextureRect(sf::IntRect(0, 750, SUB_BUTTON_WIDTH, BUTTON_HEIGHT));
 subScreenIndex = POSTGAME;
 subScreenOpen = true;
 }
 }
}
void game::draw()
{
 window.clear();
 // draw bat and ball
 window.draw(bat);
 window.draw(ball);
 // draw main logo
 if (screenIndex == MAIN_MENU) {
 window.draw(introLogo);
 }
 // draw level select screen
 if (screenIndex == LEVEL_SELECT)
 {
 if (overWorld)
 {
 for (size_t i = 0; i < data.highWorld; ++i)
 {
 window.draw(bossBricks[i]);
 }
 }
 else
 {
 if (world == data.highWorld)
 {
 for (size_t i = 0; i < data.highSubWorld; ++i)
 {
 window.draw(displayBricks[i]);
 }
 if (data.highSubWorld == 10)
 {
 window.draw(bossBricks[(world - 1)]);
 }
 }
 else
 {
 for (size_t i = 0; i < LEVELS; ++i)
 {
 window.draw(displayBricks[i]);
 }
 window.draw(bossBricks[(world - 1)]);
 }
 }
 }
 //draw upgrade icons
 if (screenIndex == UPGRADES)
 {
 for (size_t i = 0; i < NUM_UPGRADES; ++i)
 {
 window.draw(upgrades[i]);
 }
 }
 // draw game bricks and coins
 if (screenIndex == GAME_SCREEN)
 {
 for (size_t i = 0; i < bricks.size(); ++i)
 {
 window.draw(bricks[i]);
 }
 if (subWorld == 10)
 {
 window.draw(bossBricks[(world - 1)]);
 }
 for (size_t i = 0; i < coinPool.size(); ++i)
 {
 window.draw(coinPool[i]);
 }
 }
 // draw buttons
 if (screenIndex == LEVEL_SELECT && overWorld)
 {
 for (size_t i = 0; i < screen[screenIndex].size() - 1; ++i)
 {
 window.draw(screen[screenIndex][i]);
 }
 }
 else
 {
 for (size_t i = 0; i < screen[screenIndex].size(); ++i)
 {
 window.draw(screen[screenIndex][i]);
 }
 }
 // draw subscreen over everything else
 if (subScreenOpen)
 {
 window.draw(subScreenBackground);
 for (size_t i = 0; i < subScreen[subScreenIndex].size(); ++i)
 {
 window.draw(subScreen[subScreenIndex][i]);
 }
 }
 window.display();
}
void game::start()
{
 while (window.isOpen())
 {
 input();
 update();
 draw();
 }
}
200_success
145k22 gold badges190 silver badges478 bronze badges
asked Mar 16, 2018 at 5:06
\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

Order your includes

For example from narrow to broad scope:

// local code used by your project
#include "foo.h"
// libraries 
#include "external/foolib.h"
// C headers 
#include <bar.h>
// Anything from the standard library
#include <foobar>

The comments are just there for clarity and should be omitted in production code. Also make sure to leave vertical space between include guards and logical groups of includes for readability.

Prefer constexpr over const

As per Scott Meyers - Effective Modern C++

• constexpr objects are const and are initialized with values known during compilation.

• constexpr functions can produce compile-time results when called with arguments whose values are known during compilation.

• constexpr objects and functions may be used in a wider range of contexts than non-constexpr objects and functions.

• constexpr is part of an object’s or function’s interface.

Prefer using scoped enums

Scoped enums offer a number of advantages over their regular counterpart.

Example:

enum class screenChoice : unsigned int {
 MAIN_MENU = 0,
 // ...
};

Be careful with how you call constructors

There is a difference between Foo foo and Foo foo() and it might come back to bite you sooner or later.

Use member initializer lists as much as possible

While you do already make good use of them you also have an incredibly amount of initialization inside the body of your constructor. Some of that can already be moved into the list initializer. Other parts can either be moved into a separate init function or can be moved into the list init after you refactor the class.

Design

As you correctly stated, this class is too big.
You use comments to mark different segments of your code already. So what you should do instead is decompose your game class into several smaller classes based on the segments you already have in form of comments.
For example you could have a class that is responsible for resource loading, level loading, input handling etc. In the end the game class would call the functionality these subclasses provide. This will increase maintainability of your code immensly and also means you won't have to recompile everything if you change a single line.

answered Mar 16, 2018 at 12:46
\$\endgroup\$
3
  • \$\begingroup\$ I can't use constexpr inside a class (I read up on why and understand it) should I then still use const in my case or should I pull the values outside the class into global scope? Also did I do anything odd with my constructors? or were you just warning me of the potential of accidentally declaring a member function that returns Foo as apposed to a member with the type Foo? \$\endgroup\$ Commented Mar 20, 2018 at 3:40
  • 1
    \$\begingroup\$ @bruglesco (1) The reccommendation is to generally prefer constexpr if possible otherwise sticking with const is fine. In your case the screen size constants are only there to eliminate magic numbers which is good. However you could move those into main (where they could then be constexpr) for example as I would assume you eventually want to dynamically pass the resolution. (2) Regarding the constructors, it's just something to look out for. Make sure you're aware of this behavior. \$\endgroup\$ Commented Mar 20, 2018 at 9:16
  • 1
    \$\begingroup\$ @bruglesco I think the main point to take away from this is that, as you correctly realized, the class needs to be broken down into smaller pieces. Remember that a class should generally only have one thing to do and do it well \$\endgroup\$ Commented Mar 20, 2018 at 9:18

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.