Sign in to follow this  
DesCr

Let's design a tiny engine

Recommended Posts

With all my engine design problems lately, I've devided to strip it down to a few systems that I know how to work with. The overall design is still what I'm questioning. Here is what I want the stripped down system to do: 1. User opens "game" 2. A script file is called 3. Results of the script file are displayed. I'm very OO, so the objects I want to create are Direct3D, Kernel, Script, and Texture. The Kernel will contain a Direct3D and Script instance. Direct3D will take care of blitting and flipping buffers, and Script will basically be a script handler. For test purposes, I'll create a script that just creates a textured quad on the screen and moves it down from top to center. What would be my best design options here? Should I make Direct3D a singleton? Should anything else be a singleton? Should I avoid them? I'd like to implement this without singletons since that's what I'm told is best, but how would I go about this?

Share this post


Link to post
Share on other sites
A singleton ensure that a class has only one instance and provide a global point of access to it.

There are many ways to create a singleton, here is a way to do it:


//Ok this is the Sound class, a singleton
class Sound
{
protected:
static Sound *Instance;
public:
static Sound *Inst()
{
if(Instance==NULL) Instance = new Sound;
return Instance;
}

void Init();
void PlaySound(int id);
void Shutdown();
};
Sound *Sound::Instance=0;//Set the instance pointer to null


Now here's how you can use your singleton ANYWHERE in your program.


//Let's say this is GameEngine.cpp
void Engine::Init()
{
Sound::Inst()->Init();
}
void Engine::DoFrame()
{
Sound::Inst()->PlaySound(53);
//Do other stuff...
Sound::Inst()->PlaySound(33);
}


You never have to create any object externally since the class will dynamically create the object by itself when calling the static function Inst() for the first time.

The problem right now is that the dynamically created 'Instance' won't be deleted.
I would add a "delete Instance;" in the Shutdown function of the class Sound and make sure to not forget to call it... but I believe there's a better way to delete the instance.
Unfortunatly I rarely use Singletons so I don't really know..

But I suggest you to do some search on google for singletons, there are a lot of info about them...

One last thing, here's a great article for building an engine:
http://www.gamedev.net/reference/programming/features/enginuity1/

Share this post


Link to post
Share on other sites
I don't like that article. It's way too advanced for what I'm trying to do right now. He goes way into server/client architecture, smart pointers, etc. I'm not even trying to implement memory management yet.

Share this post


Link to post
Share on other sites
No good ever came from singletons in my experience. It seems that everyone goes through a singleton obsession phase, then realizes they are crap, and just uses a global like they should have in the first place. I've never had a legitimite use for a singleton, and rarely seen others have them. For what I was using them for, globals did the exact same job, without the hassle across DLLs. 'Shared' is equivilent to 'static'.

edit: In my limited experience with VB.Net, where I've seen Shared it is the same thing as static. I'm not sure exactly what you mean by Shared clasess. If you mean that every member is Shared, or you can accomplish the same thing by putting Shared in front of the class declaration, then yes, it is the same as static.

Share this post


Link to post
Share on other sites
Quote:
Original post by Kibble
No good ever came from singletons in my experience. It seems that everyone goes through a singleton obsession phase, then realizes they are crap, and just uses a global like they should have in the first place. I've never had a legitimite use for a singleton, and rarely seen others have them. For what I was using them for, globals did the exact same job, without the hassle across DLLs. 'Shared' is equivilent to 'static'.

edit: In my limited experience with VB.Net, where I've seen Shared it is the same thing as static. I'm not sure exactly what you mean by Shared clasess. If you mean that every member is Shared, or you can accomplish the same thing by putting Shared in front of the class declaration, then yes, it is the same as static.


Ok, let's say I make an object Direct3DO{}, it's a singleton, and for now just has a pointer to the direct3d9 interface.

Aside from the obvious example problems(creating an object with 1 member...), can you tell me why it'd be a better method to just create the pointer in the global space? EITHER WAY I have to release it myself, so I just don't see the argument against singletons.

Share this post


Link to post
Share on other sites
its simple, dont create an object in global space.
Instead, assuming C++ here, on init of your main 'kernel' create reference counted smart pointers to the various subsystems and pass them down the line to things which are required (or even pass a pointer to the 'kernel' and give it a 'getSound' etc set of functions). The use of reference counted smart pointers is to stop your objects becoming deleted by mistake somehow and crashing everything out.

This forces you to think about your design and what needs what instead of having a bunch of global objects you can access from anywhere and temping you into silly hacks and not thinking about the design and the interfaces properly.

I fell into the above trap on my last project so i speak from experiance, once it was done I realised the only section which I could fully justify being a singleton was the error loggin system because i truely did need to get to that from anywhere in the code, everything else had logicaly points where I would need it.

Share this post


Link to post
Share on other sites
That post was after a 14 hour shift (QA) so I apologize if it comes accross wrong.

Are you saying create the object, for example:


class Direct3D
{
...stuff
}


...then create an instance of that object in the global space? If so, then I'm stuck again. Many things need access to the Direct3D object. Where I get stuck is this. I'm making an engine before I make the game. Now when I make the game, I can create an instance of Direct3D in the game source and work from it. The problem is that many other objects in my engine ALSO need access to the Direct3D object. When I'm writing objects such as Texture, it needs access as well. Do you make a Kernel object that houses everything, and do it all through there? Even still, I'd have to DERIVE my Texture object from the kernel for it to have access to the instance of Direct3D created in the kernel. The only solution I have found is singletons, but I'd be overjoyed to hear the solution to my problem without them. It'd probably teach me a lot and make me a more robust programmer, but most people aren't willing to share how they design.

Share this post


Link to post
Share on other sites
this is a strange discussion. you want to build an engine, ask about "the overall design", and then you ask if you should use singletons, or global pointers? For me this is totally irrelevant. The functionality is the same, in both cases. It's just that singletons are wrapped, and therefore maybe it's a bit simpler to handle them (although you have the overhead of writing a wrapper class).

to me that's like somebody who wants to build a car, starts designing the engine, but then gets lost answering the question if he wants to use black or white leather for the seats...

i think it will be far more difficult to answer questions like "how could i design the interface of my renderer", "should i use lua, python etc for my scripts", "how to implement a texture cache" etc...

Share this post


Link to post
Share on other sites
Quote:
Original post by DesCr
Many things need access to the Direct3D object.


Less than you think need direct access if you design things right...

Quote:
When I'm writing objects such as Texture, it needs access as well.


Thus you create a texture manager at some point and pass it a pointer to the Direct3D object in its constuctor, it stores a copy and oh look, it has access.

Quote:

Even still, I'd have to DERIVE my Texture object from the kernel for it to have access to the instance of Direct3D created in the kernel.


no, you wouldnt do that at all, because you texture or texture management class doesnt have a IS A relationship with the kernel.

Quote:

The only solution I have found is singletons, but I'd be overjoyed to hear the solution to my problem without them. It'd probably teach me a lot and make me a more robust programmer, but most people aren't willing to share how they design.


Singletons might well jump out as a 'solution' but they really arent.
The simplest way is just to pass pointers to objects down the line to things which require them, so for example your texture manager would take a pointer to the Direct3D object when its constructed, store it and for any later operations use that stored pointer to access the object.
Thats the basic idea behind it anyways.

Share this post


Link to post
Share on other sites
Quote:
Original post by DesCr
That post was after a 14 hour shift (QA) so I apologize if it comes accross wrong.

Are you saying create the object, for example:


class Direct3D
{
...stuff
}


...then create an instance of that object in the global space? If so, then I'm stuck again. Many things need access to the Direct3D object. Where I get stuck is this. I'm making an engine before I make the game. Now when I make the game, I can create an instance of Direct3D in the game source and work from it. The problem is that many other objects in my engine ALSO need access to the Direct3D object. When I'm writing objects such as Texture, it needs access as well. Do you make a Kernel object that houses everything, and do it all through there? Even still, I'd have to DERIVE my Texture object from the kernel for it to have access to the instance of Direct3D created in the kernel. The only solution I have found is singletons, but I'd be overjoyed to hear the solution to my problem without them. It'd probably teach me a lot and make me a more robust programmer, but most people aren't willing to share how they design.

Have the Direct3D class have a member IDirect3DDevicex *, and either just make it public or have a function to get a pointer. then have a global instance of the Direct3D class. Or, make a class that holds all of your systems (rendering, sound, networking, etc.), and make a global instance of that with functions to get to those systems.

The other problem with singletons is people use them for things that they need global access to, which isn't so bad itself, but they also use them for things that it could be useful to have multiple instances of. globals solve that problem easily. Take for example a logging system. Sometimes people make that a singleton, but why not a global? You could use a global for the main log, and then other things could instantiate their own logs for other things if they want. Maybe your level class has a Build method that resolves triggers or something, you could create a separate log to log errors in the build process. Nothing says you have to, but if you are truly designing an 'engine', you should leave the options open.

Of course, you are ignoring the (better IMO) possibility of just passing the pointer around, and you don't have to pass every pointer to every class. Say you have a game object class, and a level class. The level class would have a pointer/reference to Direct3D, and your game objects would have a pointer/reference to the level they are in. The level class could have a method to get the Direct3D reference, and there you go.

There is no one size fits all solution. I would use a global for something like a log, and pass a pointer around for something like CDirect3D.

Share this post


Link to post
Share on other sites
Quote:
Original post by crupp
this is a strange discussion. you want to build an engine, ask about "the overall design", and then you ask if you should use singletons, or global pointers? For me this is totally irrelevant. The functionality is the same, in both cases. It's just that singletons are wrapped, and therefore maybe it's a bit simpler to handle them (although you have the overhead of writing a wrapper class).

to me that's like somebody who wants to build a car, starts designing the engine, but then gets lost answering the question if he wants to use black or white leather for the seats...

i think it will be far more difficult to answer questions like "how could i design the interface of my renderer", "should i use lua, python etc for my scripts", "how to implement a texture cache" etc...


Well I plan to implement my own small scripting language, and I know how to implement a texture cache.

I may have mixed my design and implementation questions together, but my designs contain a lot of technical aspects. I need to know which objects will interact with which, how they will be derived, what they will need to pass around, and singletons which I detach by not drawing a line to other objects. (Although I've decided not to use them). My point is: the decision on whether to use singletons or globals plays a big part in my UML design.

Share this post


Link to post
Share on other sites
From my experience, it really isn't an advantage to expose a certain subset of gfx, input, and sound functionality to a scripting language. Games are complex. You'll end up implementing all the functionality you thought you were dodgeing with your 'simple engine' in the scripts anyway. Also, the scripting language is going to be alot more limiting than doing it in c++.

But its a good excercise, so have fun.

Share this post


Link to post
Share on other sites
Quote:
Original post by evolutional
My jsInvaders game is build along the lines of that 'tiny' architecture. The game starts up, loads it's levels from XML, loads the scripts and then displays the results. It's not bad ;)


Wanna make it open source? :P

Share this post


Link to post
Share on other sites
Heh, the plan is that jsInvaders is open source (released under the zlib license).

Don't get me wrong though, it's VERY simple and is suitable for top-down 2d shoot-em-up games only. It's not built for things like QUake, etc so if you're expecting it to be FPS/RPG-capable then I'm sorry :P

It is proving useful as a protoyping tool for my larger Shoot-em-up (Manta-X) and should be useful to 'teach' newbies the logic behind game-making.

It is being built as a suppliment to the articles I've been writing about "Creating moddable games in XML and javascript".

I'm uploading the pre-alpha in a few days (maybe a bit longer than a week - my priorities are on my web-rebuild [smile] )

Share this post


Link to post
Share on other sites
Well my plan wouldn't be to try and use it. I'd just like to look over source and see how things are done. When you do get everything up and open source, could you inform me at

colinpayetteNOSPAM@cox.net

Obviously remove NOSPAM. Thanks!

Share this post


Link to post
Share on other sites
The overwhelming response here is that singletons and global data are bad. It's important to keep in mind that they are not inherintly evil. If something should be "globally accessible" then it should be. The problem is that it's not immediately obvious where the boundary is.

In specific reference to the D3D global pointer question, I would consider it bad practice to make the pointer arbitrarily global and have the entire application use it. A safe way to handle this would be to make a Renderer class, perhaps make it a singleton, and internally it should handle everything regarding Direct3D. If there are other responsibilities which need to deal with D3D, such as a Texture or Mesh manager, they should accept the pointer in their initializers, and then the pointer would be global to all member functions of those systems. Those systems could then in turn be made global for the application to responsibly use (or a subsystem Facade could be setup which provides localized API-like access to their functionality).

An implication I'm making here is that I have not found a use for lazy initialization of singletons except for memory management. I enforce an Initialize() and Shutdown() function. The D3D owner, the renderer, would be responsible for calling the TextureManager's Initialize() and Shutdown() functions with the necessary arguments. [To be extra safe, a stack-object should be created which calls Shutdown() on a subsystem, similar to an auto pointer. Remember, Resource Acquisition is Initialization!]

The moral of the story is that so long as you exercise good design rationale when building something, you can always "break the guidelines," so to speak, when there is nominal value in doing so. They are just that, guidelines.

------------------------------------------------
Here are some examples where I've succesfully applied this rationale:

In my game-engine, which has been used succesfully to create a bunch of small 2D games and one rather nifty 3D CTF game, we used singletons for the various subsystem facades. When I need to load a geometry file to pass to the constructor of an Actor class, I would invoke our MeshManager to load the file and return a mesh object. It would be silly to require the MeshManager to be passed as a pointer all the way into an arbitrary Factory in the game from the engine main just to make it "non-global."

Another example is our logging system. Unlike the one which was described earlier in this thread, there were many actual "logging objects." The trick, though, is that they were created by invoking the global LogManager. This allowed us to change the output streams (XML, plain-text, one-log-per-subsystem, one-log) for all log objects in a single place, the initialization of the LogManager. That manager piggy backed off of our FileManager, which was also a singleton, and it worked in a similar manner. Changing from a virtual file system (like a single pack file) to the standard file-system is a matter of changing one line of code. In fact, we've been toying with the idea of a network file system for remote debugging. All I/O over a network by adding a network stream class and changing one line of code.

------------------------------------------------

BTW, this thread would probably get more responses if it was in the Software Engineering forum.

Good luck!

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