🔒 Locked
Good Engine/Game structure ?
Advertisement
Original Post
What do you like this suggestion for structuring my game code. There is a game object containing all game data. Then each subsystem would in turn get access of it and do it's own processing. Then the mainloop would look something like this: ProcessNetwork(game); ProcessPhysics(game); ProcessAI(game) ProcessLogic(game) ProcessRendering(game) The question is whether this is to coarse, maybe you should only send in parts of the game object to reduce dependencies. Alternatively you could inherit from a interface for each subsystem. Like this: ProcessNetwork( (Network*)game ); ProcessFysik( (Physics*)game ); ProcessAI( (AI*)game ) ProcessLogic( (Logic*)game ) ProcessRendering( (Rendering*) game) and class Game : public Network, public Physics, public AI .... and a subsystem interface could look like this: class Physics { virtual void ObjectCount() = 0; virtual Vector3d& AccessPosition( int objectId ) = 0; virtual Orientation& AccessOrientation( int objectId ) = 0; .... }
Ugh, no. Multiply inheriting from all subsystems for your game is A Bad Idea.
Remember basic object-oriented programming: Inheritance means an Is-A relationship.
This means that D is a B. Is your game object really a Network layer, a Physics layer, an AI layer, a Logic layer, and a Rendering layer? I would hope not.
Instead, it would make more sense to use the Has-A relationship. As in, your game HAS a Network layer, a Physics layer, etc. The Has-A relationship is defined in terms of composition.
Same thing goes for game objects. You wouldn't want your game object to inherit from PhysicsObject, AIObject, RenderObject, etc. Instead, those are components that your game object may have.
There are a whole host of issues with how to maintain the components, and their relationships with each other, the game, and other game objects, but that comes a bit further down the road.
By the way, unless you're using mix-in interfaces, if you start to go down the path of multiple inheritance, stop and figure out why. It's almost always (but not always) a mistake.
Oh, while I'm thinking of it: Why would you need to pass your game into each subsystem? That means that your whole design is hopelessly cross-tied. Each subsystem should be provided enough information to function on its own, remaining completely ignorant of the specifics of the game - or, for that matter, that there's even a game present at all. Any cross-subsystem functionality should be higher up: The whole point of the game, for example, is to bundle together functionality from all of the subsystems to make the game. Game objects' reason for existence is tie together smaller grained functionality from functional objects provided by the subsystems.
Remember basic object-oriented programming: Inheritance means an Is-A relationship.
class B{};class D : public B{};This means that D is a B. Is your game object really a Network layer, a Physics layer, an AI layer, a Logic layer, and a Rendering layer? I would hope not.
Instead, it would make more sense to use the Has-A relationship. As in, your game HAS a Network layer, a Physics layer, etc. The Has-A relationship is defined in terms of composition.
Same thing goes for game objects. You wouldn't want your game object to inherit from PhysicsObject, AIObject, RenderObject, etc. Instead, those are components that your game object may have.
class Physics{ // Whatever Physics-y stuff you need here... void RunSimulation(void);};class Game{ Physics *physicsLayer; Renderer *renderer;};class Entity{ PhysicsRepresentation *physicsObject; Renderable *renderObject; AIController *aiObject;};There are a whole host of issues with how to maintain the components, and their relationships with each other, the game, and other game objects, but that comes a bit further down the road.
By the way, unless you're using mix-in interfaces, if you start to go down the path of multiple inheritance, stop and figure out why. It's almost always (but not always) a mistake.
Oh, while I'm thinking of it: Why would you need to pass your game into each subsystem? That means that your whole design is hopelessly cross-tied. Each subsystem should be provided enough information to function on its own, remaining completely ignorant of the specifics of the game - or, for that matter, that there's even a game present at all. Any cross-subsystem functionality should be higher up: The whole point of the game, for example, is to bundle together functionality from all of the subsystems to make the game. Game objects' reason for existence is tie together smaller grained functionality from functional objects provided by the subsystems.
My idea was the game could hold all the game state at one place and by implementing a number of interfaces express only subsystem specific data for each subsystem. The good part with this design would flexibility for the internal representation inside the game object and data reuse (the same data could be access by different functions in different interfaces in different ways).
GameState is a better word for my "game" object.
GameState is casted to the specific subsystem interface before it's handled to the subsystems internal processing. But the subsystem doesn't know that GameState implements other interfaces, and likewise it doesn't know anything about gamestates internal representation. It doesn't even know there is a such a thing as a gamestate. So I don't think there is such a strong coupling that you may think.
The important point is that each interfaces to gamestate expresses just enough for each subsystem to do it's work, nothing more.
GameState is a better word for my "game" object.
GameState is casted to the specific subsystem interface before it's handled to the subsystems internal processing. But the subsystem doesn't know that GameState implements other interfaces, and likewise it doesn't know anything about gamestates internal representation. It doesn't even know there is a such a thing as a gamestate. So I don't think there is such a strong coupling that you may think.
The important point is that each interfaces to gamestate expresses just enough for each subsystem to do it's work, nothing more.
I thing abstract classes are solution (aka interfaces). Just declare that each of your classes (network, physics, ai, ...) will have commong method, push all objects in some attribute in Game class, and then call each:
class ISomething{public: virtual void Process() = 0;};class Network : public ISomething{public: void Process() { ... }};class Physics : public ISomething{public: void Process() { ... }};...class Game{public: std::list<ISomething*> something; // or std::set if order is not important // in constructor or in other intitialization // if needed pass this (Game object) to each constructor something->push_back(new Physics()); something->push_back(new Network()); ... // in some method for (std::list<ISomething*>::const_iterator it = something.begin(); it != something.end(); it++) { (*it)->Process(); } // or rewrite it with std::for_each or different mechanism};Quote:
Original post by Zelcious
My idea was the game could hold all the game state at one place and by implementing a number of interfaces express only subsystem specific data for each subsystem. The good part with this design would flexibility for the internal representation inside the game object and data reuse (the same data could be access by different functions in different interfaces in different ways).
GameState is a better word for my "game" object.
GameState is casted to the specific subsystem interface before it's handled to the subsystems internal processing. But the subsystem doesn't know that GameState implements other interfaces, and likewise it doesn't know anything about gamestates internal representation. It doesn't even know there is a such a thing as a gamestate. So I don't think there is such a strong coupling that you may think.
The important point is that each interfaces to gamestate expresses just enough for each subsystem to do it's work, nothing more.
(I was pretty much thinking of GameState or Game as synonymous - I'll just use Game because it's less typing)
If each subsystem is completely independent of each other subsystem, why conjoin them? Why do they need to be all muddled together in the game, only to be psuedo-separated back out when the game calls them?
Always remember The Rule of One: Every thing should have one clear purpose, appropriate for scope.
* A function should do one thing, and that thing should be clearly obvious from its name. It should ideally be small enough to fit on one page - anything larger is likely doing more than one thing, and should probably be broken up.
* A class should represent one thing, and only have members and functions that are a part of that thing. Any significant amount of functionality that's not immediately part of that thing should be split off into another class. A car class, for example, may have some number of tires, but the details of the tires should be contained in a tire class that the car class Has-A list of.
* A module should group closely related classes, or contain a subset of a group of related classes. If the module is small (say, <1000 lines), then it's reasonable to keep one or more classes in the module. If the module is too big, first move groups of classes out into their own module. If a single class is too big, well, it's probably violating the "one representation" rule.
* A library should address one set of needs. Say, rendering, or physics, or figuring out what 6 times 9 is.
Anyways, the multiple-inheritance approach violates the class rule in ways that really aren't discussable in polite society.
As for the amount of data that's shared between major subsystems, aside from maybe position, orientation, and velocity of objects, what's there really to share?
Even then, why would the subsystems need to "pull" that data from the game or game objects? Why couldn't the game objects "push" that data from one subsystem to another as appropriate?
For example, if the physics system determines that the physics representation of a ball gets whacked with a stick, and starts moving the ball around, it could use a callback to the object representing the ball, and that game object could then set things up for the game object's render object to say "hey, I'm over here now, and I'd like a bit of motion blur, please." This keeps everyone's responsibilities separated, and keeps the job of tying things together in a game specific way encapsulated in, well, game specific objects and supervisory roles.
Cheers,
Jason
bubu LV: How do you suggest Process access the game data, like game objects ?
That is the tricky part IMHO.
JasonBlochowiak:
I did this so that I wouldn't have to duplicate data and could hide the internal representation of data. If you had for example several versions of position in each object you have to provide means of setting all of them at once.
I agree with that but the thing is that it only violates the class rule in one specific place, gamestate, not in the subsystems themselfs, right ?
Your absolutely right, there isn't much data to share. Maybe I could put that in a common part so that for example physics operate on a physicspart and a locationpart ? I've thought about doing so before and maybe it's the best idea ?
Sounds interesting, but I don't quite follow. Could you explane a bit more ?
Who has the callback and when/where does it get called?
That is the tricky part IMHO.
JasonBlochowiak:
Quote:
If each subsystem is completely independent of each other subsystem, why conjoin them? Why do they need to be all muddled together in the game, only to be psuedo-separated back out when the game calls them?
I did this so that I wouldn't have to duplicate data and could hide the internal representation of data. If you had for example several versions of position in each object you have to provide means of setting all of them at once.
Quote:
Anyways, the multiple-inheritance approach violates the class rule in ways that really aren't discussable in polite society.
I agree with that but the thing is that it only violates the class rule in one specific place, gamestate, not in the subsystems themselfs, right ?
Quote:
As for the amount of data that's shared between major subsystems, aside from maybe position, orientation, and velocity of objects, what's there really to share?
Your absolutely right, there isn't much data to share. Maybe I could put that in a common part so that for example physics operate on a physicspart and a locationpart ? I've thought about doing so before and maybe it's the best idea ?
Quote:
For example, if the physics system determines that the physics representation of a ball gets whacked with a stick, and starts moving the ball around, it could use a callback to the object representing the ball, and that game object could then set things up for the game object's render object to say "hey, I'm over here now, and I'd like a bit of motion blur, please." This keeps everyone's responsibilities separated, and keeps the job of tying things together in a game specific way encapsulated in, well, game specific objects and supervisory roles.
Sounds interesting, but I don't quite follow. Could you explane a bit more ?
Who has the callback and when/where does it get called?
Quote:
Quote:
Anyways, the multiple-inheritance approach violates the class rule in ways that really aren't discussable in polite society.
I agree with that but the thing is that it only violates the class rule in one specific place, gamestate, not in the subsystems themselfs, right ?
I suppose. But that's a bit like saying "there's only one bullet hole, but it's in his head." The game is what bridges all of the subsystems together, and so it's tremendously important. If there were some tangible benefit, I could be convinced that a design hack was appropriate, but I've not seen the need for that type of hack in any of the full scale titles I've been involved with.
Quote:
Quote:
As for the amount of data that's shared between major subsystems, aside from maybe position, orientation, and velocity of objects, what's there really to share?
Your absolutely right, there isn't much data to share. Maybe I could put that in a common part so that for example physics operate on a physicspart and a locationpart ? I've thought about doing so before and maybe it's the best idea ?
I just handled that by copying some of that info around (say, from the game logic part to the physics part). There isn't really enough of it for doing so to be a problem.
Quote:
Quote:
For example, if the physics system determines that the physics representation of a ball gets whacked with a stick, and starts moving the ball around, it could use a callback to the object representing the ball, and that game object could then set things up for the game object's render object to say "hey, I'm over here now, and I'd like a bit of motion blur, please." This keeps everyone's responsibilities separated, and keeps the job of tying things together in a game specific way encapsulated in, well, game specific objects and supervisory roles.
Sounds interesting, but I don't quite follow. Could you explane a bit more ?
Who has the callback and when/where does it get called?
The physics system I'm most familiar with lets you specify callbacks for collisions. So, when you run the physics simulation, any callbacks that you've specified will get triggered when the collisions happen - your object handling code can then do as it sees fit. In my example above, it would update the relevant render object in whatever way it wanted to. The point there being that the physics handling has a definite boundary between itself and the object handling code, and the object handling code oversees how the object reacts (to the physics) and how it presents itself (by making a request to alter the way the object is presented by the rendering system).
here's some ideas i'd like to post for you guys to consider.
personally, i prefer to seperate game state from game behavior. in terms of MVC (an obviously debatable approach), i try to consider the game state as the model of my game system at any given moment in "time". i then manipulate that game state using game processes, or controls, as is needed by that specific game. views are then used (rendering, sound, network?) to represent the game state at any given moment in time and to allow an actor (user, other computer, even another process) to understand and eventually interact with the game state.
so in terms of what you guys are discussing, i would make my game state stupid. game objects know about themselves, obviously, and eachother. they understand (abstractly) what relationships they have with eachother and that's it. game objects do not know how to self represent. they do not even know how to change over time. so at the bottom of the food chain we have game state (aka, game objects). they know nothing about anything else.
game state as a whole is maintained by ("is had by") a gamestate class that is instantiated in the scope of a master application class. this class' lifetime exists for the duration of the game application from beginning to end. game behaviors (AI, Rendering, Physics, GameState Advancement, whatever) are encapsulated in classes and instantiated in the same scope as the game state. The game state is then registered, by pointer or by reference, with each game process. This allows every process to access any aspect of the game state it may need. this also introduces a major implication: all game processes explicitly use (dependence) the game state, and thus if the game state changes then game processes must also change. however, this relationship is a clear one and there is little room for confusion. a major hurdle that must be addressed with such a system is how do independent processes interact when necessary? i've heard of several creative ideas ranging from "use the gamestate to passively communicate" to "use events and have processes access a master event queue" to "interconnect all processes that need to interact and be done with it".
make any sense? ultimately i agree that multiple inheritance is rarely a good answer, if ever. interfaces are ok, but only as long as they are purely abstract. use simple chains of composition whenever possible. my AppClass has a GameState, a RenderingProcess, a GamePlayProcess, and an InputProcess. Each process interfaces with their own APIs. My AppClass has a game loop that loops through my processes and runs my game. Simple system.
a lot of these callback systems and "listener" systems i see are great for some kind of programs, especially when event driven programming is an obviously effective choice, but game engines are predominantly not event driven. they are cyclical (Game Loop) and procedural(Input, Update, Output, repeat). games perform well through carefully managed states and operating constraints. keeping things as simple as possible will ultimately lead your game to where you want it to be: fast, fun, and easy to maintain and modify.
my two cents.
personally, i prefer to seperate game state from game behavior. in terms of MVC (an obviously debatable approach), i try to consider the game state as the model of my game system at any given moment in "time". i then manipulate that game state using game processes, or controls, as is needed by that specific game. views are then used (rendering, sound, network?) to represent the game state at any given moment in time and to allow an actor (user, other computer, even another process) to understand and eventually interact with the game state.
so in terms of what you guys are discussing, i would make my game state stupid. game objects know about themselves, obviously, and eachother. they understand (abstractly) what relationships they have with eachother and that's it. game objects do not know how to self represent. they do not even know how to change over time. so at the bottom of the food chain we have game state (aka, game objects). they know nothing about anything else.
game state as a whole is maintained by ("is had by") a gamestate class that is instantiated in the scope of a master application class. this class' lifetime exists for the duration of the game application from beginning to end. game behaviors (AI, Rendering, Physics, GameState Advancement, whatever) are encapsulated in classes and instantiated in the same scope as the game state. The game state is then registered, by pointer or by reference, with each game process. This allows every process to access any aspect of the game state it may need. this also introduces a major implication: all game processes explicitly use (dependence) the game state, and thus if the game state changes then game processes must also change. however, this relationship is a clear one and there is little room for confusion. a major hurdle that must be addressed with such a system is how do independent processes interact when necessary? i've heard of several creative ideas ranging from "use the gamestate to passively communicate" to "use events and have processes access a master event queue" to "interconnect all processes that need to interact and be done with it".
make any sense? ultimately i agree that multiple inheritance is rarely a good answer, if ever. interfaces are ok, but only as long as they are purely abstract. use simple chains of composition whenever possible. my AppClass has a GameState, a RenderingProcess, a GamePlayProcess, and an InputProcess. Each process interfaces with their own APIs. My AppClass has a game loop that loops through my processes and runs my game. Simple system.
a lot of these callback systems and "listener" systems i see are great for some kind of programs, especially when event driven programming is an obviously effective choice, but game engines are predominantly not event driven. they are cyclical (Game Loop) and procedural(Input, Update, Output, repeat). games perform well through carefully managed states and operating constraints. keeping things as simple as possible will ultimately lead your game to where you want it to be: fast, fun, and easy to maintain and modify.
my two cents.
JasonBlochowiak:
I understand your point about not violating a such central concept as gamestate.
My point was just that by defining several abstract interfaces to gamestate that violation is not that dramatic. I will try to
rearange the towards your suggestion.
If you duplicate data, like position. Is there even any benefit of storing that data together in a object ? Could it just be
stored in separate collections identified by a single objectID ?
What is your input on that ?
I like the collision callback idea. I think i'll let the setup register the logics collison callback with physics. That way
neither logic or physics need to know of each others existance
datadawgx:
A dumb gamestate was what I suggested. Only that I would provide several (abstract) interfaces for it. So that each subsystem
only see what it needs to see. It's just to further reduce the coupling.
How do you implement interprocess communication ? Does the "messages" go through gamestate or do the processes have direct
access to the other processes it's interrested in ? Is exactly everything stored inside gamestate or where do you draw the line
?
------------------
As I see it, there is three major question central to game engine design.
1. How to represent objects (gamestate)
2. How subsystem interact with (gamestate)
3. How subsystem interact with other subsystems.
Since I mainly been into graphics only before I'm just starting to trying find out a good solution for this.
There doesn't seem to exists a perfect or really natural solution so I guess you just have to find resonable good idea and stick
with it.
I understand your point about not violating a such central concept as gamestate.
My point was just that by defining several abstract interfaces to gamestate that violation is not that dramatic. I will try to
rearange the towards your suggestion.
If you duplicate data, like position. Is there even any benefit of storing that data together in a object ? Could it just be
stored in separate collections identified by a single objectID ?
What is your input on that ?
I like the collision callback idea. I think i'll let the setup register the logics collison callback with physics. That way
neither logic or physics need to know of each others existance
datadawgx:
A dumb gamestate was what I suggested. Only that I would provide several (abstract) interfaces for it. So that each subsystem
only see what it needs to see. It's just to further reduce the coupling.
How do you implement interprocess communication ? Does the "messages" go through gamestate or do the processes have direct
access to the other processes it's interrested in ? Is exactly everything stored inside gamestate or where do you draw the line
?
------------------
As I see it, there is three major question central to game engine design.
1. How to represent objects (gamestate)
2. How subsystem interact with (gamestate)
3. How subsystem interact with other subsystems.
Since I mainly been into graphics only before I'm just starting to trying find out a good solution for this.
There doesn't seem to exists a perfect or really natural solution so I guess you just have to find resonable good idea and stick
with it.
like i said, you can certainly use "gamestate" as a means of passively communicating between processes. also, you could use a combination of event classes that are known by those processes that 1) need to make those events and 2)need to respond to those events. then processes use events to communicate... but how efficient is that? the fastest but least maintainable method i can imagine is to directly interconnect processes that need to communicate. for performance critical systems this may make more sense. but just saying "process A needs to communicate with process B, so it explicitly knows of process B" may not be necesary. Maybe process A defines an interface for any process that it must communicate with. Process B implements a class to that interface that acts as a one way adapter. When process classes are instantied in some application scope, the application registers process B's adapter with Process A. Process A gets to Process B through the registered adapter, but still knows nothing of Process B directly. I did something like that for a project once, and it has downsides. Communication mediums have to be established when maintaining process independence. It can pay off, or it can just give you endless headaches.
Quote:
Original post by datadawgx
a lot of these callback systems and "listener" systems i see are great for some kind of programs, especially when event driven programming is an obviously effective choice, but game engines are predominantly not event driven. they are cyclical (Game Loop) and procedural(Input, Update, Output, repeat). games perform well through carefully managed states and operating constraints. keeping things as simple as possible will ultimately lead your game to where you want it to be: fast, fun, and easy to maintain and modify.
Hmm, I think I'd agree with you that quite a lot of game engines aren't heavily event driven. But, I don't think I think that's an entirely good thing.
The internal state of a game object will either be affected by a) nothing, b) the passage of time, or c) by the action of some other game object or system component (and b could be a part of c, really).
If your event system is nicely polymorphic, you can treat events as soft-bound function calls with a variable number of parameters that will be recieved by zero or more callees.
Example: Player presses button to shoot gun. Player object sees button press, sends event to gun to shoot. Gun (assuming ammo/cooldown status) spawns bullet. Bullet creates collision/physics ray component to add to the simulation to determine impact, if any. Physics system sees query ray hit object, sends collision events to object and bullet. Object ignores the collision. Bullet inspects collision, determines that object in question is solid, so it commits suicide after sending a "bullet impact damage" event to the object. Object gets damage event, doesn't care whether or not it came from a bullet (this is the polymorphic part), but does care that it's being damaged, so it decreases its internal hit points. It then realizes it just got broken, so it decides to blow up, because that's the kind of object it is. It suicides, replacing itself with whatever fragments and effects are appropriate, and sends out an explosion event.
As a variant, instead of a bullet, the player could be wielding a flamethrower. Instead of sending out a ray, the flamethrower could emit blobs representing balls of fire. On impact, instead of sending a "bullet impact damage" event, it could send a "fire damage" event. The recieving object could decide that it would treat fire damage the same as any other damage event, or it could decide that it's an extra-flammable object (taking extra damage), or it could decide that's it's not at all flammable (by ignoring the event entirely).
Anyways, like I said, polymorphism in event handling can be quite useful for in-game inter-object and inter-system interactions.
Also, events can be handy mechanisms for encapsulating network traffic.
They can also be handy for dealing with inter-thread issues. Render object state-change and update events could be queued from the main game thread to a separate rendering thread to decouple the two, for example.
I'm not recommending the extreme form (no function calls, all routed events), but I think that for current PC, and next-gen console development, event based systems are going to be quite a bit more relevant than they have been in the past.
I'd give more examples, but I need to go finish Rise of Legends. :)
Cheers,
Jason
Oh, almost forgot to answer your question Zelcious: I suppose you could avoid actually having a "real" game object, and just have it be implied by components stored in separate subsystem lists, but that wouldn't be how I'd approach it. This is basically because the game object logic tends to need to manipulate the components, and it's easier to have them aggregated into one object, rather than needing to go grab them from a list somewhere every time. The book-keeping headaches this can cause can be greatly minimized by using smart pointers effectively.
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement