Sign in to follow this  
Samurai Jack

Game Enigne: Putting it all together (design)

Recommended Posts

Greetings! I have huge problems with my conceptual design of a game engine. My basic idea was something like this: 1.) cWindow - class that creates a window, just a very small WINAPI wrapper, with one mayor routine virtual MessageProc(....) like WINAPI. One might override it and trap the messages. 2.) cGraphics - class that wraps arround Direct3D, takes cWindow as input. But there is a problem: when cWindow resizes or requires repaint, how shall it notify cGraphics? For example: WM_SIZE event was rised, cGraphics does not know anything about it but the Direct3D device should be reset. 3.) cEngine - basicaly a state machine or a stack of states. You just push a new state and it becomes active, windows message pump is being overtken and in non messaging moments Update and Render methods are called. But still it requires cGraphics to function and on the other hand cWindow too etc.. etc... The problem is that souch classes are no longer independant, it becomes somewhat of spagetti code. cWindow might require cState or cTimer and on the other hand it also might require cGraphic and cGrpahic requires cWindow etc... etc.. I could use singletons, but I believe with a better design, it would be possible to have a cWindow that can function without any reference to cGraphic and so the basic blocks would be independand. I do not want to produce too many classes, but the basic things sould be stand alone and capable to join with other classes. Thank you in advance.

Share this post


Link to post
Share on other sites
Well I hate the cClassname thing but here is an idea, make a cEvents class that fires off events that other classes can register to recieve. There has been a lot of writings on this, search google and this site for more info.

Share this post


Link to post
Share on other sites
I'm with Mike on this one.. some sort of message pump/event system/client server kinda idea seems to be the best idea. I would suggest you look into boost signals/slots to register and fire events. Its what I have been using and its incredibly well put together.

Share this post


Link to post
Share on other sites
I really dislike the way you design it.

What you do is a top down design

cgraphics cengine cwindow.... thats all the top of your engine and so simple that you will fail with your project as soon as you want to create a complex system


In my current design (its both for a 3d editor and a fps engine)

Well I have written some basic subsystems that are required by any decent engine

1. a virtual file system wrapper for physfs + required classes for file access ....
2. texture,material,effect & shader systems
texture represents the texture you load from your harddrive
material represents what you get out of these textures
e.g.: a material defines the material type, the effects that happen on interaction with polygons with this material and which textures are used
a 4 layer blended terrain would get a material assigned

like this
*material 007
*layer 1 = ground1.dds
*layer 2 = ground2.dds
...
*layer n = groundn.dds

3. the whole system depends one a lot of classes and can be reused in any further project so I keep them together and let them interact with another
e.g.: the material,effect and shader managers require access to the filesystem so I chose to use singletons to give access to read in all the shader defs...


Think about you approach if you really want to structure it as simple as you do right now, this cries for design flaws that will lead to failure or cause a ton of work to refactor code.

You should really design your code for reuseability otherwise its pretty hard to come up with a decent app in relatively short time

I am working on my code base for 3 years now and its still far from finished but I really increase my productivity with each day I invest into the code base

just make sure you have unit tested everything long enough or it will lead to nested bugs all over the project and thats really a pain ** *** ***

Summary:
Make your design more complex and let the classes of the individual subsystems interact with each other and tread these subsystems as a whole that is only touched by accessors(e.g.: public getters and setters)

now design you application in a way that you can unify these subsystems to a big whole that works well. The only unreusable and redundant code is that, that links the subsystems and that is a tiny part of the whole project

Share this post


Link to post
Share on other sites
I would make the seperation between 'engine' and 'library' even more clear...

You could make a base-library (.lib, .dll), which (speaking in the example
of the previous poster) contains your filesystem and a resourcemanager which
can use the filesystem.

Now have your application link with the lib, and inherith from the base
resourcemanager to add specialized behaviour (textureMgr, materialMgr, ...)

Using timer-instances spread out over the code usually give headaches.
Instead, you can abstract Timers away by having your renderloop all
'advanceTime(float elapsedTimeInSeconds)' on your engine-parts.
Only the mainloop has the timer, and updates components on each iteration.

This has the advantage you can easily add things like moviecapture (where
you want a constant framerate, and your hdd won't be quick enough to write
data in realtime), or for making sth like a slowmotion (just scale your
timedelta with some factor in the mainloop).

cState sounds like a perfect canditate for a template/statemachine. YOu
can derive your concrete states from the base-class, and use a generic
statemachine template in which make a few calls to say which transitions
are valid.

>a cWindow that can function without any reference to cGraphic and so
>the basic blocks would be independand.
You could put your windowing system in a seperate library (reusability)
and let it 'talk' to your graphics-system over an abstract interface -
your app can then derive from this interface and make calls to your
'real' graphics-system.

Share this post


Link to post
Share on other sites
Quote:
Original post by Basiror
...
3. the whole system depends one a lot of classes and can be reused in any further project so I keep them together and let them interact with another
e.g.: the material,effect and shader managers require access to the filesystem so I chose to use singletons to give access to read in all the shader defs...


A lot of people would argue the use of singletons is a design flaw. Our engine has 1 singleton and I hate it. It's our logger, there is better ways to do it though but at this point I'm not willing to change it (I didn't make it). There are places for singletons but they should be an exception rather then a common thing (imo).

Share this post


Link to post
Share on other sites
Why shouldn t I use singletons when I consider a subsystem as a whole? I won t bloat my code with them, its just a way to access data directly without passing a ton of function parameters

And they have the advantage that you can make them multithreading compatible on the fly opposed to simple global variables.

I see no reason why you shouldn t use singletons. In my opinion every sub system that exists only once should be represented by a singleton.

Share this post


Link to post
Share on other sites
Because, honestly, if you're using a singleton its normally out of laziness or bad design. Rarely can you flat out say you NEED it as there are normally better ways around it. I'm not sure where you went to school but using globals in all the classes I seen (C++ classes) was a big no-no and earned you docked marks (10% on average PER global up to 50%).

Why does your collision detection subsystem need access to your sound manager? If you only want one instance of either of them, say so in the documentation.

"Try to follow the flow of an app using singletons. (To summarize, any part of the code can call any part of the code at any given time. The joy!)" -- Seriema

Read Washus great writings on "Singletonitis". Part 1, 2, 3.

Here's a link to a GDNet journal entry

If your sound manager needs access to your renderer, you have other serious issues besides singletons anyways ;-)

Like I said, our log manager is a singleton, but a junior coder implemented it and while I was on vacation. It's now all over the engine and the one item I can almost justify as a singleton pattern. But when I have spare time it too will be going the way of the Dodo bird.

Share this post


Link to post
Share on other sites
Quote:
Original post by Mike2343
Because, honestly, if you're using a singleton its normally out of laziness or bad design. Rarely can you flat out say you NEED it as there are normally better ways around it. I'm not sure where you went to school but using globals in all the classes I seen (C++ classes) was a big no-no and earned you docked marks (10% on average PER global up to 50%).


I didn t learn C++ in school. I learned it on my own. And you got a PM why Singletons are no sign of lazyness or bad software design of use properly in the subsystems only where they ought to be used

Clearly nobody would say the use of a singleton to access the sound manager in the collision code is good design.

But using a singleton class type with a well defined access api in a subsystem only that can be considered as a whole is still far better than passing information by messages or passing tons of base class pointers around to access the information needed.

The access semantics of well defined subsystems won t change very much from project to project nor will a well defined codebase change very much so from the point of view of code reuseablility singletons can even improve reuseability.

In my current projects I only have to change 1 line of code to plug in another class implementation without changing the access api at all.



here an example of 3 subsystems

the material/shader system, the geometry manager and the filesystem

both a geometry manager and the material system need access to the filesystem to load in the desired resources.

The access semantics of a filesystem won t change anyway within the next 10 years.: open,close,read write seek ....

Now the material systems provides access to resources like textures ....
so the render needs access to retrieve handles that can be bound

and he needs access to the geometry data
both wont change very much within the next few years

the material system passes the texture data to the graphic card and stores a handle, you can access this handle from the render as described above or you can let the geometry system store the handles in a preprocess at load time so the renderer only needs access to the geometry system in the end

And the number of singletons reduces to a minimum and certainly won t pollute your code in any way .

Share this post


Link to post
Share on other sites
i have lots of managers. They are not singletons. I have a main device class contains references to all my managers. And all my managers have a reference to the d3d device

So i go like this. (just an example of what it might look like and this has its own issues)

device->getSkinManager()->ActivateSkin(skinID);
device->getEffectManager()->BeginEffect(effectID);
device->getMeshManager()->RenderMesh(meshID);
device->GetEffectManager()->EndEffect();

if a manager needs to know about another manager i pass a pointer and make it a friend class but i only do this as a last resort. E.g. Mesh managers has a reference to my Primitive manager. I cant stand passing references per function. It makes code look ugly.

there could be a design flaw in your singleton and a remote piece of code could use your singleton incorrectly when it shouldnt be touching it at all.

You cant say for certain you wont have more than 1 instance. You might extends your engine to use 2 monitors which uses multiple devices so now you might want another instance of your managers.

It would be nice if you could make class members only public to some functions.

Share this post


Link to post
Share on other sites
If I need a second instance of an object as you said device for D3D I would use a multiton its the same as a singleton but it can handle "n" instead of 1 instances

besides that I would structure the code a little bit differently

e.g.:
activate contextrender something ....
activate another context
render something
deactivate context ...


I doubt that I will need another instance of any of my resource managers, since this is the point of a resource manager, to organize resources at one point and only pass handles to the other subsystems.

My way of designing software is some kind of bottom up with layering of subsystems to abstract the basics in the top layers thus reducing the code complexity a lot.

The result is extremely reuseable code and a growing code base, whose code quality improves with every project.

Share this post


Link to post
Share on other sites
Did you read the links I provided? They're not long but very educational. I was a singleton fan like yourself, then I started reading other peoples code and never seen them used in production code. Then I researched them a bit more and stopped using them for obvious reasons.

Quote:

Clearly nobody would say the use of a singleton to access the sound manager in the collision code is good design.


Well a singleton will let you do this. So your code, by your own admission is bad design. A singleton can be accessed by anything and everything. That's what a singleton is and allows. Now if your code is just for you and no one will EVER use it, then fine, code however you like.

Why do you only need one resource manager, one renderer? What if I want to start loading a second level in a second resource manager? What if I want to render another view to a different screen? None of this should involve the engine needing to be touched at all in a decent design. Heck in my engine you can have unlimited numbers of theses (the documentation can say "only implmenent this once!" but if the coder chooses not too, its not your place to stop him).

I don't have pointers being passed all over the place and events are not a bad thing or a bad design. I, like Riviera Kid, has a GeneralMgr class that holds starts up all the subsystems in order. It then passes pointers to the subsystems that need them, say Renderer to the SceneGraph (nothing else needs the Renderer), SceneGraph gets a pointer to the AudioMgr, AIMgr Gets a pointer to the SceneGraph and AudioMgr, etc. This is all done with one function then the classes know everything they need too and I never worry about it again. There is no more passing of pointers, etc.

The way my GeneralMgr is setup, it only allows single instances of what it wants, but if someone is using my engine on their own project (without the source say) they don't need to use GeneralMgr, they can make their own and startup 10 ResourceMgrs if they please. It took no more time to design/implement this then if I had done singletons and is non-restrictive.

Share this post


Link to post
Share on other sites
Quote:
Original post by Basiror
If I need a second instance of an object as you said device for D3D I would use a multiton its the same as a singleton but it can handle "n" instead of 1 instances


Then why even bother with a singleton OR a multiton when much better design patterns exist?

Quote:

besides that I would structure the code a little bit differently

e.g.:
activate contextrender something ....
activate another context
render something
deactivate context ...


Maybe an example with something more code like.

Quote:

I doubt that I will need another instance of any of my resource managers, since this is the point of a resource manager, to organize resources at one point and only pass handles to the other subsystems.


But why couldn't someone that uses this in the future want two levels loaded at once but not managed by the same resource manager. Unless you can predict the future with 100% certainty why design future coders who use your engine into a corner?

Quote:

My way of designing software is some kind of bottom up with layering of subsystems to abstract the basics in the top layers thus reducing the code complexity a lot.


Keeping code complexity to a minimum should be all programmers goal (unless you're doing it for job security haha). Sadly, in the real world timelines usually mean its not possible.

Quote:

The result is extremely reuseable code and a growing code base, whose code quality improves with every project.


My non-singleton using code is the same and I'd say even more so since it has no restrictions. While you can change yours so you don't have them, I don't have to touch anything to do so.

I can take my AudioMgr, Renderer, ResourceMgr, etc out of my engine and in a matter of minutes having it working alone in a new project. As many of the sub systems as possible were developed as much as they could be stand alone, then inegrated into the engine so they're easy to extract and yet not a mess for the engine to use.

As a side note, I too am self-taught but I will never stop learning. I've helped a lot of friends with coding classes in college/university but have yet to see a teacher (I know they exist, heard about them on these forums) that accept singletons or global variables. The first time I heard this I called the teacher an idiot, then researched it a bit on forums and found out maybe he wasn't lol.

Anyways, long weekend!

Share this post


Link to post
Share on other sites
Those ideas have all their good and bad aspects. Singletons are good because you do not need to write any further fixed pointers that will allow you to talk with other classes but on the other side, why does all need to be so explolited? As the colleague above said, why does a sound manager need to see the renderer singleton? I believe it can be achived much cleaner - Micro$oft also does not include any singletons in their DirectX SDK and it allso can work.

My question is about Class communication. For example:
I would like to have a cDirectX9 class that holds just the IDIRECT3D9DEVICE pointer and clearly initializes it. Also that class could open "n" render target windows like cRenderTarget. The problem is what about when the cRenderTarget window resizes and would like to notify cDirectX9 device? I would not like to say it like so:

class cDirectX9;//forward declaration
class cRenterTarget
{
cDirecrX9* m_pDirectX;
...
onResize() { m_pDirectX->Resize();
}

class cDirectX
{
std::vector<cRenderTarget*> devices;
}

or on the other way I allso do not like the idea:
cRenderTarget::onResize() { this->Invalidate ( cRenderTarget = dirty; }
and when the cDirectX comes in it's update look it checks the windows whenever they are dirty and resets the device.

The problem is, either I use forward declaration or have to implement some passive workaround. Can it be performed in a better way? I would like cDirectX to be a factory for cRenderTarget(s) but they should be able to communicate.

Thank you in advance!
It is very nice to see what kind of design patterns many different coders use.

Share this post


Link to post
Share on other sites
This thread has been a long debate about singletons, so let's get it back to the OP's question. Somebody earlier mentioned using message passing, which I second. You'll have to learn about data distribution management (DDM) to make message passing sane (message passing can slaughter your performance if your audio system, physics system, AI system, and all your game entities have to process and throw away your WINDOW_RESIZE message), but in my experience, message passing has really done a lot to reduce coupling between components.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by Basiror
Why shouldn t I use singletons when I consider a subsystem as a whole? I won t bloat my code with them, its just a way to access data directly without passing a ton of function parameters

[B]And they have the advantage that you can make them multithreading compatible[/B] on the fly opposed to simple global variables.

I see no reason why you shouldn t use singletons. In my opinion every sub system that exists only once should be represented by a singleton.


Careful how you make them thread safe, you need to use barriers.
Have a look at this paper.
http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

Share this post


Link to post
Share on other sites
Jack, use a centralized messaging system. Everyone sends messages to the same object with a recipient number on them. Have a stack for each system, place the item in the appropriate stack, and pop them off as need be. The windows system wouldn't need to know about the graphics system and the graphics system wouldn't have to know about the windows system. Nor would the messages to one system get in the way of another system. Thats how I would implement it at least.

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