Game engine architecture

Started by
31 comments, last by Telastyn 18 years, 5 months ago
Hey all, Now I know this is the kind of question which has been asked loads of times. I also know through extensive searching (several days worth of searching for an answer!) that it's the kind of question that very rarely gets a decisive answer... so I've sat and worked out exactly what it is that causes my problem and I think I've distilled the question down to the point of hitting the nail on the head, which may help get a more concisive answer from someone a little more knowledgable! So anyhoo.... my question is regarding communication between classes interacting in the main game loop. Like many others, I've gotten caught in the trap of dividing my game elements into distinctly separate components such as input/sound/characters/levels etc. This in itself is not a bad thing, but when you then realise that the input component needs to pass messages to the character component to tell them to move.... the character component then needs to check with a physics component to see what the results of moving are (collision, free movmement, etc), the physics component needs to check this data with the level component which itself queries the terrain/scenery it contains. Now this is all well and good, but requires that each class either stores a pointer to the classes it is (semi) related to, or requires that it stores a pointer to the parent class to which it is made a friend so that it can access its' member classes. This, in my mind, is messy.... the links between classes are often obscure at the best of times, and I'm forever going back to the initialise function of a class to add in an argument that will pass in a pointer to the required class.... then you've got to add a stored pointer to the class in your private variables. This gets more and more messy the more levels of classes you have to go through to obtain a pointer to the class you need to access. You could instead create a chain through the classes that calls the function you want for you, but then you need to code a CheckForCollision(x,y,z) type call for every class in the chain and for every function you wish to access, which is nightmarish! So it's: a) confusing when you have obscure links b) a pain in the backside when you have to trawl through a couple of levels of classes repeating above process just to allow classes to communicate. c) when rewriting code it is less reusable because storing pointers to other classes requires explicit knowledge of how the classes work in order to use their functionality. Our classes become dependant upon the functionality of other classes and can no longer be dropped directly into another engine because all of the dependant areas need to be re-written. The method of adding a pointer to the parent class and making the child a friend class of the parent makes it easier to access other components of the parent, but feels very much like it goes against the object oriented ethos. So, summing the problem up.... the difficulty is in setting up communications between classes that if drawn in a hierarchy, would be on the same level. A solution that appears more elegant would be some kind of a messaging system to request and send data through some kind of interface, so only the interface needs reprogramming upon starting a new project, but I'll be damned if I can think of a solution without major flaws in it. So are we really doomed to be writing code that is hard to reuse because there is no better way? Is it less of an issue than I think it is with the standard method, or is there some mystical magical method I'm unaware of?
Cheers,SteveLiquidigital Online
Advertisement
Instead of letting the the topical classes interact with each other, have a game class which act as an supervisor. Something like this:
// Inside the Game-classwhile( true ){    _player.update( delta_time );        // Player is derived from PhysicalObject    _physics.applyPhysics( _player ); // the _physics-object takes care of checking                                      // and compensating for collisions    if( _input.keyPress() == key_up )        _player.setState( accelerate );}


This solution has worked for me so far. It may not be the best, but it is easy to follow and it doens't require complex relations between classes.
[s]--------------------------------------------------------[/s]chromecode.com - software with source code
Here are my 2c about this problem that bothers me as well.

First, I believe that you should make your modules (physics, terrain, input etc.) as generic as possible, not aiming at a specific game but enabling as much capabilities as possible. This way you would have generic modules at your disposal.

The next thing to do is to really treat these as black boxes and NEVER rely on one another. The input module will never know about the physics, and the physics module will never know about the terrain. How do you do it? You have another module - your ENGINE module. This one interacts with all the modules but keeps them from interacting between themselves. The interaction takes place based on familiar data structures. For example, you should have a structure, defined in a common level (like the engine for example), that will describe an input event. You will also have a structure that will define a physics outcome of some sort, etc. This will behave like a contract between the engine and its modules.

This way you can do the following:

1. As long as the contracts remain intact, you can change the engine and the modules as much as you like, and nobody gets hurt.
2. If you do need to change a contract, you will know that you have at most caused a change in a link between the engine and N modules. These changes can sum up to N links - but they will never be N^2 links (which might be the case when "everybody is talking to everybody").


I believe this is a fair way to go about doing things. Naturally it has its own flaws, like centralism, and having data defined by the engine instead of the client modules. However, as I believe that the main client here is the engine and the modules are just its pawns, I think this might be a good way to go.

Waiting to hear other ideas :-)
Dubito, Cogito ergo sum.
Hmm, I actually hadn't considered simply removing a level from the hierarchy. That's not a bad solution actually, but what bothers me about it is that there is no categorisation. It feels correct to have certain aspects of the engine dealt with entirely separately.

I suppose the topical nature of some classes is the cause of the problem they try to resolve as they add an unnecessary level of abstraction which I had never really considered removing before. I would worry a little however with this method, that too much work is being taken on by the game supervisor class.

Well, certainly the best answer I've seen to this question so far, but I'd appreciate anyone else confirming that this is how they deal with things or pointing out any negative implications to this method, not that I want to pick fault, but I want to make sure I'm doing things as well as I can :)

Thanks,

Steve

*EDIT* DadleFish, thanks for your reply also! That suonds just like the kind of interface method I was thinking of myself. I think I've certainly got some thinking to do now, it's been great to see both opinions so quickly :)
Cheers,SteveLiquidigital Online
Hey,

I've been having similar problems, hopefully if i explain how my engine works it may help.

Until recently i've been structuring my classes so that they can interact with each other, this was bad for 2 reasons:

1. The code gets messy quickly
2. Code could not be easily reused

So when I decided to give an engine another go I did some research, the most useful articles I found were the Enginuity series by Superpig. They describe a number of complex techniques, of which I selected a few. I used his Singleton class, and the Task Based approach to the engine. This means that Input / Sound / Video etc. are all tasks which have Start/Update/Stop method. They can be suspended and resumed, and are controled by a kernel class.

So now in my engine, anything that I only need a single instance of is derived from the Singleton class. This includes: the game log, the main game timer, the texture manager, the physics environment, the kernel, and the application class.

Anything that needs regular updating in the game loop is derived from the task class; the physics, video, sound, input etc.

Although my engine is still in its early stages, everything is tidy, bugs are easier to find and most importantly everything is modular and can be dropped into another project.

Another tip is: Do not be afraid of static functions and classes. Use them wisely but remember they are there.

Hopefully that might give you some ideas,

Luke.

Member of the NeHe team.
What you can do is, first declare a mother class, let's say the "game" class and then have child classes underneath it and all messages go through the mother class, which makes it understandable.

Sorry for the vague description... but if you need more info, please feel free to mail me: thedatabandit@hotmail.com

Cheers,
-Zubair_
Amps
Quote:Original post by databandit
Sorry for the vague description... but if you need more info, please feel free to mail me: thedatabandit@hotmail.com

Cheers,
-Zubair_

...or we can use this forum!
[s]--------------------------------------------------------[/s]chromecode.com - software with source code
Forgive me if I'm being dense, but the Enginuity articles, while being very good, and something I'd certainly like to implement, actually do not help with allowing easy communication between classes? Perhaps that wasn't the intention, but I still ask, as if it does have relevance to my query, it would certainly help if I understood exactly how it is relevant!

So far, I think the engine interface style class seems the best solution... that way, we still have to go through the process of building the links... but all the code for the links is in a common place. This means that if we do have to add in new modules to an engine and remove old ones, we simply slot them in, add any new links in the engine interface class and the newly added classes, and we needn't touch a line of code in that which has already been created unless it needs to interact in a different way with the new component. If it does need to interact with the new component, all rewritten code will revolve around the engine interface class only.

Take our previous example, say we wish to add in floor-plate switches to our moving around a map situation. We create a new class to represent interactive map elements. With the suggested system, we simply pass a EngineInterface pointer to the interactive objects class containing the floor-plate switch object. Now the switch needs to query the physics module to determine if anything is pressing on it. We implement in our EngineInterface class a function such as IsAnythingPressingOnSwitch(). We then let the EngineInterface worry about obtaining that data via querying the character engine for all characters within a given radius to determine if they are on top of the switch or not, and the switch never even has to know how the character class works.

If we then in future include a class representing objects that can be pushed onto the switches, we simply alter the EngineInterface IsAnythingPressingOnSwitch() to also check with the ObjectManager class for objects within radius. We never need ever touch the switch class in any way to let it know how to deal with the existence of the new class.

So anyhoo, I guess I'm simply restating what was said previously by DadleFish, by way of a more solid example, but it seems to make sense to me :P

Cheers,

Steve

*EDIT* please ignore the fact that switches and boxes are both interactive elements that should be in the same class (little mistake of mine!)... I think the point is still made ;)
Cheers,SteveLiquidigital Online
As others mention, pretty much any time you have a behavior that needs two [or more] other behaviors to work, it belongs in a class/object "above" those two other behaviors.

To the example:

Quote:
Take our previous example, say we wish to add in floor-plate switches to our moving around a map situation


Okie dokie.

Quote:
We create a new class to represent interactive map elements.


I'm not certain that you can abstract all the different interactive map parts this way, and I'd start by making a base interface class, not a full fledged class; but that's an implimentation detail.

Quote:
With the suggested system, we simply pass a EngineInterface pointer to the interactive objects class containing the floor-plate switch object.


No, bad. What you do is have the EngineInterface pointer 'create' the interactive object and put it into a list, or have a mediator [which is a level above the engine] create the interactive object and attach it to the upper level part. [or better, see below] Remember, ownership flows down hill.

You might still need a 'parent' or 'owned_by' pointer in the lower class, but the idea is to make the 'lower' classes handle less and less problems so that the lowest can handle a simple problem easily. The 'higher' classes combine these until a really big problem, like a game, becomes managable code.

Quote:
Now the switch needs to query the physics module to determine if anything is pressing on it.


And then you've dependancy again.

Personally, I would try something like this:
engine contains map.engine creates floor plate, adds to map.engine creates image/model representing floor plate,  engine links image data to floor plate data [boost::weak_ptr, reference, bald ptr], adds to lower rendering module, forgets it.engine creates physics representing floor plate, engine links physics object callback/trigger to something it owns...  (likely a member function of the engine floor plate)[boost::function, boost::weak_ptr] adds to lower physics module, forgets it.


Then the rendering part can focus on rendering the model, and only knows/cares about the model. The physics can focus on 'triggering' the plate, and ignore how it's rendered. The engine knows about both, but can safely ignore how the rendering and triggering is done. Those are problems solved by 'lower' parts.
So essentially, what you're saying is that we should create seperate models of everything, i.e. the physics part of the engine should deal with physics and physics only.... the AI engine AI only such that if the AI needs to access terrain data, the terrain data should be included in the AI model as a fundementally separate entity from the actual terrain data.

I can see the benefits of such a method, but surely this causes just as many problems? Okay, so the engine knows that there are 3 versions of the terrain, 1 in the AI class, 1 in the physics class and 1 in the rendering class, but we now have to ensure that 3 separate models of the terrain are kept synchronised with one another and 3 times the memory consumption. I suppose to solve the synchronisation issue every change must be made in the base engine on a base object, which would then look after updating the other representations of its data.

The more I think about it, the more appealing this method is, but I'm still not 100% convinced yet... some more food for thought though.

Thanks!

Steve
Cheers,SteveLiquidigital Online

This topic is closed to new replies.

Advertisement