Sign in to follow this  
Sean Doherty

Object Orientated Question

Recommended Posts

From and Object Orientated prespective if you have a unit firing a laser at a second unit: Who should be responsible for drawing the lasers muzzle flash? Who should be responsible for drawing the lasers impact point? Who should be responsible for drawing the laser?

Share this post


Link to post
Share on other sites
1) The muzzle renderer object for the unit's "muzzle" object is responsible for drawing the muzzle flash.
2) Not sure what you mean. Are you talking about the results of the laser hitting the second unit? If so, then the unit renderer for the unit that got hit is responsible for drawing it.
3) The laser renderer object should be responsible for drawing the laser object.

If you don't know what I mean by the *renderer classes, then here's a little example of what I do:
Suppose that you want a tank in a game. These are the classes I would create:
* the Tank class:
Responsible for moving the tank and shooting the Tank's cannon object.
* the TankCannon class:
Responsible for shooting projectiles.
* the TankRenderer class:
Responsible for rendering a Tank object.
* the TankHumanController class:
Responsible for controlling a Tank object based on human input.
* the TankComputerController class:
Responsible for controlling a Tank object based on AI.

Each controller class, except for Tank, which "controls" a TankCannon object, has an update function, and is passed the object it should control on creation. Each renderer class has a render function and is passed the object it should render on creation. Both the Tank and the TankCannon class know nothing about how they are shown (in this case rendered) or how they are controlled.
This is the "proper" way of doing it; because if you group all of those classes together like most people do, you violate the Single Responsibility Principle (SRP).

Share this post


Link to post
Share on other sites
MKH's is the technically most accurate, although it doesn't deal with the real question of who manages the resources. The renderer should be responsible for the low-level task of doing the actual drawing, as it is the only bit of the system able to properly decide on global graphics operations such as sorting, culling, clipping and lighting.

ld

Share this post


Link to post
Share on other sites
The weapon should spawn a muzzle flash, impact thing and beam and just put them into the world, then they should be responsible for drawing and updating themselves.

And even rendering themselves is an open question I generally prefer having the renderer doing the drawing and that each object don't know how to render itself but can tell the render of an model-id (model in the broadest way possible, gotten from the renderer in the first place) that it would like rendered at a given position.

Share this post


Link to post
Share on other sites
Quote:
Original post by bytecoder
1) The muzzle renderer object for the unit's "muzzle" object is responsible for drawing the muzzle flash.
2) Not sure what you mean. Are you talking about the results of the laser hitting the second unit? If so, then the unit renderer for the unit that got hit is responsible for drawing it.
3) The laser renderer object should be responsible for drawing the laser object.

If you don't know what I mean by the *renderer classes, then here's a little example of what I do:
Suppose that you want a tank in a game. These are the classes I would create:
* the Tank class:
Responsible for moving the tank and shooting the Tank's cannon object.
* the TankCannon class:
Responsible for shooting projectiles.
* the TankRenderer class:
Responsible for rendering a Tank object.
* the TankHumanController class:
Responsible for controlling a Tank object based on human input.
* the TankComputerController class:
Responsible for controlling a Tank object based on AI.

Each controller class, except for Tank, which "controls" a TankCannon object, has an update function, and is passed the object it should control on creation. Each renderer class has a render function and is passed the object it should render on creation. Both the Tank and the TankCannon class know nothing about how they are shown (in this case rendered) or how they are controlled.
This is the "proper" way of doing it; because if you group all of those classes together like most people do, you violate the Single Responsibility Principle (SRP).


ByteCoder,

Is this what you mean?

Share this post


Link to post
Share on other sites
Byte:

Whether you know it or not, your little schpeil on implementing a tank has cleared up a lot of design issues I was having as well. Ever consider writing design tutorials?

Sean:

What did you use to create that UML design? I wasn't aware that there were programs and have been using Excel.

Share this post


Link to post
Share on other sites
Quote:
Original post by DesCr
Byte:

Whether you know it or not, your little schpeil on implementing a tank has cleared up a lot of design issues I was having as well. Ever consider writing design tutorials?

Sean:

What did you use to create that UML design? I wasn't aware that there were programs and have been using Excel.


Enterprise Arhitect is the best program you can get for a reasonable price.

http://www.sparxsystems.com.au/

There are still a number of issues with the design; I have only posted the first question. Once I get some feedback I will evolve the model.

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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++ :)

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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).

Share this post


Link to post
Share on other sites
I'm still not convinced that the design you guys are presenting is worth the extra effort. I will certainly agree, though, that it gets the stamp of approval for the various OO design principles (SRP, DIP, etc.). I think it might be interesting to somehow reduce the need to create four or five classes for every entity, yet still retain the minmized dependencies and such.

What I present here is the design of small portion of my engine as it stands right now. The interesting dependencies and objects, relating to this discussion, have been marked. I would love for some "compare and contrasting" between my design and the one bytecoder is suggesting. I really would like to understand why one design is better or worse than the other.

A rather amusing side-note: I am currently working on a Level Editor plugin for Maya. I have fully applied the abovementioned OO design principles to it, and it is coming along incredibly well. The responsibilities are incredibly broken up and the dependencies minimized. It started as an assignment in school. When we turned it in the teacher told us to not turn in the solutions. Jokingly, I commented that he probably wants mine cause I have a lot of files. He suggested that six was a lot. At that point in development I had about eighty files (h, cpp, and inl). So I gave him the solution. heh.

Share this post


Link to post
Share on other sites
Clash,

Very interesting design! I have a bunch of questions; here are a few off the top of my head:

1. What is your first and second dispatch?

2. Since IRenderable is inherited from IPosition, I am assuming that IPosition has an empty not pure virtual Render funtion just incase the Position is not renderable?

3. Can you expand on your PysicalBehavior enumerted type? How does it work?

Share this post


Link to post
Share on other sites
Wow, that design is very similiar to the one I described earlier. You must be a very good designer ;) (ya know, I say it's good because it looks like mine, so therefore I'm complimenting myself...yeah, I don't get out much). Anyways, I don't have much time to talk now, hope to debate with you later :)

Share this post


Link to post
Share on other sites
Quote:
1. What is your first and second dispatch?

This feature is something that I am still mucking with. Currently the physics system invokes a virtual callback on both entities involved in the collision, and passes the other object to it. I don't like that because it requires having switch statements on the type of the other object in the callbacks. It also puts you in a situation where collision behavior can go in one of two objects.

SO, the double dispatch method would ideally use a hashmap of hashmaps, which would end up being a sparse matrix of callback functions. The first hashmap would be the "first" entity in the collision and the second hashmap would be the "second" entity. The types (memory addresses of the RTTI objects) would be used as the keys. If the target function is not found, the system would flip the "first and second" entities, and search again. If no callback is found, no response is signaled to the game. [note: My first attempt at trying this would be using the hash_map class. If it proved to be too slow, a custom hash function would need to be written which reliably mapped the memory addresses to a lookup table]


void PM::DoCollisions()
{
//...

if(!FindAndInvoke(firstEntity, secondEntity))
FindAndInvoke(secondEntity, firstEntity)

//...
}

bool PM::FindAndInvoke(IPositionable* firstEntity, IPositionable* secondEntity)
{
DDTable::iterator sit = m_DDTable.find(&firstEntity->GetRTTI());

if(sit == m_DDTable.end())
return false;

DDTable& callbackTable = sit->second;
sit = callbackTable.find(&secondEntity->GetRTTI());

if(sit == callbackTable.end())
return false;

sit->second(firstEntity, secondEntity);
return true;
}


The benefit of this is that it maps perfectly to the traditional "Component Interaction Matrix," and we all know that the more the solution in code aligns with the problem-domain the better the solution. Not to mention, you no longer need switch statements in the various collision callbacks.

One caveat to this, that I can see, is that the callbacks would have to be of the form void (*Callback)(IPositionable*, IPositionable*);. This requires the callback to downcast the objects to their correct types. Normally that would be unsafe, but in this situation, the callback would only be invoked if the objects were in fact the expected types, so it's safe. (Avoiding diamond hierarchies allows static_casting to work in this situation).

A side-effect of this design is that all manipulations to the objects must be through their public interface. Some could see this as a good or bad thing. I see it as a positive outcome, because it requires you to more accurately model the responsibilities of the entities.

This idea grew out of the discussion of double-dispatch techniques in Modern C++ Design. Great book.

Quote:
2. Since IRenderable is inherited from IPosition, I am assuming that IPosition has an empty not pure virtual Render funtion just incase the Position is not renderable?

Nope. Notice that the Renderer and the PhysicsManager use visitors to navigate the SceneGraph and collect the objects they each need to act on. This way, the only virtual function needed is the traditional Visit(). This implies, of course, that all visitors that walk the scene graph are derived from some common visitor type.

The ideal solution to this would be to seperate the physics properties from the positional properties of the object. Something like: IPositionable <-- IRenderable. IPositionable <-- IInteractable. IRenderable AND IInteractable <-- Tank.

That has a better seperation of responsibilities imo, but it leaves us with the diamond inheritance problem. So rather than having the diamond, the PhysicalBehavior enumeration is used.

[qupte]3. Can you expand on your PysicalBehavior enumerted type? How does it work?[/quote]
The PhysicalBehavior enumeration works as a bitfield. It is used to set the types of physical properties that the object has. The physics manager then modifies the object differently depending on its properties. For instance, if the IS_COLLIDABLE bit is not set, then the entity is skipped for collision. If APPLY_WATER_PHYSICS is enabled, then the forces that get applied to the object would be adjusted to simulate the presence of water.

This idea comes from the UT engine.

Quote:
Wow, that design is very similiar to the one I described earlier. You must be a very good designer ;) (ya know, I say it's good because it looks like mine, so therefore I'm complimenting myself...yeah, I don't get out much). Anyways, I don't have much time to talk now, hope to debate with you later :)

Thanks :D. I'd love to hear your thoughts on the differences between the two designs.

Share this post


Link to post
Share on other sites
Clash:
I am most interested by your use of patterns and OO mechanisms. Do you use any in handling collision resolution?

Slightly OT:
What happens (or should happen) when, over the space of one frame or tick, two spheres go from not having collided yet to intersecting each other. Say you wanted them to collide and touch but not overlap. Who can one prevent this?

Anthony

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