5
\$\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();
    }
}
\$\endgroup\$
4
\$\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.

\$\endgroup\$
  • \$\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\$ – brug Mar 20 '18 at 3:40
  • \$\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\$ – yuri Mar 20 '18 at 9:16
  • \$\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\$ – yuri Mar 20 '18 at 9:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.