A bit of a simple question but it's one I'm never too sure of. This is mostly in context of game development. Let's say you had an entity, like a ship or a character. Should (and I know this is a matter of opinion) this object contain a Draw method? (perhaps implement an IDrawable interface)
When looking at source code in some game projects I see this done in very many ways. Sometimes there is a separate class that may take in an Entity base object and draw it, given its properties (texture, location, et cetera). Other times the entity is given its own draw method and they are able to render themselves.
I hope this question is not too subjective.. Sometimes I hear people say things like drawing should be completely separate from objects and they should not know how to draw themselves or have the capability to. Some are more pragmatic and feel this kind of design where objects have this capability are fine.
I'm not too sure myself. I didn't find any similar questions to this because it may be a bit subjective or either method is fine but I'd like to hear what SO has to say about it because these kinds of design decisions plague my development daily. These small decisions like whether object A should have the capability to perform function B or whether or not that would violate certain design principles.
I'm sure some will say to just go with my gut and refactor later if necessary but that's what I have been doing and now I'd like to hear the actual theory behind deciding when certain objects should maintain certain capabilities.
I suppose what I am looking for are some heuristics for determining how much authority a given object should have.
You should go with whatever makes your implementation the easiest. Even if it turns out you made the wrong choice, you first hand experience on why one method is better than the other and can apply that knowledge in the future. This is valuable knowledge that will help you make decisions later. I find that one of the best ways I learn the merits of something is to do it a wrong a few times (not on purpose mind you) so you can get a good understanding of the pitfalls of an approach.
The way I handle it is this: An entity has all the information on it that is required for it to be drawn. e.g. The sprites that make it up, where they are located relative to the center of the entity. The entity itself does very little, if anything at all. It's actually just a place to store information that other systems operate on.
The world rendering code handles drawing the world as well as all the entities in it. It looks at a given entity and draws each of its sprites in the right spot, applying any necessary camera transformations or effects or what-have-you.
This is how most of my game works. The entity has no idea about physics either, or anything. All it is is a collection of data, which other systems operate on. It doesn't know about physics, but it does have some Box2D structures that hang off of it which Box2D operates on during the physics updating phase. Same with AI or Input and everything else.
Each method has it's pros and cons.
Advantage of letting objects draw themselves:
It is more comfortable for both parties. the person writing the engine just has to call a function, and the person using it, writing these IDrawable classes has low level access to everything they need. If you wanted to make a system where each object has to define what it looks like which shaders and textures it will use, and how to apply any special effects. You will either have a pretty complicated system, or a very limited one.
Advantage of having a renderer manage and draw objects
All modern 3D game engines do it this way, and for a very good reason. If the renderer has full control over which textures are bound, which models to use, and which shaders are active, it can optimize. Say you had a lot of items rendering with the same shader. The renderer can set the shader once and render them all in one batch! You can't do this if each object draws itself.
Conclusion
For small projects, its fine to have each object draw itself. It's more comfortable in fact. If you really want performance and want to squeeze everything out of your graphics card, you need a renderer that has strict control over what is rendered and how, so that it can optimize.
It depends what you mean by 'draw itself'. If you mean should it contain low-level routines involving pixels or triangles, then probably not.
My suggestion:
If the 'appearance' involves some custom drawing routine, that could be in that class. On the whole though drawing would be abstracted from underlying API perhaps and either using the strategy, visitor or some IoC pattern the object would be drawn by some rendering manager. This is especially true in game design where things like swapping textures in/out of video memory and drawing things in the correct order is important; something needs to be able to optimize things above the object level.
To try to be more specific, one part of your object's graph (object itself or appearance subdivision) implements IRenderable
, has a method Draw(IRenderEngine)
, and that IRenderEngine
gives the IRenderable
access to methods like DrawSprite
, CreateParticleEffect
or whatever. Optimising or measuring your engine and algorithms will be far easier this way, and if you need to swap out engines or re-write in an unmanaged, high-performance way (or port to another platform), just create a new implementation of IRenderEngine
.
Hint: if you do this, you can have many things that look the same/similar but act differently by swapping out the behavior, or lots of things that look different but act the same. You can also have the behavior stuff happen on a server, and the appearance stuff happen on a client.
I've done something very similar to this before. Each class of object had a base class that contained properties used on the client, but on the server it was a derived instance of that class that was instantiated, and it included the behavior (movement, simulation, etc). The class also had a factory method for providing its associated IRenderable (appearance) interface. Actually had a pretty effective multiplayer game engine in the end with that approach.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With