grep
grep

Reputation: 4026

C++ / SDL encapsulation design help

So I am semi-new to C++, and completely new to SDL. Most of my conceptual knowledge of OOP comes from Java and PHP. So bear with me.

I am trying to work out some simple design logic with my program / soon to be side-scroller. My problem lies with trying to make my 'screen' layer (screen = SDL_SetVideoMode(...)) accessible to all my other classes; Hero class, background layer, enemies, etc. I have been loosely following some more procedural tutorials, and have been trying to adapt them to a more object oriented approach. Here is a little bit of what I have so far:

main.cpp

#include "Game.h"
#include "Hero.h"

int main(int argc, char* argv[])
{
    //Init Game
    Game Game;

    //Load hero
    Hero Hero(Game.screen);

    //While game is running
    while(Game.runningState())
    {
        //Handle Window and Hero inputs
        Game.Input();
        Hero.userInput();

        //Draw
        Game.DrawBackground();
        Hero.drawHero();

        //Update
        Game.Update();
    }

    //Clean up
    Game.Clean();

    return 0;
}

As you can see, I have a Game class, and a Hero class. The Game class is responsible for setting up the initial window, and placing a background. It also updates the screen as you can see.

Now, since my Game class holds the 'screen' property, which is a handle for SDL_SetVideoMode, I am stuck passing this into any other class (ex: Hero Hero(Game.screen);) that needs to update to this screen... say via SDL_BlitSurface.

Now, this works, however I am getting the idea there has GOT to be a more elegant approach. Like possibly keeping that screen handler on the global scope (if possible)?

TLDR / Simple version: What is the best way to go about making my window / screen handler accessible to all my subsequent classes?

Upvotes: 2

Views: 1770

Answers (5)

Loki Astari
Loki Astari

Reputation: 264571

I like the way you are doing it.

Though rather than passing the screen reference I would pass a reference to a game itself. Thus each hero object knows which game it belongs too, it can then ask the game object for the screen as required.

The reason I would do this is so that in a few years when your game is a wide and successful product and you convert it for online-play you really need to do no work. The game server will be able to easily support multiple game objects, each game object hosting multiple hero objects. As each hero object wants to draw it asks the game for the screen abd updates the screen (the screen can now very from game object to game object and still work perfectly (as long as they have the same interface).

class Game
{
   public:
       Game(Screen& screen)
          : screen(screen)
       {}
       virtual ~Game()           {}
       virtual Screen&  screen() { return theGameScreen;}

       void update() { /* Draw Screen. Then draw all the heros */ }

   private:
       friend Hero::Hero(Game&);
       friend Hero::~Hero();
       void addHero(Hero& newHero) {herosInGame.push_back(&newHero);}
       void delHero(Hero& newHeor) {/* Delete Hero from herosInGame */}

       // Implementation detail about how a game stores a screen
       // I do not have enough context only that a Game should have one
       // So theoretically:

       Screen&              theGameScreen;
       std::vector<Hero*>   herosInGame;


};

class Hero
{
    public:
        Hero(Game& game)
            : game(game)
         {game.addHero(*this);}
        virtual ~Hero()
         {game.delHero(*this);}


         virtual void Draw(Screen& screen)  {/* Draw a hero on the screen */}
     private:
         Game& game;
};

Main.

#include "Game.h"
#include "Hero.h"

int main(int argc, char* argv[])
{
    //Init Game
    Screen   aScreenObject
    Game     game(aScreenObject);

    //Load hero
    Hero     hero(game);             // or create one hero object for each player


    //While game is running
    while(game.runningState())
    {
        //Handle Window and Hero inputs
        Game.Input();
        Hero.userInput();

        //Update
        Game.update();
    }

    //Clean up
    // Game.Clean(); Don't do this
    //               This is what the destructor is for.
}

Upvotes: 2

Andrew Edgecombe
Andrew Edgecombe

Reputation: 40382

It sounds like you're looking for a way to implement the Singleton design pattern, where you would have a single Screen object. If you know you're only ever going to have a single Screen object it should work fine.

In this case you would implement a static method on the Game class:


class Game
{
    public:
        static Game *GetTheSceenObject();
    private:
        static Screen *theScreen;    // details of initialisation ommitted
}

that would return a pointer to the single Screen object.


If there is a possibility that you'll end up using multiple SDL screens, though, it may be worth creating a Draw() method in your Hero class that is responsible for drawing the hero on each of the Screens managed by the Game class by iterating through a list provided by the Game class.

That functionality could be contained in the methods of a common DrawableThingy class that Hero and Enemy are derived from.

Upvotes: 1

Seth Carnegie
Seth Carnegie

Reputation: 75150

Passing Game.screen around is more OO (though it might be better to have a getter function) than having one instance of it that can be accessed from any class, because if you have one global version, you can't have more than one Game.screen at any one time.

However if you know you'll only ever need one in the entire lifetime of the program, you might consider making Game::Screen() a public static function in the Game class that returns a private static member screen. That way, anyone can call Game::Screen() and get the screen.

Example (assuming ScreenType is the type of screen and that you store a pointer to it):

class Game {
public:
    static ScreenType* Screen() {
        if (!screen)
            screen = GetScreenType(args);
        return screen; 
    }
}

private:
    // if you don't already know:
    // static means only one of this variable exists, *not* one per instance
    // so there is only one no matter how many instances you make
    // the same applies to static functions, which you don't need an instance to call
    static ScreenType* screen;
};

// and somewhere in a .cpp file
ScreenType* Game::screen = NULL;

// and you use it like this
ScreenType* scr = Game::Screen();
// use scr

Upvotes: 0

thedaian
thedaian

Reputation: 1319

The first, and honestly, easiest solution, is to use a global variable. Yes, yes, yes, everyone says global variables are horrible, but in this situation, it's perfectly fine.

The other solution, which is a bit more work, but can result in somewhat more portable code, is to encapsulate your drawing functions into a single, static class. This way, you can draw to the screen directly without having to pass around a variable, or have to lie awake at night thinking the code review police will get you because you used a global variable. Plus, this can potentially make it easier if you ever decide to port your game to a new library. Some quick and dirty pseudocode:

class Drawing
  public:
    static void Draw(x, y, sdl_surface graphic, sdl_rect & clip=null);
    static void init(sdl_surface & screen);
  private:
    sdl_surface screen;

void Drawing::Draw(x, y, sdl_surface graphic, sdl_rect & clip=null)
{
  sdl_blit(x, y, graphic, clip);
}

void Drawing::init(sdl_surface & screen)
{
  this.screen=screen;
}

Upvotes: 1

Magnus
Magnus

Reputation: 514

I don't know if it's elegant, but what I do for the side-scrolling game I'm making is to make a show() function in each class than draws to the screen, and passing the screen handle as a parameter. Then whenever I want to draw something to the screen I just do foo.show(screen). The screen handle is in main().

Upvotes: 1

Related Questions