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();
}
}
1 Answer 1
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.
-
\$\begingroup\$ I can't use
constexpr
inside a class (I read up on why and understand it) should I then still useconst
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\$Summer– Summer2018年03月20日 03:40:30 +00:00Commented Mar 20, 2018 at 3:40 -
1\$\begingroup\$ @bruglesco (1) The reccommendation is to generally prefer
constexpr
if possible otherwise sticking withconst
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 beconstexpr
) 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– yuri2018年03月20日 09:16:55 +00:00Commented 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\$yuri– yuri2018年03月20日 09:18:19 +00:00Commented Mar 20, 2018 at 9:18