class design

Started by
10 comments, last by thefatshizms 9 years, 7 months ago
Okay so I'm creating a small game engine based in directx11 (both for educaational purposes and fun) and I've come into a bit of bother with designing my c++ classes. Basically I want to have a default graphics class which will do all the minimum required stuff for directx and then I have sub classes which will inherit from this class so I can use functions like onrender etc. This is where I've got an issue... I'm not sure how to make it so I can create my subclasses and inherit these functions easily without adding their onrender functions etc to the programs main loop in order for them to be called. I hope this makes sense... I'm on my mobile so its hard to type. In conclusion I want to be able to make subclasses or "subsystems" in which the functions that they've overwritten/etc get automatically called in the main loop without having to create an object of every class and calling the function.

If you need more explaining please say so.
Advertisement

It sounds like you are trying to design an application framework, which is distinct from a renderer. In general, you have to have a basic interface to your application class which can be manipulated by the main loop. That can be as simple as having an initialize, update, and shutdown set of methods. Then you can overload each of those methods to do whatever additional calls you want.

Basically I want to have a default graphics class which will do all the minimum required stuff for directx and then I have sub classes which will inherit from this class so I can use functions like onrender etc.


You really don't want that. It's an abuse of inheritance and poor object structure. Keep a single class that wraps your graphics device and do not ever inherit from it. To support different graphics APIs, you can make an interface that abstracts the graphics device operations (create buffer, create texture, submit geometry, etc.) and have your concrete implementation derive from that.

Derivation is explicitly an extension of a type and shouldn't be used for other purposes (sans some C++-specific inheritance tricks you almost never need to worry about in application code).

Types represent a single idea and should not try to represent multiple ideas. This is called the Single Responsibility Principle, which is one of the lessons taught in the SOLID. In this case, your graphics device should do only one thing - abstract access to the graphics hardware. Everything else (window management, high-level rendering, resource management, etc.) should be handled by different classes without any inheritance.

Higher-level types that need to use the graphics device should contain a reference/pointer to the graphics device (a has-a relationship) and never inherit from the graphics device (an is-a relationship). You should always prefer aggregation (has-a) over inheritance (is-a) when building your classes.

You might have an application base class that you can extend with your own OnRender method or the like in order to contain and abstract away the basics of window management, input management, etc. This base class might contain a unique_ptr<IGraphicsDevice>, but it will not inherit from the graphics device.

This is where I've got an issue... I'm not sure how to make it so I can create my subclasses and inherit these functions easily without adding their onrender functions etc to the programs main loop in order for them to be called. I hope this makes sense...


There's two pieces to this, if I'm understanding you correctly.

First, provide default implementations of your virtual functions. The Application base might have a virtual DoRender() method with an empty body. A derived application class can then optionally implement the DoRender method.

Second, don't put any base class logic in a virtual method. If your interface is not pure-abstract (e.g. it has logic in it), then all of its virtual methods should be protected and not public. The public methods should do all the mandatory logic and then delegate to the protected virtual methods. Something like:

class Application {
protected:
  // override this in a base
  // protected because this is an implementation detail and not a public interface
  virtual void DoRender() {}

public:
  // public call to perform the Something action
  void OnRender() {
    // do special things we always need to do before derived class's extensions
    BeginFramge();

    // invoke the derived class's extensions, if any
    DoRender();

    // so special things we always need to do after derived class's extensions
    EndFrame();
  }
};

class MyApp final : public Application {
protected:
  void DoRender() override {
    // do all your special app-specific rendering here
    DrawBackground();
    DrawScenery();
    DrawCharacters();
    DrawEffects();
  }
};

Sean Middleditch – Game Systems Engineer – Join my team!

Thanks for the replies.

An framework is what I was looking for yes. To try explain it more if I've confused anyone here's some code (note: sorry for the indentation, I'm on my phone and have no internet on my pc):


Class Graphics {
Public:
//
Virtual void Render();

};

//
Class Light : public Graphics {
//implements lights within the game/scene
Public:
//
void Render();
};

//

int mainLoop()
{
While(true)
{
//
Gfx->Render();// something like this but what it will do is call all render functions from all the "subclasses" like light, geom etc
}
Return 1;
}
Again, sorry for the indentation/sloppy code... I'm unable to get to a pc with internet access at the moment.

Also, thanks sean I'll have a read and think about what you've said.
Thanks for the reply sean. I think I now understand. About the code, I can't seem to get it working... It seems that the virtual function isn't being called (DoRender() though OnRender does) inside the main loop.

You can do this:



class Graphics {
        class RenderState; //Viewport, etc.
        Array<Buffer> m_vBuffer;
};

class DX : Graphics {
};

class OGL : Graphics {
};

class Engine {
      class Graphics;      class Sound;      class Game;
};class Scene {
      Array<Actor> m_vActor;
      class PhysicsSystem;
};

class Game {
     //NextState, Span, Draw()
     class Engine;
     class StateMachine;
};
I don't understand how that would allow me to call all OnRender etc functions within the main loop.

@Jason would you mind giving me a quick example? Thanks.
Update:

Been reading my c++ book and there seems to be stuff in there about creating an application framework how I want. Now the only problem is how do I get my main loop to call a Render() function that will call all class render derivations of that function. I've seen some people do it (though can't remember where) by putting adresses of classes/objects or something within a vector and then looping through the vector to call all the Render() functions (or something like that).
It looks to me like you are using inheritance for the complete wrong thing here. The specific language that makes me think that is "will call all class render derivations of that function", which makes no sense whatsoever.

Public inheritance establishes an "IS A" relationship. For instance: "An OpenGL graphics module is a graphics module". Now try: "A light is a graphics". That makes no sense, even if you fix the syntax. Perhaps if you replace `Graphics' with `Renderable'. But still, you don't invoke `.Render()' on the base class and hope something good happens.

What you seem to want is a class (perhaps `Scene') that knows about all the things that need to be rendered and whose `.Render()' method invokes the `.Render()' methods of all the things it contains. It would be fine for all of those classes to inherit from `Renderable', but it's likely it doesn't buy you anything. Unless at some point you are going to need a container of pointers to `Renderable' so you can call `->Render()' on them without knowing the specific type, you don't need inheritance.
Ah okay now I understand. Yes a scene class is what I need... How would I implement it? How would I invoke Render() of everything it contains?

This topic is closed to new replies.

Advertisement