• Advertisement
Sign in to follow this  

Design Issues: Making systems know about eachother (engine)

This topic is 4320 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I know this is a pretty frequent question, but id like to know how you design your code, so that the different systems of your engine know about eachother. My current system: Core |-->Rendererer |-->ResourceHandler |-->Sound etc.. As you can see, the core can access everything neatly, but whats the best way of making the subsystems talk to eachother. One option is to make the core a singleton and fetch its instance and go from there. Is this the kind of situation that a singleton might be useful? Seems to be a lot of different opinions about them.. Any thoughts?

Share this post


Link to post
Share on other sites
Advertisement
You could try using Dependency Injection. Basically pass the object reference as a parameter to one of the other objects for it to be used.

class DisplayManager {
private SoundManager soundManager = null;
...
public void setSoundManager(const SoundManager &sm) {
this->soundManager = sm;
}
}

Share this post


Link to post
Share on other sites
In my engine, I have all the core components as static classes (SpriteRenderer, SoundManager, GameTimer, KeyboardInput, etc). They are not encapsulated by a Core. I've tested a bunch of different small-scale engine designs, and I think this works well.

For instance (the engine is a sprite based 2D C#/MDX engine btw) the rendering is handled by each renderable object itself.

So the the Ship class has a method like this

public void Render()
{
// ...

SpriteRenderer.AddSprite(shipSprite);
}

and the Background class something like

public void Render()
{
// ...

SpriteRenderer.AddSprite(backgroundSprite1);
SpriteRenderer.AddSprite(backgroundSprite2);
}

I.e. all classes can access the renderer, and tell the renderer which sprites to render.

It works the same for sound and input, these are static classes, so every class has access to these everywhere. Note that it is only the core classes that are static, all other gameobjects are instanciated.

I know some people dislikes having objects static and therefore global, but for game engine components I think it makes sense.

The regular updating (i.e. update input and render all added sprites etc), is handled by so called tasks. So I have a task that regularly updates input and regularly renders the scene etc. This idea is from superpigs Enguinity series (Part III) and it works great.

Btw, I listened to a keynote by Tim Sweeny at Epic Games recorded at the GDC 2006, where he told about the Unreal Engine 3.0 and its architecture. It's really intresting, you can find it at http://www.gdcradio.net/.

Share this post


Link to post
Share on other sites
Have you considered message passing? Have all the modules accept an enumerated message type with two integer values (which you can use as pointers or whatever).

Share this post


Link to post
Share on other sites
Thanks for the fast response :)

Im going to try the dependency injection method first, as I kinda liked the concept.

This message passing, how does that really work? Do you have any articles or some other docs on that method?

Share this post


Link to post
Share on other sites
Got Dependency Injection working on all systems just now :) Works great. Thanks again everybody ;)

Share this post


Link to post
Share on other sites
Quote:
Original post by klette
This message passing, how does that really work? Do you have any articles or some other docs on that method?


I'm sure I could find an article quickly, but I know it was covered well in Game Programming Gems 4.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ravuya
I'm sure I could find an article quickly, but I know it was covered well in Game Programming Gems 4.


I found this one. Do you know of a bettter resource explaining it? I'm very interested as this is what I'm currently dealing with.

Share this post


Link to post
Share on other sites
Quote:
Original post by Enselic
It works the same for sound and input, these are static classes, so every class has access to these everywhere. Note that it is only the core classes that are static, all other gameobjects are instanciated.

I know some people dislikes having objects static and therefore global, but for game engine components I think it makes sense.


I do things the same way. Peoples' hatred of global variables is a bit harsh... you just have to know when to use them. If you have an object that is used everywhere in the program, will be created when the program starts, and lasts until the program ends, then static/global objects are the most straightforward/flexible/readable way to do it.

Share this post


Link to post
Share on other sites
Quote:
Original post by tstrimp
Quote:
Original post by Ravuya
I'm sure I could find an article quickly, but I know it was covered well in Game Programming Gems 4.


I found this one. Do you know of a bettter resource explaining it? I'm very interested as this is what I'm currently dealing with.


This page is always my first stop when constructing a complex system. I'm away from my home machines so I can't find other pages on it, but I'm sure others exist.

Share this post


Link to post
Share on other sites
Quote:
Original post by JBourrie
If you have an object that is used everywhere in the program, will be created when the program starts, and lasts until the program ends, then static/global objects are the most straightforward/flexible/readable way to do it.


Straightforward: Yes.
Flexible: Hardly.

MSN

Share this post


Link to post
Share on other sites
Quote:
Original post by Ravuya
Have you considered message passing?

I'm for message passing, too, because it is hands-down the most flexible and extensible of the systems suggested here. With a robust messaging framework in place, one subsystem doesn't need to know anything about any other subsystem - it doesn't even need to know if that subsystem exists! All it needs is to be able to send messages to the system queue, and handle messages off the system queue directed to it.

With message passing, you can actually "stub" out functionality in full, sending as-yet unhandled messages to the queue, and when you implement the handler the functionality comes online without any modification to the sending/originating subsystem.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:

With message passing, you can actually "stub" out functionality in full, sending as-yet unhandled messages to the queue, and when you implement the handler the functionality comes online without any modification to the sending/originating subsystem.


u can do the same with empty functions

Share this post


Link to post
Share on other sites
Quote:
u can do the same with empty functions


That does not make a lot of sense. Assuming you mean empty methods... By doing this you are forced to implement for each class that is capable of recv messages stubs for each possible message. Or derive from a common base class which implements all possible methods, and then override these stubs when a message should be handled.

It Much simpler to make each message capable class implement a single Message method and implement required message handling with a switch case combination. You only need to case messages then that this type will support. Others can be default ignored.

[Edited by - lubby on April 21, 2006 12:43:21 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by JBourrie
Quote:
Original post by Enselic
It works the same for sound and input, these are static classes, so every class has access to these everywhere. Note that it is only the core classes that are static, all other gameobjects are instanciated.

I know some people dislikes having objects static and therefore global, but for game engine components I think it makes sense.


I do things the same way. Peoples' hatred of global variables is a bit harsh... you just have to know when to use them. If you have an object that is used everywhere in the program, will be created when the program starts, and lasts until the program ends, then static/global objects are the most straightforward/flexible/readable way to do it.


If you really want them to be global, consider using singletons instead. That way you get to ensure it's initialized exactly once, and it'll be a bit easier to deal with if you later on decide that it shouldn't have had global scope after all, since you're already dealing with an instance of a class, rather than a static class.

And I'm having a hard time seeing why you think this usage of globals is particularly good.

Share this post


Link to post
Share on other sites
I struggled with all of these concepts for a long time. I finally settled on making each subsystem do only one thing, what the function says, and making the engine itself combine the smaller blocks of code into larger blocks of functionality.

If I didn't do it this way, I kept finding that hunting down where a variable gets changed could be a nightmare. Making entities load meshes was not elegant. My current system is at least procedural, but I'm not about to claim that it is fast or efficient, even though it may be.

I also use a message system for handling incoming network messages and being able to send debug text from anywhere.

It works for me.

Share this post


Link to post
Share on other sites
Quote:
Original post by Spoonbender
Quote:
Original post by JBourrie
Quote:
Original post by Enselic
It works the same for sound and input, these are static classes, so every class has access to these everywhere. Note that it is only the core classes that are static, all other gameobjects are instanciated.

I know some people dislikes having objects static and therefore global, but for game engine components I think it makes sense.


I do things the same way. Peoples' hatred of global variables is a bit harsh... you just have to know when to use them. If you have an object that is used everywhere in the program, will be created when the program starts, and lasts until the program ends, then static/global objects are the most straightforward/flexible/readable way to do it.


If you really want them to be global, consider using singletons instead. That way you get to ensure it's initialized exactly once, and it'll be a bit easier to deal with if you later on decide that it shouldn't have had global scope after all, since you're already dealing with an instance of a class, rather than a static class.

Personally for me, it is a tradeoff.

It takes less time to write

SpriteRenderer.AddSprite(sprite);

than

SpriteRenderer.Instance.AddSprite(sprite);

or

SpriteRenderer::GetInstance().AddSprite(sprite);


And the risk of initializing deinitializing twice is quite low in my engine since I have engine defined tasks that taks care of that, so it is not up to the user of the engine.

To "start" my engine, the code looks like this:

static void Main(string[] args)
{
// Start the core tasks
CreateAndUpdateGameWindowTask gameWindowTask = new CreateAndUpdateGameWindowTask(10, "Spatra 2", 800, 600);
KeyboardInputTask inputTask = new KeyboardInputTask(20, CreateAndUpdateGameWindowTask.GameWindow);
GameTimerTask timerTask = new GameTimerTask(30);
GameStateManagerTask gameStateManagerTask = new GameStateManagerTask(40);
SpriteRenderingTask spriteRenderingTask = new SpriteRenderingTask(200000, false, 800, 600);
Kernel.AddTask(gameWindowTask);
Kernel.AddTask(inputTask);
Kernel.AddTask(timerTask);
Kernel.AddTask(spriteRenderingTask);
Kernel.AddTask(gameStateManagerTask);

// [...]

Kernel.Execute();
}







Initialization and deinitialization follows from the design of the Kernel system.

Besides, converting from static classes to singletons isn't very demanding in case it would be needed.

Quote:

And I'm having a hard time seeing why you think this usage of globals is particularly good.

I think game engine components, which really are needed througout the entire application life time, are ok to have global becuse it simply becomes easier to use without the system getting messy, which other uses of globals easily becomes. I mean we are talking about like 6 globals.

In my early attemts on engine design I used dependencay injection as suggested in this thread. To me the code always ended up as messy, with dependencies being injected everywhere.

Btw, there is another parallell interesting discussion on engine design at the Game Engine Design Questions thread.

[Edited by - Enselic on April 21, 2006 5:06:25 AM]

Share this post


Link to post
Share on other sites
Correct me if im wrong, but would the message passing work something like this?

-------- ---------
|system1|<==fetch_msg==|CORE|===send_cmd==>|system2|
-------- ---------

The core fetches messages from the subsystem, clears the msglist, then send the message to the correct system?

And if this is the case, how does one implement an effect way of fetching resources from the ResourceManager?
Having the Renderer store pointers to the textures its using is one way, but im a bit worried about the timing. I guess each system runs its own thread ?

[Edited by - klette on April 21, 2006 5:15:40 AM]

Share this post


Link to post
Share on other sites
There are two basic lines of attack to this, as I see it anyway.

If you just want something that works, for this game now, then having static members of your Core class (or just global variables if you're in C++ and therefore allowed such extravagances) for each major component of the game is fine. Then your code everywhere can see and use those components. Advantages: fast to write and code for, and no overheads passing data around; disadvantages: no flexibility, you'll be hard pressed to reuse any of the code in another project.

For a more flexible solution you need to have some form of module registration and a method of requesting actions on the part of other modules. For example:
interface IComponent {
object Action(string command, object args);
}

class Kernel {
static Hashtable<string,IComponent> Components;
...
}

class SomeModule {
SomeModule(){
// Registration
Kernel.Components.Add("SomeModule", this);
}

void SomeMethod(){
// Request an action
Kernel.Components["Renderer"].Action("AddSprite", sprite);
}
}

(Okay, so you'd probably do it with ints and a list of constants for efficiency's sake, but the basic design would hold.) Advantages: flexible, reusable, extensible; disadvantages: lots of overhead on cross-module activity.

Share this post


Link to post
Share on other sites
Quote:
Original post by Bob Janova
disadvantages: [...], you'll be hard pressed to reuse any of the code in another project.

I agree that it isn't the best solution from an flexible point of view, but in what way do you mean that static accessible core engine components prevent code reuse?

Share this post


Link to post
Share on other sites
Just that the code you write would include a lot of stuff specific to the project, so it would be hard to reuse it in another. It's nothing to do with static accessibility (if you notice, in the flexible solution the list of components is static too). That is, almost by definition less flexible code will be harder to reuse.

Share this post


Link to post
Share on other sites
I would agree on going with Core as singleton. I mean there will be only one Core instance in the project. and using it as a singleton and some management I think is good no?

Share this post


Link to post
Share on other sites
There have been about a billion discussions on this forum about singletons/globals and their evilness (either real or believed). I recommend you find and read these (some are just a week old if I recall right) and will give you a better idea of why so many people think they're evil.

I think a good solid messaging system is the best bet and the one I went with. Globals really don't belong in an OO system. They're unprotected. Anyone can change data and you won't know where/who/when without some serious headaches to debug it.

Honestly your model loading software does not have a need to access your renderer, so why allow it? The only thing that has access in some way to the renderer in my engine is the scene graph. Sound doesn't need to get access to the renderer, so it cannot.

If you only need/want one copy of x module. State so in the documentation. Then if someone chooses not to read it, it's THEIR problem not yours.

Share this post


Link to post
Share on other sites
Think im gonna try this messaging thing ;) Just having a hard time seeing "the whole picture" .. Fairly unexperienced when it comes to large systems...

Learn by doing seem to be a good thing here ;)

Share this post


Link to post
Share on other sites
I had a friend of mine make a game (simple 2D tetris like game) with no engine. Just build it. Then using the same art assets redo the game as game/engine. Then he built up from there. Seems to be a great way to learn and get something working fairly quickly which is good.

Mike

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement