• 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
Subotron

Engine: allow modules to access eachother (singleton?)

25 posts in this topic

I'm currently designing what I call an engine, but is basically a framework for my upcoming game project. I'm having some serious issues concerning access of modules by different modules. My engine base class is the EngineApp, which consists of a number of modules, like a texture manager, window manager, renderer, error manager, etc. Now, this EngineApp should, in my opinion, be globally accessible because each module might need to access different modules. For example, if something goes wrong in the texture manager because the renderer tries to access a non-existant texture, the error manager should handle the error, which in turn may mean it needs to tell the window manager to exit. Since these modules are all defined in EngineApp, the simplest thing to do (to me) is to make the engine app a singleton class. However, in a game, it would be useful to derive a class from the EngineApp (say GameApp). However, if I make the EngineApp singleton, the GameApp class will call the functions from EngineApp (so GameApp::GetSingletonPtr()->Run(); would actually execute EngineApp::GetSingletonPtr->Run();). This is not intended obviously. Is there a way, with or without singleton usage, to fix these global access problems? I could obviously pass pointers to all modules that are needed when working in some module, but that doesn't seem like a very good idea to me. Now I know how most people think about singletons, and I try not to use them if I can do without, but for now I don't see another solution that works equally well. Who can help me? Edit: not that I think it matters, but for the record, the engine code should ultimately be put in a lib or dll, while the game code (and thus also the GameApp class) is in another project that uses the library.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Subotron
I could obviously pass pointers to all modules that are needed when working in some module, but that doesn't seem like a very good idea to me.

Why doesn't this seem like a good idea?
0

Share this post


Link to post
Share on other sites
Quote:
For example, if something goes wrong in the texture manager because the renderer tries to access a non-existant texture, the error manager should handle the error, which in turn may mean it needs to tell the window manager to exit.


This way the error manager will have to know about all the modules, and know how they want to handle errors. It would be much better if the error manager would fire an event saying there was an error (with the relevant information), and the other modules would respond accordingly. That is, the error notification should be completely separate from the error handling (in fact, that's exactly what exceptions and error codes are for).
0

Share this post


Link to post
Share on other sites
Because for the described scenario, the renderer would need a pointer to the texture manager, error manager, and as the engine expands probably a lot more. That would give messy constructors with 10 parameters that will always have the same value. Engines I used in the past (not my own) don't seem to take this approach either, and I was always very happy about that :p
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Gage64
Quote:
For example, if something goes wrong in the texture manager because the renderer tries to access a non-existant texture, the error manager should handle the error, which in turn may mean it needs to tell the window manager to exit.


This way the error manager will have to know about all the modules, and know how they want to handle errors. It would be much better if the error manager would fire an event saying there was an error (with the relevant information), and the other modules would respond accordingly. That is, the error notification should be completely separate from the error handling (in fact, that's exactly what exceptions and error codes are for).


I guess you are right. You kind of caught me, I don't do error _handling_ yet, mostly just logging of the error and quitting. But either way, the problem still exists that the module where the error occurs should call the error handler. I could just pass a pointer to the error handler for every module, but that really doesn't seem like an elegant solution to me. Is this just me?
0

Share this post


Link to post
Share on other sites
Quote:
That would give messy constructors with 10 parameters that will always have the same value.


This suggests that there isn't enough abstraction/indirection in your design. You might want to look at the Facade Pattern for a simple example of this. Also, some of the articles in the first link in my sig might also be helpful.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Subotron
Because for the described scenario, the renderer would need a pointer to the texture manager, error manager, and as the engine expands probably a lot more. That would give messy constructors with 10 parameters that will always have the same value. Engines I used in the past (not my own) don't seem to take this approach either, and I was always very happy about that :p

And lo, we find the real problem - too many cross-dependancies between systems. Or, more likely, you think theres too many dependancies between systems. Often when you actually start passing systems as parameters then you'll find you have much less cross talk than you initially thought. Give it a go and see. If you still have too many dependancies then take a look and see which ones are reasonable and which ones are inappropriate and see how you can remove or reduce them.
0

Share this post


Link to post
Share on other sites
Quote:
the problem still exists that the module where the error occurs should call the error handler.


Not really. The module should only report the error.

I don't know the best way to implement this, but for example, your error manager could allow other modules to register handlers for specific events. When a module generates an error that might interest other modules, it will tell the error manager about it, and the error manager will in turn iterate through the list of registered handlers for that error and call each one with the relevant error information. Note that the error manager doesn't know anything about the modules that are registering handlers (the error "listeners"). It simply calls the handlers, not knowing to who they belong.

Again, I don't know if this is a good implementation (in fact, I think it's not...), but it demonstrates the indirection I was talking about earlier.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Gage64
Quote:
That would give messy constructors with 10 parameters that will always have the same value.


This suggests that there isn't enough abstraction/indirection in your design. You might want to look at the Facade Pattern for a simple example of this. Also, some of the articles in the first link in my sig might also be helpful.

I'm not quite sure if I understood, but would the EngineApp class in this case be the facade? Basically this simply means that this Facade hides the nasty code with pointer passing and such so that the end user doesn't notice it happening?
0

Share this post


Link to post
Share on other sites
Quote:
I'm not quite sure if I understood, but would the EngineApp class in this case be the facade? Basically this simply means that this Facade hides the nasty code with pointer passing and such so that the end user doesn't notice it happening?


I'm not sure that's a good example. I think the subsystems in the engine are too varied to allow you to give them one interface (at least not one that isn't composed of 300 functions...).

One example that comes to mind is the rendering system. It has many internal components that are conceptually separate (the transformation pipeline, the clipper, the lighting processor, the texture mapper, the rasterizer, ...), but you can have one interface that simplifies the interaction with all these systems and even hides some of them from you (sorry if this is a bad example, maybe someone can give a better one).
0

Share this post


Link to post
Share on other sites
You can make modules unaware of each other using signals and slots:


class CriticalErrorHandler {
public:
virtual ~CriticalErrorHandler(){}
virtual void handle() = 0;
};

class ErrorHandler {
public:
void addCriticalErrorHandler( CriticalErrorHandler * c )
{
criticals.push_back(c);
}

void onCriticalError()
{
std::for_each(criticals.begin(),criticals.end(),std::mem_fun(&CriticalErrorHandler::handle));
}
private:
std::vector<CriticalErrorHandler *> criticals;
};

class Window {
public:
void close(){ std::cout << "closing window!" << std::endl; }
};

class CloseWindowOnCriticalError : public CriticalErrorHandler {
public:
CloseWindowOnCriticalError( Window *window ) : window(window) {}
virtual void handle()
{
window->close();
}
private:
Window *window;
};

int main()
{
Window window;
ErrorHandler errors;

errors.addCriticalErrorHandler( new CloseWindowOnCriticalError(&window) );

errors.onCriticalError();
}




Note how the Window knows nothing about the ErrorHandler and the ErrorHandler knows nothing of the window. Using boost we could clean it up with smart pointers etc. This is a simplified example. Boost has signals and slots stuff too, but I haven't really used them (most of the stuff I do with events like that is in Lua, so everything is just a Lua function).
1

Share this post


Link to post
Share on other sites
Quote:
Original post by Subotron
Because for the described scenario, the renderer would need a pointer to the texture manager, error manager, and as the engine expands probably a lot more. That would give messy constructors with 10 parameters that will always have the same value.

You mentioned two so far. What are the other 8?
My point is, obviously, that you can't really design your code around "probably". Perhaps one day you'd need to pass in 800 references. Or perhaps not. All you can do is look at what you have now, and what you're going to need. Which 10 components is the renderer going to need access to? And why?

So far, your renderer needs access to two components (texture and error). Why couldn't those be passed in the constructor?

And what is the alternative? Having it access the same 10 different components, *without getting references to them*? That's just as messy, but now you've hidden the dependencies behind a singleton. Now you don't even *know* which components depend on what. That is messy.

Of course, to tidy things up, you can always bundle a few parameters together in a struct before passing them to the renderer. Give it a struct containing references to texture manager and other graphics-related components all in one neat package. Without using singletons or other kinds of glorified globals.
0

Share this post


Link to post
Share on other sites
Quote:
Original post by rip-off
You can make modules unaware of each other using signals and slots:
...


Shouldn't Window know about ErrorHandler? How else will it notify that there was an error? (and who calls onCriticalError()?)
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Gage64
Quote:
Original post by rip-off
You can make modules unaware of each other using signals and slots:
...


Shouldn't Window know about ErrorHandler? How else will it notify that there was an error? (and who calls onCriticalError()?)


Well, someone needs a reference to the error handler to trigger the error. Personally I think the error handler is a poor example, I use exceptions and in my code the window will be closed if the exception stack unwinds past its scope. But I worked with the example I was given.
0

Share this post


Link to post
Share on other sites
Ok, what if an end user would want to load a model and add it to the scene manager (or whatever). The user would do something like


smart_ptr<Model> = new Model( fileName );
SceneMgr->Insert( model );



at least, that's what I'd like it to be like. Now suppose something goes wrong while creating the model (say, the file doesn't exist). This error would have to be logged and handled, but for this to work the model instance would need access to the error handler. This would change my call to something like this:


smart_ptr<Model> model = new Model( fileName, errorHandlerPtr );
SceneMgr->Insert( model );



The same would go for any other call to an engine function done by the end user. Now, instead of using the constructor of the model I could also write an Init function that returns an error code if something goes wrong, which could then be sent to the error handler, but that would only make things worse code-wise:


Model* model = new Model();

int ret = model->Init ( fileName );

if ( ret > 0 )
{
ErrorHandler->Handle( ret );
}
else
{
SceneMgr->Insert( model );
}



What I'd want is the constructor to initialise the object, and be able to notify the engine if some error occured, and if possible I'd stay clear of having to pass the error handler for object that is created. Also, in my engine managing objects (memory) is done by the manager, so preferably an object always gets inserted into the manager, but when something goes wrong while creating it the manager deletes the object, thereby making sure there are no memory leaks. Or, to state it differently, I'd like to pass ownership of objects to their respective managers asap so I dont have to worry about the memory usage anymore.
0

Share this post


Link to post
Share on other sites
People often say that one's design should be modular. Sadly, many people take this as meaning «use modules.» Having modules in a program does not mean that the program is modular. This is generally the point where I whip out the strong coupling and zero-dependency diagrams and beat your brain into submission, but my law school exams have been going pretty well so I'll try a nicer approach today.

You know what's modular in the real world? Condoms. They can be used as a contraceptive, they can be used to prevent STDs, they can be used as a barrel plug on paintball guns, they can be used to protect a live gun barrel from moisture when wading through rivers, they can be used to smuggle liquids or powders, and so on. And what makes condoms so modular in the first place?

  • Easy to set up. Rip the pack open, unfold, perform task. You should nt have to go through a dozen hoops and fill ten forms to get a working object.

  • Simple and general concept. It's an elastic, impermeable, transparent pocket. Although its primary use (the one it's optimized for) is prevention of insemination, it is not restricted to doing only that. In the same way, when you design a portion of your game, determine if the code you are writing is not overly restrictive. Perhaps it could be used to do other similar things in other places?

  • High availabiliy. Condoms are extremely cheap and easy to get your hands on, which proves they have not been designed by your average software engineer. I can see it now:

    «The user will only need one condom to get his thing on, right?
    — Right. Singleton?
    — Deal.»

    The fundamental point to condoms is that you can get as many as you need. If you design your objects in terms of "you only need one" then by design you are restricting reuse.

  • Standalone. It doesn't require other things to work. You don't have to plug it in. You don't have to use an Adapter pattern to fill it with distilled vodka. You don't have to set up a render-target window and you don't have to run a network thread in the background. Have your objects work on their own, like a condom: a condom needs itself, and an object to be placed around. Your modules should need themselves, and a target task to be placed around.
2

Share this post


Link to post
Share on other sites
in response to my last post, would it be a good idea to combine the error handler with exceptions here to avoid having to pass the error handler? So something like this:


// Function in the engine that is overridden by the end user to load models
virtual CustomInit();

Engine::Init()
{
try
{
CustomInit();
}
catch ( Exception )
{
ErrorHandler->Handle( Exception );
}
}


And just throw exceptions in the constructor of a model if something goes wrong?
0

Share this post


Link to post
Share on other sites
Quote:
Original post by Subotron
in response to my last post, would it be a good idea to combine the error handler with exceptions here to avoid having to pass the error handler?


I would rather use a functional approach. Caml pseudocode:

(* Unhandled code *)
let _ = frobnicate(42)

(* Handler *)
let handle_not_found f =
try
f ()
with
| Not_found -> 91

(* Handled code *)

let _ = handle_not_found (fun () -> frobnicate(42))


Other than that, I'd say "handle the error" is a bad abstraction to add yourself, because it's already provided by exceptions.
0

Share this post


Link to post
Share on other sites
Thanks for responding, but that pseudocode doesn't really clear things up. Probably because I don't know Caml.

I think I do get what you mean by it being a bad thing to add the abstraction ErrorHandler->Handle( Exception ). Handling it can be done locally in such a case, but the error still needs to be reported. So I made a small change in the code below.

From what I read the past hours, about RAII and other stuff, I understand my proposed method would in fact be a good way to go. If the function looks like this:


Engine::Init()
{
try
{
CustomInit();
}
catch ( Exception )
{
Logger->Write( Exception );

if ( Exception.level == ERR_CRITICAL )
Exit();
}
}

// Override done by the end user
Engine::CustomInit()
{
boost::shared_ptr<Model> mdl = new Model ( file ); // If an exception is thrown, the model cleans itself up and re-throws the error
SceneMgr->Insert ( mdl );
}







In case of everything loading fine, no problems. In case of errors, the model never gets added into the scene manager because an exception is thrown. The model will be properly deleted due to the smart pointer, and without having to pass the logger (or error handler) the engine is still notified of the error, and might respond to it if necessary.

This would mean I only have to pass the error handler/logger to modules, and just put other end-user related code that might throw engine exceptions inside a try { ... } catch { ... } block to catch the errors. End users could add custom exceptions and ways to handle them, so there's no restrictions there.

Is there anything wrong with this approach? Will it suffice if the CustomInit function does more than just load one model, or should those different actions be surrounded by additional try - catch blocks? I mean, say I load 3 models, and the second one throws an exception. This doesn't necessarily mean I can't keep using the 1st (and 3rd) models, but it might. As far as I see, catching the exception this way might ruin things for me because everything executed after throwing the exception never actually gets executed. What if some not-so-serious error occures at the first step of CustomInit, and I still want to execute the remainder of the function?

Thanks for all the help so far, I think I already got a better understanding but I want to get this right, since it is the basis for most of the engine.
0

Share this post


Link to post
Share on other sites
For your constructor initialization problem...I'm not sure if it's necessarily the best way to solve it, but you could use some sort of factory function for instantiation of your Model class. This would allow you hide the details of your error/exception handling from the code that's using your framework, and give you an opportunity to pass it references to modules that are internal to your frame work (texture managers, error handlers). This approach also gives you some other interesting options, such as using a custom memory allocator to allocate memory for the models. Or you can have your function return an abstract interface class, if you want to completely hide the public interface from the implementation.

Just trying to give you some ideas to work with, at any rate. [smile]
0

Share this post


Link to post
Share on other sites
Quote:
Original post by MJP
For your constructor initialization problem...I'm not sure if it's necessarily the best way to solve it, but you could use some sort of factory function for instantiation of your Model class. This would allow you hide the details of your error/exception handling from the code that's using your framework, and give you an opportunity to pass it references to modules that are internal to your frame work (texture managers, error handlers). This approach also gives you some other interesting options, such as using a custom memory allocator to allocate memory for the models. Or you can have your function return an abstract interface class, if you want to completely hide the public interface from the implementation.

Just trying to give you some ideas to work with, at any rate. [smile]

thanks for the suggestion. This might be a good idea actually. I'm not sure if I understand what you mean with "factory function" (reading up on design patterns right now due to the suggestions in this thread, I found out I missed out on quite a lot of theory which I should've known before starting this project, exception handling included) but as far as I get it you mean I'd just write a function that encapsulates creating a model instance and adding it to the manager, and possibly other stuff like error handling, etc.

Just to check if I got it right (keeping in mind that a scene manager might not be the best place to do this), I could do something like this:


CustomInit()
{
SceneMgr->Insert ( file );
}

SceneMgr::Insert( const std::string& file )
{
try
{
boost::shared_ptr<Model> mdl = new Model ( file );

Insert( mdl );
}
catch ( Exception )
{
localErrorHandlerPtr->Notify( Exception );
}
}

0

Share this post


Link to post
Share on other sites
Ahh this old problem. I have recently been in your position of trying to understand how all this different parts can possibly communicate with each other. You might want to consider an event based system whereby an object listens for events ocurring in another object and will respond to ones relevant to it. Using this system I would have your main engine class listen for "Error Events" so it can shutdown the engine or do whatever. This is along with the error handling within the object itself as well. Sorry I can't contribute more but I'm very bad at communicating my ideas in text form.
0

Share this post


Link to post
Share on other sites
I suggest taking a look at the XNA framework (it's C#, but bear with me), in particular, the Game, GameComponent, and GameServices classes.

Here's an article that explains it.
http://www.nuclex.org/articles/xna-gamecomponents-and-gameservices

Relating it to your example, Game would be your EngineApp, while the various manager classes would be derivatives of GameComponents. GameComponents' constructors are passed a reference to the Game class on creation, which they use to register themselves through a Listener pattern. Game keeps a list of GameComponents and calls their overloaded Initialize and Update members. So, a component could technically access other components through this list by keeping a reference to the Game class. However, this would cause more problems down the road because it doesn't eliminate the tight coupling and dependencies between components. It also introduces a problem with order, depending on which components are registered first.

So, XNA introduces the GameServices class. Any class, even a GameComponent/manager, that provides services to other components inherits from GameService and registers itself with Game, using a unique key (in XNA, this is the class type) for identification. A component can then check with the Game class whether a Service with a given key has been registered and if so, get a reference to it. In your example, ErrorHandler could be such a Service. The TextureManager GameComponent could get and keep a reference to the ErrorHandler service and call its logging member functions (e.g., Write) when required.

This polymorphism and indirection allows you (or someone else) to create derived error handlers with different behavior, such as writing to a log file asynchronously, logging to a remote database, etc simply by registering the new class as a service with the right key. No change necessary in the other components that use this service class, as long as they use the same interface functions (Write).

However, there's still a coupling problem with this because you still access the Service by casting it to a derived class and using its interface directly. e.g., Write(...).

Instead of using Services, we've implemented our inter-component communications through an event messaging system, essentially like the signal & slots (Listener pattern) system recommended in rip-off's post. Game, or a class derived from it, would either maintain an event queue that it processes every frame, or provide a sort of TriggerEvent() function that immediately dispatches events/messages. Components register themselves with Game, again using the Listener pattern, to be notified when events that they are interested in are processed.

Using the error handler example again, a ResourceMissing event is either queued or triggered immediately. When this event is processed, any registered listeners can do whatever it needs to. For example, you could have an error logger listen for this event and log a message, AND you could also have a ResourceManager listen for this event and deal with the problem by creating a placeholder texture, stream the texture from DVD, or a remote server, etc.

Therefore, the events that a component listens to (understands), and generates in return (sends), becomes a nice, extensible API for that component. You can now control how the component interacts with the outside world, instead of giving strangers full access using a raw pointer. If you add new events to the component, it won't affect older components (they don't know about the new event types) and vice versa (you won't have to modify your component because of a new component's event types that you aren't interested in).

Of course, with all this indirection, you pay for it in performance, compared to directly calling functions across modules (spaghetti!). You have to pick what is better for your needs. What you gain is better decoupling, modularity, and extensibility, which is great for prototyping and upgrading components.

You might also extend this framework by allowing GameComponents and Services to be registered from DLL libraries to create a plugin system. I would add a Shutdown callback from Game to allow proper clean up though.

NOTE: in XNA, which uses C#, multiple inheritance isn't really a problem, but it's something I prefer to avoid in C++. So, our GameComponents that also provide services actually implement the Service interface through the PIMPL idiom, instead of inheriting from both GameComponent and Service.
0

Share this post


Link to post
Share on other sites
Yes that's pretty much what I meant, except that you probably want to return a reference to the Model so that the code using the framework has something to manipulate. If you're using interface classes, it would look something like this:



class IModel
{

public:
virtual void DoSomething () = 0;
virtual void DoSomethingElse () = 0;
};

class CModel
{

public:

virtual void DoSomething ();
virtual void DoSomethingElse ();

private:

virtual void PrivateFunction ();
};

...

IModel* SceneMgr::Insert( const std::string& file )
{
try
{
boost::shared_ptr<Model> mdl = new Model ( file );

Insert( mdl );

return mdl.get();
}
catch ( Exception )
{
localErrorHandlerPtr->Notify( Exception );

return NULL;
}
}






0

Share this post


Link to post
Share on other sites
I've always handled my engines the same way. Make one main engine class then have the components of the engine as public members and in their constructor just pass them a pointer to the engine. Voila components can access components. Just gotta make sure you use it correctly. Also I have begun relying on an event system to handle the more complex things in my engine. Never had a problem with this design.

Each component for me is a manager class (i.e. EntityManager, GUIManager, EventManager, etc).

public EngineName
{
EventManager eventManager = new eventManager(this);
}
you get the idea. Then in the Manager classes you can do e.errorManager... (where e is the public pointer that points to the engine).
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