Object Orientated Question

Started by
31 comments, last by RandomTask 19 years, 8 months ago
Bytecoder's breakdown is rather useful. However, I do have an issue with it. The fact that every object which is to be rendered will require a special "renderer" class.

The act of rendering an object can be abstracted out beyond the object itself. Specifically, every object that is rendered is a bunch of triangles organized into a mesh. Each mesh has properties associated with it which determine how it's rendered.

Now, the way which my engine is currently designed is to have an interface which all renderable objects implement. This interface would provide access to the abstraction which I described above.

The only thing that the concrete classes would actually have to do is specify a mesh to render. A seperate rendering subsystem would then iterate through a scene-graph which organizes these renderable objects and do its thing.

A Scene Manager could construct that scene graph out of these Renderable objects. For situations where an object needs to be in the scene-graph but is not renderable, it simply has no geometry associated to it and is ignored by the renderer.

Granted, this certainly violates the SRP from an "if the rendering interface changes then everything that is rendered has to recompile," but it doesn't require the construction of an explicit renderer class for every object in the game. Conversely, the solution which has the renderer relying on the tank, requires the renderer to recompile everytime the tank does. However, that does indeed follow the DIP, so from a strict "design" standpoint, byte's solution does seem better.

In the end, my solution violates the SRP and the DIP (Dependency Inversion Principle), as the upper layer depends on the lower layer, and not vice versa. However, I feel that the robustness and "ease-of-use" of the solution warrants those violations.

Just to note, the Rendering system which I describe is actually being designed, written, and maintained by a friend of mine and not myself. It is part of a full featured game-engine we have been working on.

I'd like to hear bytecoder's thoughts on my solution!
- Jason Citron- Programmer, Stormfront Studios- www.stormfront.com
Advertisement
@ Clash,

Can you post a diagram of your system? Or email it to me and I can post it for you?

Thanks
_______________________________________Understanding is a three edged sword...Freelance Games - Home of XBLIG Starchonwww.FreelanceGames.com
Quote:Original post by Clash
Bytecoder's breakdown is rather useful. However, I do have an issue with it. The fact that every object which is to be rendered will require a special "renderer" class.

The act of rendering an object can be abstracted out beyond the object itself. Specifically, every object that is rendered is a bunch of triangles organized into a mesh. Each mesh has properties associated with it which determine how it's rendered.

Now, the way which my engine is currently designed is to have an interface which all renderable objects implement. This interface would provide access to the abstraction which I described above.

The only thing that the concrete classes would actually have to do is specify a mesh to render. A seperate rendering subsystem would then iterate through a scene-graph which organizes these renderable objects and do its thing.

A Scene Manager could construct that scene graph out of these Renderable objects. For situations where an object needs to be in the scene-graph but is not renderable, it simply has no geometry associated to it and is ignored by the renderer.

Granted, this certainly violates the SRP from an "if the rendering interface changes then everything that is rendered has to recompile," but it doesn't require the construction of an explicit renderer class for every object in the game. Conversely, the solution which has the renderer relying on the tank, requires the renderer to recompile everytime the tank does. However, that does indeed follow the DIP, so from a strict "design" standpoint, byte's solution does seem better.

In the end, my solution violates the SRP and the DIP (Dependency Inversion Principle), as the upper layer depends on the lower layer, and not vice versa. However, I feel that the robustness and "ease-of-use" of the solution warrants those violations.

Just to note, the Rendering system which I describe is actually being designed, written, and maintained by a friend of mine and not myself. It is part of a full featured game-engine we have been working on.

I'd like to hear bytecoder's thoughts on my solution!

bytecoder's example, as I understand it, follows the Model-View-Controller pattern. Tank and TankCannon are model classes. TankHumanController and TankComputerController are controller classes. TankRenderer (or TankView) is the view class, whose sole purpose is to manage how the tank is displayed. Of course there would still be a separate rendering system, which would *not* need to be recompiled every time a view class is modified. I can't speak from any game dev experience, but I'd imagine if your game objects are sufficiently complex, then this kind of separation makes things a lot cleaner.
I don't see any sort of problem with the Object and ObjectController classes. There's no obvious way to abstract the notion of a controller to the point that it makes individual controllers useless.

Now with regard to seperating out the "view" logic into a third object, I'm still not convinced that doing so is better than just extending an interface which implements the generic functionality needed by the renderer.

I'd love to hear a case for using a full MVC as opposed to my solution.

*note I don't have a UML diagram of the rendering system. I'll whip one up and post it in a few days.
- Jason Citron- Programmer, Stormfront Studios- www.stormfront.com
Quote:Original post by Clash
I don't see any sort of problem with the Object and ObjectController classes. There's no obvious way to abstract the notion of a controller to the point that it makes individual controllers useless.


Jason,

I guess I didn't fully understand the implementation; I thought it was a full MVC implementation.

I will try to clarify my question:

When CTankCanon creates CLaser; how does CTankCanon create the corresponding instance of CLaserRenderer? My question was based on the assumption that the model cannon see the view and CTankCanon is part of the model.

However, I now think it is intended that CTankCanon will be a controller class that can see the view and the model. In this case CTankCanon can create CLaser and CLaserRenderer when there is a fire event. Basicaly the controller and the model are being mixed together instead of using and observer pattern to communicate to the controller?

Can you clarify for me; maybe I way off base?

Thanks for the help
_______________________________________Understanding is a three edged sword...Freelance Games - Home of XBLIG Starchonwww.FreelanceGames.com
Quote:Original post by Clash
Now with regard to seperating out the "view" logic into a third object, I'm still not convinced that doing so is better than just extending an interface which implements the generic functionality needed by the renderer.

I'd love to hear a case for using a full MVC as opposed to my solution.


The more I think about the MVC arch the more I like it.
It keeps the code needed to render an object seperated from the code that controls its behavioral responsibilities. The TankRenderer class could do what you suggest, it implements the interface required by the graphic engine and connects to (one or more?) tank objects to draw them. This would allow highly customized drawing for a given object.

In classic MVC is there one view-object instance per model instance, or do multiple models share a common view? Do views need unique per-instance data?
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Quote:Original post by Magmai Kai Holmlor
Quote:Original post by Clash
Now with regard to seperating out the "view" logic into a third object, I'm still not convinced that doing so is better than just extending an interface which implements the generic functionality needed by the renderer.

I'd love to hear a case for using a full MVC as opposed to my solution.


The more I think about the MVC arch the more I like it.
It keeps the code needed to render an object seperated from the code that controls its behavioral responsibilities. The TankRenderer class could do what you suggest, it implements the interface required by the graphic engine and connects to (one or more?) tank objects to draw them. This would allow highly customized drawing for a given object.

In classic MVC is there one view-object instance per model instance, or do multiple models share a common view? Do views need unique per-instance data?


Well one possible advantage of having one unique view instance per model is you can change what the view is displaying without modifying the model. For example, one view could be blue tank and the other view could be a red tank. Since the tank meshes are past to the view at the time of creation it is not necessary for the model to know this information. Although, you could implement a mesh key into the model.

Also, I wonder how this would effect performance; generally, you are updating each instance of the view before you decide to start rendering. But know you would be updating and displaying at the sames time; since they are a shared view instance.

I guess I am still not clear on ByteCoder's architecture? Is the controller and the model mixed together. For example, is CTankCannon responsible for creating both the CLaserRender and the CLaser?

_______________________________________Understanding is a three edged sword...Freelance Games - Home of XBLIG Starchonwww.FreelanceGames.com
Whoah, this thread really grew while I was gone. I was having some computer problems, so I couldn't really check on it.
Sean Doherty: yes, that looks correct. The TankRenderer should delegate the rendering of the Cannon to a seperate TankCannonRenderer. When you need to create something that is not part of the object you need to pass in a spawner object that creates all of the necessary objects for you. In your example you would have to pass a LaserSpawner that spawns the laser for the tank, which may or may not also spawn a LaserController and/or LaserRenderer. The LaserSpawner would probably also register the laser and related objects with the world. By the way, did I mention that doing all of this is so much easier in Python than in C++ :)
Quote:Original post by bytecoder
Whoah, this thread really grew while I was gone. I was having some computer problems, so I couldn't really check on it.
Sean Doherty: yes, that looks correct. The TankRenderer should delegate the rendering of the Cannon to a seperate TankCannonRenderer. When you need to create something that is not part of the object you need to pass in a spawner object that creates all of the necessary objects for you. In your example you would have to pass a LaserSpawner that spawns the laser for the tank, which may or may not also spawn a LaserController and/or LaserRenderer. The LaserSpawner would probably also register the laser and related objects with the world. By the way, did I mention that doing all of this is so much easier in Python than in C++ :)


The following is the updated class diagram based on your feedback:



I have a couple of questions:

1) Who is responsible for destroying the laser after it has been fired? Is that the job of the world?

2) We no longer have a MVC design; since the controllers and the model are not linked via the observer pattern?

3) If we ported the code we would only have to replace the view and all of the concrete builders (spawners)?

4) Where does the Mesh and World Matrix reside? I assume it is in each of the renderers?

5) How would collision detection work (assuming I wanted detailed)?


Here are my thoughts on collision detecrion:

You could pass the scene to a collision detaction manager (CDM). The CDM perform collision culling on each of the renderable objects. At end of this step there would be a queue of objects that would need to be tested. Next you pass two objects from your queue at a time to the collision detector to determine if there is a collision?

Assuming there is a collison and one or more of the objects should be destroyed; or an explosion needs to be spawned how is resposible?
_______________________________________Understanding is a three edged sword...Freelance Games - Home of XBLIG Starchonwww.FreelanceGames.com
Here are some problems I see:
1) The world doesn't interact with Tank objects directly, rather it interacts with the Tank object's controller.
2) A tank's cannon is not passed into it as an argument due to the fact that a tank's cannon is part of the tank. Thus the tank renderer renders the cannon, too, probably by delegating this responsibility to another class--TankCannonRenderer.

You also seem to be missing a key detail--how the object's are created. By using a generic object spawning class (which somewhat resembles the builder pattern) you can have different ways of spawning an object. For example, you could have a RandomObjectSpawner, which spawns objects at random positions, or you could have a FileObjectSpawner, which spawns objects based on input from a file. If you wanted to save the output of a spawner to a file, you could create a spawner class that basically just funnels requests to the spawner object you pass to it and copies the output of that to a file. Using these 3 spawners you could create a save game mechanism without changing the internals of the game at all. Ofcourse this assumes that objects don't make random choices. However, you are able to spawn top-level objects (objects that were created by the spawner) randomly.
Quote:
1) Who is responsible for destroying the laser after it has been fired? Is that the job of the world?

The laser is a seperate object, so therefore the laser is responsible for notifying the world that it needs to be destroyed.
Quote:
2) We no longer have a MVC design; since the controllers and the model are not linked via the observer pattern?

They never were linked via the observer pattern.
Quote:
3) If we ported the code we would only have to replace the view and all of the concrete builders (spawners)?

Ported the code to what, a different OS?
Quote:
4) Where does the Mesh and World Matrix reside? I assume it is in each of the renderers?

If you pass them in as a parameter to the renderer you can use them for collision detection, too.
Quote:
5) How would collision detection work (assuming I wanted detailed)?


Here are my thoughts on collision detecrion:

You could pass the scene to a collision detaction manager (CDM). The CDM perform collision culling on each of the renderable objects. At end of this step there would be a queue of objects that would need to be tested. Next you pass two objects from your queue at a time to the collision detector to determine if there is a collision?

Assuming there is a collison and one or more of the objects should be destroyed; or an explosion needs to be spawned how is resposible?

Collision detection should always be done by the world object. Collision response is another issue altogether and depends heavily on the type of game. You should do collision response that is always true in the world; e.g if every collidable object in the game has a bounce factor you let the world do the collision response for that. Most of the time you'll need to add a method to the controller that handles special collision cases (you don't want a space ship to blow up when it hits a powerup :D).

This topic is closed to new replies.

Advertisement