code organization

Started by
17 comments, last by JasonBlochowiak 17 years, 9 months ago
Quote:Original post by JasonBlochowiak
Erm, I'd say that's exactly backwards. You want to push responsibility out of main classes/functions and closer to the leaf classes. That's pretty much the point of encapsulation - everything knows how to do its own thing, rather than requiring central classes to know how to do everything.

In the case of a global resource like a screen handle (keeping in mind that I haven't even looked at SDL, so I don't know the specifics there), it's one of the rare cases where it generally makes sense to have it available to all code that's going to be doing any drawing.


I don't agree. I don't think rendering code should be scattered across all your objects. Instead I think it should be centralised in one class. It's a matter of personal preference though.

Advertisement
I've tried it both ways and I prefer the scattering right now (with a centralized interface that delegates after sorting). Something to keep in mind though is that it's more difficult to switch out the renderer if the rendering code is all over the place.
Quote:Original post by JasonBlochowiak
Erm, I'd say that's exactly backwards. You want to push responsibility out of main classes/functions and closer to the leaf classes. That's pretty much the point of encapsulation - everything knows how to do its own thing, rather than requiring central classes to know how to do everything.
It encapsulates, yes; but it violates another fundamental principle of object orientation, which is focused responsibility. What you've now got is a class that handles both logic/AI and rendering. You might add sound or physics, and by the same approach it'd handle those as well. What you get is a class that does a great many different things, which is dangerous, because it becomes harder to keep track of exactly what it does and does not handle.

If you want to preserve encapsulation, then you can use a third object - a 'RenderableToken' that contains coordinates, an SDL_Surface*, and any other data that the renderer object needs to do its job for a particular game object.

Taking the concept to its logical conclusion gives you a client-server model for rendering. You create your Renderer object - the server - and have it store a list of pointers to RenderableTokens. When a new game object - a client - is created, it requests a new RenderableToken from the server, who creates it and adds it to the internal list before returning it to the client. Now, all the client needs to do is update the data in the RenderableToken as it moves and animates, and the server can just pass through all the current RenderableTokens each frame and render them all. Nobody needs to know anything about the internals of anyone else, so it's all encapsulated, and it keeps the objects in the system focused in their responsibilities: The game object is responsible for game object behaviour, the Renderer is responsible for rendering, and the RenderableToken is responsible for storing the information needed to render one game object.

Richard "Superpig" Fine - saving pigs from untimely fates - Microsoft DirectX MVP 2006/2007/2008/2009
"Shaders are not meant to do everything. Of course you can try to use it for everything, but it's like playing football using cabbage." - MickeyMouse

In case you don't know, there is a function SDL_GetVideoSurface which returns the current display surface. With this, you can avoid passing the screen pointer around all the time or using a global.
Quote:Original post by Kylotan
Quote:Original post by JasonBlochowiak
Erm, I'd say that's exactly backwards. You want to push responsibility out of main classes/functions and closer to the leaf classes. That's pretty much the point of encapsulation - everything knows how to do its own thing, rather than requiring central classes to know how to do everything.


I don't agree. I don't think rendering code should be scattered across all your objects. Instead I think it should be centralised in one class. It's a matter of personal preference though.


Hmm, in re-reading what I posted, I don't think it quite reads like I meant it to, and there's probably more agreement here than we initially thought.

What I meant to get across is:

1) Central game management classes shouldn't have knowledge about the specifics of individual game components.

2) Responsibility should be pushed as far away from central classes as makes sense. So, my game class shouldn't know the details of how to draw any of the game elements. The game elements shouldn't know the details of how they get rendered, but they should know how to specify (at a fairly abstract level) how they want to appear. Game specific render components know how to translate the game objects' appearance requests into a combination of requests to base render objects. The base render objects know the specifics of the rendering API they're using, such as DirectX or OpenGL.

Heh, I wrote this before reading superpig's post. Although his and my terminology differ some, we're pretty much saying the same thing. I completely agree that game objects shouldn't contain the details of their subcomponents - they should aggregate the subcomponents as self-contained units, and make requests of those subcomponents.

For what it's worth (and especially for anyone wondering if this is viable In The Real World), this approach has shipped SKUs on a high-performance, cross-platform engine, including targeting systems with weak CPU performance, and multiple core rendering APIs (custom PS2 API, DX on XBox, and the GL-ish API on GameCube).
Simple game organization will typically end up with lists of different objects ordered by functionality.

Thus, you'll have a list of "things that want to be rendered" and a list of "things that want to play sounds" and a list of "things that want to update game state N times a second" etc.

The central renderer just needs to traverse the list of things to render (which might be a list of sprite object instances in a 2D game), and render them. For greater effect, make this list into some snazzy datastructure like a quad- or octree. The sprite class knows how to actually render stuff (or, more likely, add rendering requests to your graphics core, which then batches by texture and blats data out at the end of the frame).

The central world simulation core just needs to step through the objects added to its "object that wants to simulate" list, and make all the objects collide/simulate. For bonus points, use a physics simulation package for implementing the simulatable objects (like ODE, PhysX, etc).

The game objects, then, become responsible for creating the right kinds of objects (renderable, simulatable, user-input-reader, etc), registering them with the right central systems, and then plumbing information between them (simulation position outputs to rendering position input, etc).

The trick is to use narrow interfaces for each concept within your game, so that each subsystem doesn't need to know about anything else, except the entity class needs to know (in its implementation) about the other interfaces it manages -- the public entity interface, however, probably doesn't expose any of the other interfaces, so it's still fairly well isolated.

Some of this was already said, but I figured I'd put it all into one collected post, to show the main thread of how orgniazation falls out.
enum Bool { True, False, FileNotFound };
Quote:Original post by hplus0603
Simple game organization will typically end up with lists of different objects ordered by functionality.

Thus, you'll have a list of "things that want to be rendered" and a list of "things that want to play sounds" and a list of "things that want to update game state N times a second" etc.

The central renderer just needs to traverse the list of things to render (which might be a list of sprite object instances in a 2D game), and render them. For greater effect, make this list into some snazzy datastructure like a quad- or octree. The sprite class knows how to actually render stuff (or, more likely, add rendering requests to your graphics core, which then batches by texture and blats data out at the end of the frame).

The central world simulation core just needs to step through the objects added to its "object that wants to simulate" list, and make all the objects collide/simulate. For bonus points, use a physics simulation package for implementing the simulatable objects (like ODE, PhysX, etc).

The game objects, then, become responsible for creating the right kinds of objects (renderable, simulatable, user-input-reader, etc), registering them with the right central systems, and then plumbing information between them (simulation position outputs to rendering position input, etc).

The trick is to use narrow interfaces for each concept within your game, so that each subsystem doesn't need to know about anything else, except the entity class needs to know (in its implementation) about the other interfaces it manages -- the public entity interface, however, probably doesn't expose any of the other interfaces, so it's still fairly well isolated.

Some of this was already said, but I figured I'd put it all into one collected post, to show the main thread of how orgniazation falls out.


I was wondering would you have a generic entity class which contains all the information about an entity and the functions like add_to_render_list() or somthing? If so would you need to call the functions to add to different lists each time you wanted it drawn etc? Would you have a general list of the entity's too?
Thanks for all the reading guys, got me thinking. And thanks simian for pointing out that function. I've got a simple version of the game going in a console with chars isntead of tiles, and it seems to fit together, keeping code seperated and stuff. Thanks again

Bender
Quote:Original post by kzar
I was wondering would you have a generic entity class which contains all the information about an entity and the functions like add_to_render_list() or somthing? If so would you need to call the functions to add to different lists each time you wanted it drawn etc? Would you have a general list of the entity's too?


The way I do this is to insert the aggregated subsystem component into the relevant list. For example, if a game object has a physics component, it gets added to the physics simulation; if it has a renderable component, that gets added to the "stuff to render" list, etc. The game object itself gets added to the game's list of, well, game objects.

In my implementation, the lists are persistant. For example, you don't have to repeatedly say game.AddToRenderList(myRenderObject) every frame - just once when you create the render object. How you handle destructor functionality and the implications of list auto-removal are very dependent on your development philosophy. In my current codebase, destructing an object that's still in an active list is considered A Bad Thing, and I throw an assert - my leaning is to require that client code properly dispose of their components at destructor time. The pain of that requirement is eased considerably by my use of reference counted smart pointers.

This topic is closed to new replies.

Advertisement