Implementing a scene director

Started by
15 comments, last by agentultra 11 years, 2 months ago

I have an App class that I would like to manage a stack of objects subclassed from Scene and the main game loop. The App class' purpose is to yield control of the main game loop to the currently active Scene object.


using namespace std;

typedef unique_ptr<Scene> ScenePtr

class App {
    ...
    stack<ScenePtr, vector<ScenePtr>> scene_stack;
    ScenePtr current_scene;
    ScenePtr next_scene;

    // Don't want to go through the trouble of managing a Singleton
    // but for basic safety, don't allow copies.
    App(App& other);

    int run();

public:
    App();
    ~App();

    ...

    void startWithScene(ScenePtr scene);
    void pushScene(ScenePtr scene);
    void popScene();
};
 

There's a bunch of other code in there for initializing the display, running the actual game loop, etc. What I'm having trouble with (and can't find a decent answer on SO for) is how I can pass a pointer to a dynamically allocated Scene subclass into startWithScene, pushScene, etc.


int App::startWithScene(ScenePtr scene)
{
    current_scene = std::move(scene);
    current_scene->init();
    return run();
} 

And what I'm trying to do in main() is:


...
#include "BlankScene.h"

int main(int argc, char *argvp[])
{
    App app = App();
    app.startWithScene(ScenePtr(new BlankScene()));
}

The idea is that the Scene base class is just an interface for the App to use. The subclass provides the implementation for any given scene in a game. The scene is responsible for all of its own entities, responding to the events it's interested in, rendering itself to the display, etc. This seems like a reasonable design to me and I'm just trying to get the minimum possible infrastructure in place to get it going.

However I'm getting an error that seems unrelated. On the line in main() where I create an instance of the App the compiler is telling me that I'm calling a private constructor of the class. It wasn't giving that error before I added the class members and methods to implement the scene management stuff.

I'm starting to wonder if I'm barking up the wrong tree here. I'm reading as much as I can about smart pointers in C++11 (very nice feature) but this error is throwing me for a loop. Are there more implicit constructors or something?

(And is this even a reasonable design?)

Advertisement

Just to clarify I mean "Scene" in the sense of a 2D game, not a "scene graph" or some such. The kinds of subclasses of Scene are:

  • MainMenu
  • OverWorld
  • PauseScreen
  • ...
Just use

App app;

You don't need to copy to create one on the stack.

And it is a reasonable design.

Okay, it might help to note that I have an additional constructor:


// App.h

class App {
    ...
public:
    ...
    App(int size, int scale, const char* win_cap);
};

// App.cpp

App::App(int size, int scale, const char* win_cap)
: window_w(size),
  window_h(size / 16 * 9),
  window_scale(scale)
{
    running = false;
    display = NULL;
    window_caption = win_cap;
    
    current_scene = NULL;
    next_scene = NULL;
}

Which works just fine if I remove the App::runWithScene method and make App::run public and change the typedef for ScenePtr to a shared_ptr instead of unique... seems weird.

Ah, right. Stack. Of course. Thanks for the reply.

So the real issue reveals itself which is how to square this design with unique_ptr!

I've read in many places (and Bjarne himself says) that shared_ptr should be avoided if at all possible. Is this a case where it can be? Should I be std::move()'ing it around or is it more efficient to use a shared_ptr? (ie: should App::runWithScene push the initial scene onto the stack and point current_scene to it (shared_ptr) or should it move the reference to current_scene and leave the stack empty until a new scene is pushed? (unique_ptr)).

Look at it from the point of view of a user of the class. If you're not sure whether runWithScene puts something on the stack or not, how confused is someone just reading the interface going to be?

If you get the interface straight, then which pointer type to use is an implementation detail. Use whichever is easiest.

Sounds pretty straight forward and I'm making some progress.

I'm now trying to figure out how an implementation of the scene can request the App to push a new Scene onto the stack.

Give scenes a reference to the App.

Give scenes a reference to the App.

I'm trying to figure out the details of how to go about that. Right now I'm trying giving the Scene base class a public member that is declared as pointer-to-App. Then the App::runWithScene, App::pushScene, and App::replaceScene methods assign a reference to the pointer... but it's not quite working and I'm digging through documentation to figure out the proper way to do it.


// Scene.h

class Scene : public IEvent {
public:
    Scene();
    virtual ~Scene();

    virtual void onInit();
    virtual void onUpdate() = 0;
    virtual void onRender() = 0;
    virtual void onCleanup() = 0;
};

// Scene.cpp

Scene::Scene()
{
    app = NULL;
}

// App.cpp

int App::runWithScene(ScenePtr scene)
{
    current_scene = std::move(scene);
    current_scene->app = this;
    current_scene->onInit();

    return run();
}

Except that in my Scene subclass I'm referring to a method of the App instance and getting the error: "Member Acess into incomplete type 'App'


void BlankScene::onKeyDown(SDLKey key, SDLMod mod, Uint16 unicode)
{
    switch (key) {
        case SDLK_SPACE:
            app->pushScene(ScenePtr(new YellowScene()));
            break;
            
        default:
            break;
    }
}

So I'm looking into the pointer literature and trying to decrypt what this means. Any 'pointers' would be appreciated! smile.png

This topic is closed to new replies.

Advertisement