Sign in to follow this  
Frishert

Designing my structure of classes - Help!

Recommended Posts

Hey there! I'm working on my very own simple game, and I've got some problems with sorting out how to put everything in classes. Especially the underlying graphicspart. I was wondering if anyone could help me - here's my chain of thought: 1) At first I thought of putting all graphical functions and variables in a namespace and call it a day. This however, seemed to be impractical, because: - it cluttered up the namespace with functions and vars lying everywhere about - it didn't provide any means for datahiding and encapsulation 2) Thus, I decided to put everything in classes. Not one class, because - and correct me if I'm wrong - that's not how classes are supposed to work. A class is supposed to be a manifestation of one idea, with one task, and nothing more. Right? So I devised e.g. a class for the core stuff, and a class for loading textures. They both work seperately, and well. The core sets up the window, clears the screen, swaps buffer, etc. The textureloader loads in textures and manages the resources. My question is twofold: - Is this the correct way to go? If, up until this point, I've been making terrible design decisions, please say so now. - Where should I put the texture dráwing functions. It'd make sense to put it in the core class, because the core handles basic rendering. This means I'd have to make the texturemanager a member of the coreclass, or sort of register the instance I'm using with a pointer in the core class. It also makes sense to put it in the textureclass, because it ís texture work. Then again, then the core doesn't handle all the rendering on it's own, and loses control. What's the way to go? - Stijn Frishert

Share this post


Link to post
Share on other sites
From what I understand you have 2 classes now, correct? I don't know what kind of game you are trying to make, but typically the number of classes is a bit higher. You don't need a few hundred yet for a simply game, but if you have only 2 I think you need to look closely at the structure again.

I can't say to much about how the structure should be as I don't know what kind of game you are making, but in for example a 2D game, you could have a Game class that handles the game functions. Then have a RenderEngine that handles the window creation, rendering and keeps a list of textures. Then the Texture class handles texture loading and managing. To draw the textures, you have a Scene class that holds Objects. Each object has a position, size and pointer to the Texture.

Then when rendering (from Game), call scene->render(). The scene will ask the render engine to prepare for rendering (clear scene) and then will loop all objects, optionally checks if it's visible (in case of a scrolling 2D game) and then call texture->draw( position, size ). After rendering, it will tell the render engine it's all drawn, and the render engine shows to result.

For a complex game you will need many many more classes (have a look here for what a large rendering engine has), but if you are new to object oriented programming, don't start with to many directly.

Share this post


Link to post
Share on other sites
Here's how mine is set up, more or less:

I have a class which handles basic fullscreen window initialization, and from this, I derive specialized windows to support either OpenGL or DirectX rendering -- I don't attempt to encapsulate drawing here, just the parts that must necessarily interact closely with the windowing functionality. The final window classes provide a means to get the output renders from DirectX or OpenGL onto the window, and correctly implemented full-screen/windowed mode switching.

I have other classes which encapsulate the graphics API in a way that makes sense for my games.

I have a class which represents a surface (for rendering) as well as one for textures.

I have a series of free-standing functions (they are not classes) for loading images of various formats into the texture class. These are often called through a function pointer in my AssetCache class (more commonly called a resource manager, although that's a terrible name for what it does.) When I need deferred loading, I have a class which encapsulates a call to the correct loader, and a copy of the filename and loading options.

Finally, I have my AssetCache, which shares out references (boost::shared_ptr) to assets that have already been loaded, or creates a new asset record and loads the file for the first time so that it may be shared with future requests.

Share this post


Link to post
Share on other sites
@ MichaBen

Oh, I've got way more classes than two. The problem concerns only these two though, so those are the ones I was talking about. I see you've put the actual drawfunction in the texture itself, but frankly, that doesn't seem like a good idea. Mainly because the user will need to have access to the entire class, which defies encapsulation of the texturedata.

@ Ravyne

I see. Basically, I'm going the right direction. My coreclass handles creating the window and everything related to that. Then, I divide up the (rest of the) graphics into several classes. Now, moving on to the next step, which class(es) handle(s) rendering?

Does one of those classes take care of all the rendering:

VideoCore.RenderTexture("Stone", 135, 120); // Which then gives a call to the class that holds the textures, retrieves the data, and renders
VideoCore.RenderFont("Font", "Hello, world!", 10, 786); // Same story


Or do you give calls to the individual classes?

TextureManager.RenderTexture("Stone", 135, 120);
FontManager.RenderFont("Arial", "Hello, world!", 10, 786);


Or, as Michaben suggested:

TextureManager.GetTexture("Stone")->Render(135, 120);
FontManager.GetFont("Arial")->Render("Hello, world!", 10, 786);


- Stijn Frishert

Share this post


Link to post
Share on other sites
Well, a font or a texture would never render themselves, after all, a font and a texture is a shared resource -- which instance of itself would it draw?

In general, you're likely to have a class which represents an entity in the game world, or within the UI of your game, such as an enemy entity or text box. These will have some sort of reference (eg a pointer, reference or handle of some kind) to the font, texture or model data that are needed in order to render it.


There are really two schools of thought from here, either:

1) The entity has a render method, which is called upon in a loop inside your game function, passing in a reference to your renderer -- entity.render(rendercontext);

or

2) The render context has one (or more: rendermodel, rendertext) render function which take in an entity -- rendercontext.render(entity);


Which you choose really depends on your particular opinion on which is more natural -- however, I would strongly recommend against the entity.render() approach if that means including Direct3D or OpenGL code in your entities directly -- if you take that approach, you really should have a level of abstraction between your entities and the underlying API, or you'll really regret it when you decide to switch your project from OpenGL to Direct3D or vice-versa.

In general, however, you want to abide by the single-responsibility principle anyhow -- which states that an entity should simply be an entity, and nothing more. An entity which models that entity and also is responsible for its own rendering has more than one responsibility, and therefore violates that principle.

Share this post


Link to post
Share on other sites
Aaah yes. Well, I myself didn't like option 3 either, but MichaBen suggested it, so I figured it was an option too. Anyway, you say it's really just a matter of personal preference when choosing between 1 and 2? There's no fatal drawback to any of the two?

Great, then I've got my question answered. :)

Thanks, Ravyne!

Share this post


Link to post
Share on other sites
The manager, if you have one at all, gives you a corresponding object, which is then involved in the process of doing something.

E.g.


Screen.drawText("Hello, world!", TextureManager.getFont("Arial"), x, y);

Share this post


Link to post
Share on other sites
Quote:
Original post by Frishert
it cluttered up the namespace with functions and vars lying everywhere about

You can make globals "private" to the compilation unit by declaring them static. That way, you don't clutter up the namespace with implementation details.

static void invisible_helper_function1()
{
// ...
}

static void invisible_helper_function2()
{
// ...
}

void visible_global_function()
{
invisible_helper_function1();
if (blabla)
{
invisible_helper_function2();
}
// ...
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Frishert
Anyway, you say it's really just a matter of personal preference when choosing between 1 and 2? There's no fatal drawback to any of the two?
Either approach is workable but the rendercontext.render(entity) approach is superior in terms of design quality (Ravyne's already highlighted the Single Responsibility Principle in regards to this), it also has more scope for optimisations later such as batching and minimising GPU resource switching.

Quote:
Original post by DevFred
You can make globals "private" to the compilation unit by declaring them static. That way, you don't clutter up the namespace with implementation details.

<snip>

Actually statics at namespace scope are deprecated and replaced with anonymous namespaces:

namespace
{
void invisible_helper_function1()
{
// ...
}

void invisible_helper_function2()
{
// ...
}
}

void visible_global_function()
{
invisible_helper_function1();
if (blabla)
{
invisible_helper_function2();
}
// ...
}




[Edited by - dmatter on June 20, 2009 8:44:34 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Frishert
Or, as Michaben suggested:

TextureManager.GetTexture("Stone")->Render(135, 120);
FontManager.GetFont("Arial")->Render("Hello, world!", 10, 786);


I personally thought you would keep a pointer to the texture resource rather then a name. In case you have the name, you could rather ask the resource manager to render it. Personally I would keep a pointer to the resource directly so you don't need to lookup the name each call. In that case you can call texture->render() directly from the object. Calling manager->render( texture ) is also possible, but then you have to remember 2 pointers.

Keeping all the rendering code in the same class isn't going to work for larger projects, the code become unmanageably large some day. For example, maybe you will start using dynamic textures some day, like a big screen that shows another part of the map. That means in the texture manager, you will have to add an if statement to check if the texture is a dynamic one, and handle it. Or you could make a class DynamicTexture which extends Texture, and have it override the render function. Then you keep the code for the special effect in a separated file and it stays manageable. One special effect inside the main class won't be a trouble, but maybe later you also want a ScrollingTexture, VideoTexture, MirrorTexture, and the code will grow and grow and before you know you have 5000 lines of code and don't know which part does what anymore.

Share this post


Link to post
Share on other sites
Thanks for the help, guys, I really appreciate this! :) So, if I get this right, it works like this (and feel free to verify [grin]):

rendercontext.render(entity) is generally considered superior because it follows the single-responsibility principle, which states that every class should only take care of one task. Loading, managing and rendering resources are different tasks, and thus should be distributed amongst several classes. The texturemanager then takes care of managing textures, but should not be rendering them. You also don't want the texture to render itself (which I never was planning to do anyway), because that'd almost always mean including API code inside the texture class.

So, we devise a (new) class that takes care of the rendering; the rendercontext. In Zahlman's example, this is the class Screen, right?

Also, does MichaBen not have a valid point in his last paragraph?

Share this post


Link to post
Share on other sites
Quote:
Original post by Frishert
The texturemanager then takes care of managing textures
Be wary of manager classes; management should be the product of the interaction between various components not the burden of a single component. A manager, by definition, has several responsibilities so a manager class is surely either in violation of the SRP or it's a class that's been wrongly named. Decide what the manager actually does - if it does too much then divide and conquer, otherwise give it an appropriate name indicative of its actual job.

Quote:
You also don't want the texture to render itself (which I never was planning to do anyway), because that'd almost always mean including API code inside the texture class.
Having a texture render itself is inefficient and another deviancy from the SRP, but there's nothing overly evil about including API-specific code in a texture class if a texture forms part of the abstraction of the API. The problem you're hinting to occurs when you have API-specific code leaking into entity classes (like a sprite) that were meant to be abstracted away from that sort of thing. If the texture class is more of a texture description, or texture handle, then there probably isn't much need for API code to be in there anyway.

Quote:
So, we devise a (new) class that takes care of the rendering; the rendercontext. In Zahlman's example, this is the class Screen, right?
That's presumably Zahlman's intention, yes.
His example doesn't preclude a rendercontext-like object though, perhaps there are several screens whose draw calls batch up draw commands, a rendercontext then iterates through all its screens and renders the batches. Alternatively perhaps a rendercontext sets up the rendering device then acts like a factory for creating screens and handing them the necessary tools to perform the rendering work themselves. As you can see, one line of code isn't enough to know for sure but it's certainly enough to get one's imagination running.

Quote:
Also, does MichaBen not have a valid point in his last paragraph?

I think his point is a little extreme. If you think about how much of that code would be the same in each case or how much of that code is similar to other code - things like creating textures, setting shaders, various buffers, etc - and then refactor it, you'll find that it all vanishes pretty easily.

Share this post


Link to post
Share on other sites
Quote:
Be wary of manager classes; management should be the product of the interaction between various components not the burden of a single component. A manager, by definition, has several responsibilities so a manager class is surely either in violation of the SRP or it's a class that's been wrongly named. Decide what the manager actually does - if it does too much then divide and conquer, otherwise give it an appropriate name indicative of its actual job.

I was referring to a class I'd name TextureLoader, then. But, good point. This seems to come up a lot on the forums, but mostly without giving a reason as to why a name such as TextureManager is bad. :)

Anyway, I think I finally get what I'm trying to do really. Thanks for the help guys (especially Ravyne and dmatter), I'm on the right track again! You'll see me around ^_^.

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
Quote:
Original post by phresnel
Was Frishert an account-doble?
It was indeed.


I see. It's sometimes a bit confusing :S

Thanks.

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