tags:

views:

69

answers:

6

I'm trying to create a Breakout clone using C++, and so have several objects (like ball, paddle, powerupicon, block, etc). I understand that it's bad practice to have them at global scope, so they're initialized inside main(). The problem comes in with needing to do stuff with those objects from inside other functions (like redraw(), or reset_game()). The only method I can think of to do this would be to pass references of the objects into each function, but I'm not sure if this is the best approach.

Basically, what's the best way to do stuff with these objects from within a function? Pass them by reference? Make them global but within a namespace? Or something completely different?

A: 

If it's initializing order your worried about, you could have a pointer to them at global scope possibly in a namespace, and allocate memory for them in main.

Joe D
A: 

I would create a manager object that has all these informations. Like a class Game with a member paddle, bricks, items, etc. pp. Then you could, for example, have Game have a class method currentGame that returns the current game instance. This is sort of like having it at "global scope" but in a more OOP fashion.

E.g.:

class Game {
  public:
    Paddle *paddle;
    vector<Brick> *bricks;
    vector<Item> *items;

    static Game *currentGame();
    static void setCurrentGame(Game *newGame);

  private:
    static Game *currentGameInstance;
}

static Game *Game::currentGame() {
  return currentGameInstance;
}

static void Game::setCurrentGame(Game *newGame) {
  currentGameInstance = newGame;
}

(I'm very rusty with C++, hope the syntax is correct)

That way you could do Game::currentGame()->paddle everywhere you like.

DarkDust
A: 

"Best" is kinda subjective; you've named the standard approaches (ie: global within a namespace or effective equivalent, or pass into each function).

What I tend to do, design-wise, is group objects together by logical grouping (eg: some things inside a "gamestate" object, other things inside a "UI assets" object, etc.), and then have effective global instances of those objects. I'd recommend always having an effective namespace for the objects: this can be literally, or inside something like a singleton or named instance factory paradigm. Generally, the more "local" the object are, the more I'd tend to pass them explicitly into the functions which use/manipulate them; the more "application level" the objects are, the more I'd tend to make them effective globals.

Hope that helps.

Nick
A: 

Quoting from cppreference:

The static keyword can be used in four different ways:

  • to create permanent storage for local variables in a function,
  • to create a single copy of class data,
  • to declare member functions that act like non-member functions, and
  • to specify internal linkage.

There are also other examples there.

void foo()
{
    static int counter = 0;
    cout << "foo has been called " << ++counter << " times\n";
}

int main() 
{
    for( int i = 0; i < 10; ++i )
        foo();
}
karlphillip
+2  A: 

A simple (not necessarily flexible or powerful) approach is to define a base game_object class that defines your interface with game objects, and store those. Your objects inherit from it.:

class game_object
{
public:
    virtual ~game_object() {}

    virtual void update() = 0;
    virtual void draw() = 0;

    // get and set position
    virtual vector3 position() = 0;
    virtual void position(const vector3&) = 0;

    // etc...
};

class ball : public game_object
{
public:
    void update() { /* update the ball */ }
    void draw() { /* draw the ball */ }

    // etc
};

// etc

Now you have a common way of using a game object. Next, you can store these in a map (preferably an unordered_map if you have Boost, TR1, or C++0x), and make that map globally available:

// game_object_manager.h
typedef std::map<std::string, game_object*> game_object_manager;

extern game_object_manager manager;

You define this in a translation unit (either main.cpp or a game_object_manager.cpp) somewhere. Lastly, you use it by inserting things by name:

// somewhere, add the ball
manager.insert(std::make_pair("ball", new ball()));

And use it:

game_object* b = manager["ball"];
if (!b) /* there is no ball object */

// use b as ball */

Again, this is a simple solution and isn't necessarily robust, a best practice, or flexible. But for a first game, it'll work fine. (To improve it, you want to look into exception safety, smart pointers, and other things (game books). I recommend you do this before you try to make games.)

GMan
@gman I like your solution (the inheritnace part) but would recommend to not use global variables and rather pass the manager as a reference to all the functions that need it - as sooner or later you have 2 players or a server - what requires more than one instance of the manager.
David Feurle
@David: Like I said a couple times, this isn't a flexible solution, and pretty poor in modern game engine design. I don't follow your argument for more than one manager, though. What about two players implies you need two managers? It's the same objects in the game.
GMan
This seems to be the best the solution as far as I can see. I'm making a Breakout clone mostly as an exercise in C++ and OOD, and by no means expect to be on par with industry standards or anything. I've still got a few years before I actually start taking any programming courses :D
Lewis
A: 

The chapter on Singletons in Game Programming Patterns may help you, specifically the "What We Can Do Instead" section.

munificent