Sigh, schools.
Bottom line. No, they aren't that bad, they're incredibly simple to code.
Fine print: You can do things more efficiently without them, here was my experience.
I was trying to create a game that utilized gamestates and a state manager.
My initial thought was singletons for the gamestates.
I could switch my gamestates by going StateManager.ChangeState(Playstate::Instance());
I thought it was the most nifty and convenient thing ever.
Until I started having trouble with switching between gamestates. Since the gamestates are singletons I had to make sure all the variables that are needed in that gamestate are loaded in the initialization
ALSO I had to make sure I didn't re initialize stuff already created in other gamestates, take switching from a menu state to a playing state in a blackjack game.
I could A, start a blackjack game without a profile (Quick game)
or B, select a profile and play the game with that.
If we were to select quick start we had to make sure that the play state initialized a kind of temporary profile.
If we were to select a profile we had to make sure that this didn't happen.
Leading to heavy nesting of if's and elses (or a long switch) in the initialization of the gamestate instance.
Now we have to think about deinitializing our static gamestate, since we aren't starting from scratch (like we would without a singleton) I also have to make sure I deinitialize only the things I need deinitialized, I had to match the nested if's and elses and/or switches in the deinitilization function as well.
without a static gamestate, I can switch my gamestates like this:
state_manager->change_state(new Play_State(state_manager, game) );
Then when we move to the next state, all we have to do is call the destructor.
Now we have a uniform startup procedure.
If you take notice, I also pass the gamestate a few variable now, unlike before.
To get information between states I can refer back to my game engine.
Code:
class Play_State : public Gamestate
{
public:
Play_State(State_Manager * state_manager, Card_Game * game);
void update();
void print();
void handle_events();
};
#endif
That is my non static gamestate. I create it, I delete it. I modify those 3 functions, done.
This is my Engine class:
Code:
#ifndef GAME_H
#define GAME_H
#include <vector>
#include "Player.h"
#include "Hand.h"
#include "Deck.h"
#include "Setting.h"
#include "Resource_Manager.h"
class Card_Game
{
public:
Card_Game(){}
~Card_Game(){}
virtual void turn_logic(std::string player_id);
protected:
int calculate_winnings();
void initialize_players();
// Give player id a new hand
void give_player_hand(std::string player_id);
void shuffle_deck(int deck_id);
// Draw how_many cards to player id's hand_id from deck_idntyd
void draw_card(std::string player_id, int hand_id, int deck_id, int how_many);
void place_bet(std::string player_id, int bet);
// Split player's active hand
void split_hand(std::string player_id, int hand_id);
// check if hand value > check_value
bool check_hand_value(std::string player_id, int hand_id, int check_value);
Resource_Manager< Player > m_rPlayers;
Resource_Manager< std::vector<Hand*> > m_rHands;
Resource_Manager< int* > m_rBets;
Resource_Manager< Deck* > m_rDecks;
Resource_Manager< Setting > m_rSettings;
std::vector< Deck* > m_vDeck;
};
Granted that I'm working in a console project, and there are no perty pictures, you don't see the pointers to the renderer or the sound or the physics, but the principle is the same.
And this is what my Main function looks like:
Code:
srand ( unsigned(time(NULL)) );
Blackjack_Game * game = new Blackjack_Game();
game->init();
State_Manager * state_manager = new State_Manager;
state_manager->change_state(new Play_State(state_manager, game) );
*/
/*
while ( state_manager->running() )
{
state_manager->update();
state_manager->print();
state_manager->handle_events();
}
game->cleanup();
Look ma, no singletons!
Here is a snippet of my playstate initialization function as of the singleton era:
Code:
void Play_State::init(Card_Game * game)
{
if (game->m_betting_toggle == false)
{
// init Players
if( game->m_load_new_players == true )
{
}
// init hands
game->m_players[0].new_hand();
game->m_players[1].new_hand();
}
// init Deck
game->m_deck = new Deck;
// shuffle deck
game->m_deck->shuffle_deck();
// draw cards for players
game->m_players[0].draw(0, game->m_deck, 2);
game->m_players[1].draw(game->m_players[1].m_active_hand, game->m_deck, 2);
}
Now, that's not as bad as it was before when I was actually using that function, I think there was maybe twice as much code.
And to make it work properly, and move easily to the next state I had to have an equally or even more complex deinitialization function. Considering that some of the data generated within the handle_events function of the play state had to carry over to the next state, things like winnings and such.
Now we have this init function:
Oh wait, there isn't one, only the constructor!
All the pertinent information is stored in the game engine, so all states will have access to it.
EDIT:
To sum it up,
Use globals only when the class or variable or function will be used EVERYWHERE (which happens almost never)
Use Statics (or singletons) only when you're absolutely positive you will only need one instance in your project, even then I think the extra task of initialization /deinitialization for each singleton is too much hassle.