• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
agentultra

Implementing a scene director

16 posts in this topic

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?)

Edited by agentultra
0

Share this post


Link to post
Share on other sites

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
  • ...
0

Share this post


Link to post
Share on other sites
Just use

App app;

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

And it is a reasonable design.
2

Share this post


Link to post
Share on other sites

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.

Edited by agentultra
0

Share this post


Link to post
Share on other sites

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)).

0

Share this post


Link to post
Share on other sites
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.
2

Share this post


Link to post
Share on other sites

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. 

0

Share this post


Link to post
Share on other sites

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

Edited by agentultra
0

Share this post


Link to post
Share on other sites

I figured it out (I was using a forward declaration and forgot to include the header in my implementation of the derived class).

 

Only now I have to figure out how to push new scenes from a scene...

 

void BlankScene::onKeyDown(SDLKey key, SDLMod mod, Uint16 unicode)
{
    switch (key) {
        case SDLK_SPACE:
            std::cout << "You pressed space in the blank scene!" << std::endl;
            app->pushScene(ScenePtr(new YellowScene()));
            break;
            
        default:
            break;
    }
}

 

This will throw EXC_BAD_ACCESS.

 

I'm going to assume it has something to do with my implementation of pushScene and unique_ptr?

void App::pushScene(ScenePtr scene)
{   
    scene_stack.push(std::move(current_scene)); // current_scene becomes NULL
    next_scene = std::move(scene);
    next_scene->app = this;
}
Edited by agentultra
0

Share this post


Link to post
Share on other sites

Ok, solved my own problem. Funny that.

 

Okay, getting close to something useable. I'll post the rough framework once it's done so others may benefit.

0

Share this post


Link to post
Share on other sites
Hah, there's a bug! See if you can spot it. :)

[spoiler]Hint: what's really happening when we hit onUpdateScene? (Ignore the unfinished game timer stuff... not important. ;)[/spoiler] Edited by agentultra
0

Share this post


Link to post
Share on other sites

Well success, I've cleaned out the bugs and added the cleanup calls in the right places. It runs under Valgrind and Clang's static analyzer with no warnings or errors. I leave it up to the reader if they're interested in using this code to figure out where those bugs are and how to fix them. It's pretty simple really.

 

The next step (probably after I get sprites and animations happening) is to abstract out the scene definitions into a sqlite file or something so that I can define new scenes on the fly without touching the engine code.

 

But one step at a time. Small victories.

Edited by agentultra
0

Share this post


Link to post
Share on other sites
I skipped trough your post, mostly interested by how you write, it's very Andre Lamothe and will be great for beginners to game dev and c++ - once you get your code rock solid and you're more confident that what you've written isn't necessarily the 'right' way to do things, but perfectly acceptable, you should consider writing a full blog or something - you have a talent for writing.

Whilst skipping very quickly through the code, I noticed that your scenes implement IEvent. Conceptually that doesn't really make sense. I would assume Scenes can accept IEvents, but they aren't an event themselves. I would consider renaming this particular IEvent interface to IEventHandler or something similar. It's the IEventHandler that would accept IEvent implemented objects and would likely impose a HandleEvent(IEvent event) type method.

It's a cosmetic thing but might be a bit confusing for beginners if they are just picking up OO concepts or C++

Keep up the good work.
1

Share this post


Link to post
Share on other sites

Well thank you very much. smile.png


Also of note, I've learned quite a bit more about smart pointers since I wrote this: http://agentultra.com/sdl-and-modern-c-plus-plus-images.html


One thing to be careful about when passing unique_ptr objects into function calls as I do in the examples above is: argument evaluation order. Some languages have a defined order in which to evaluate function arguments. C++ does not. Here it's probably not an issue but imagine a function that takes a unique_ptr and a couple of other values:

 

void myFunc(std::unique_ptr<MyType> x, int y)
{
...
}

// and then later on we call it like so...

myFunc(std::unique_ptr<MyType>(new MyType()), computeY());
 

 


What do you think will happen here if computeY throws an exception?


The answer: who knows? However depending on the order that the run-time chooses to evaluate the arguments in we could leave a dangling reference on the heap to that shiny, new MyType. Scott Meyers recommends (and I whole-heartedly agree) that you should declare your smart pointers in their own expression to avoid such awkward situations.

 

std::unique_ptr<MyType> my_ptr(new MyType());
myFunc(std::move(my_ptr), computeY());


Will work. Just look up Item 17 in Effective C++. You do already have three copies of that book, right? One for beside your bed, one for your desk, and one in the bathroom? If you want to program your games in C++ then you should have at least one copy. It's indispensable.


Anyway, my code is getting a little better. My 'engine' is coming along (if you can call it that). I'll post more tutorials as I come up with them.

Edited by Kylotan
Fixed formatting presumably mangled by the notorious editor
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0