views:

473

answers:

11

I'm writing a game where the mouse-driven controller object clicks on a player object to have it do something.

There are 2 ways of initiating the interaction between mouse and player:

  • Controller calls player's function:
    Controller listens for mouse events. When a mouse click occurs anywhere onscreen, the controller searches all objects under the point that was clicked. If one of the objects is a player object, and it's "clickable" property is true, then call its appropriate function.
  • Player calls controller's function:
    Player listens for mouse events. When a mouse click occurs on the player, and the player's own "clickable" property is true, then call the controller's appropriate function.

My dilemma here is that the first option seems more intuitive with how I imagine the scenario happening in the real world, but the second option seems more intuitive with proper object-oriented design because it doesn't require looking into another object's property, which violates encapsulation to some extent (the controller must look into the player to read its "clickable" property). Also, the second option seems inline with the 'Controller' design pattern.

This is always a struggle for me -- do I defy proper object-oriented design (e.g. option 1) or do I use an implementation that seems counterintuitive to the real world (e.g. option 2)?

I'm hoping there's some kind of middle ground I'm missing.

+1  A: 

This often comes to preference. Logical games design is, very frequently, at odds with good OOP design. I lean towards what makes sense in the scope of the game universe, but there's absolutely no right answer to the question and every single problem should be taken on its own merit.

This is somewhat similar to arguing about the pros and cons of camel casing.

Rushyo
+3  A: 

MVC concept is a good practice in general and should still be used in game designs as a general principle. But by the interactive nature of the game UI, you should also look into event based architecture. MVC does not contradict with event based architecture if designed carefully. (PureMVC as an example.)

I suggest you to use observable pattern on all display objects so they can all listen/fire events. This will save you a lot of headache later on. When your code base gets more complex, you will eventually HAVE TO use more decoupling techniques such as your option 2 describes. Also mediator pattern will help as well.

Edit:

Mediator Pattern is generally a good way to organize application level events.

Here is a blog about using MVC, events and mediators in game programming:

http://ezide.com/games/writing-games.html

Aaron Qian
Can you elaborate? Maybe provide a short example? I know of the event-based architecture and you'll see that both scenarios make use of events and listeners (thus implementing 'Observer'). So, I thought I was using it.
Pup
Edited my post to provide some more info, hope this helps. Also, I think you are using observable pattern
Aaron Qian
+5  A: 

Why not go with Option 3, which is simlar to Option 1?

  • Controller calls player's function:
    Controller listens for mouse events. When a mouse click occurs anywhere onscreen, the controller searches all objects under the point that was clicked. If one of the objects implements IClickable or inherits Clickable (or whatever), then call its appropriate function (or fire an event, whichever is appropriate).
strager
Actually, I'm using this option. I excluded the interfaces from my example because they only seem to obscure the heart of my struggle.
Pup
@Pup, There's a large difference and I would prefer Option 3 to Option 1 any day.
strager
This doesn't obscure your struggle. Implementing iClickable (or something equivalent) makes clickability part of the object's interface, thus querying an object's clickability is not in violation of encapsulation. End of struggle.Further, I would interpret the "Tell, don't ask"-principle as saying that the player should be told it has been clicked rather than asking about having been clicked.
Waquo
Also, dont forget that one of the most important principles of OOD is *polymorphism*. This is definitely 100% the way to go - both from a theoretical OOD point of view, and also from a pragmatic PoV.
AviD
Slashmais - while some part of your response is true, what is missing is the fact that the controller will often not know what action to take! and therefore the player object is the one that has to make that decision. For example, one type of object may jump, another will be selected and flash, another might just explode. The controller does not, indeed, can not know what action to take. Moreover, the controller *should not* know. Thats what delegation of control is all about.
AviD
On the other hand, what might be even better, is to decouple the player classes from handling the UI, but not to put that into the controller - perhaps split each class in two (as needed): one to handle the UI (the clickable part of the class), and this one will know how to interpret these events and which methods to call on the second, app-logic part of the class.
AviD
+5  A: 

This is always a struggle for me -- do I defy proper object-oriented design (e.g. option 1) or do I use an implementation that seems counterintuitive to the real world (e.g. option 2)?

I don't think that the purpose of object-orientation is to model the real world.

I think that a (the?) reason why an OO model often follows a model of the real-world is that the real-world doesn't change much: and therefore choosing the real world as the model means that the software won't change much, i.e. it will be inexpensive to maintain.

Fidelity to the real world isn't a design goal in itself: instead you should be trying for designs which maximize other metrics, e.g. simplicity.

The 'objects' in 'object orientation' are software objects, not necessarily real-world objects.

ChrisW
This was very helpful to me! I forgot the fundamental dichotomy between computational and verbal communication -- computational interactions are very impersonal; When you interact with an object, you tell it to do something with itself. Whereas in English grammar, a subject carries the responsibility of the action upon its direct object; you wouldn't say the action is being done by the direct object, e.g. a ball. The same philosophy applies here -- don't expect the instigator of action to be carrying it out, rather, it will be up to an object to act upon itself. Still undecided.
Pup
+1 OOP was never designed to model the real world. It was designed to allow more reuse of code (which didn't really work out well). You shouldn't try to model the real world when you design something (otherwise, most OOP patterns wouldn't make sense). As long as it works well and makes sense, don't bother trying to make it conform to a particular design dogma.
SnOrfus
+4  A: 

The second method is the decidedly more idiomatic Flash way of handing things. AS3 has the event model build right into EventDispatcher and all DisplayObjects inherit from it. That means any Bitmap, Sprite or MovieClip immediately knows if it is clicked.

Think of the Flash Player as your controller. When I do MVC in Flash I almost never write a controller because the Flash Player does it for you. You're wasting cycles determining what was clicked on when the Flash Player already knows.

var s:Sprite = new Sprite();
s.addEventListener(MouseEvent.CLICK, handleMouseClick);
function handleMouseClick(event:MouseEvent):void
{
    // do what you want when s is clicked
}

I'd probably not directly access a controller from inside a sprite (probably in the view). Instead I would dispatch an event (probably a specific custom event that matched the circumstance). Make your decisions on how many times per-frame something happens. Responding to a user interaction (e.g. mouse-click) usually gives you the freedom not to worry about the overhead in event systems.

Finally - the reason I suggest this has nothing to do with some Ivory Tower concept of OOD or OOP. Principles such as these exist to help you not constrain you. When it comes down to a matter of pragmatics, go with the easiest solution that won't cause you headaches down the line. Sometimes that means doing OOP, sometimes it means functional, sometimes it means imperative.

James Fassett
"I'd probably not directly access a controller from inside a sprite (probably in the view). Instead I would dispatch an event (probably a specific custom event that matched the circumstance)."So, you mean you would dispatch a custom event from the player object that the controller would listen to the Display List for?
Pup
It really depends on your object hierarchy. I tend not to nest things too deeply (which is a good idea anyway) so I can manually bubble events to a workable level easily. I also create "view" classes that logically aggregate multiple display objects together. So typically a view will listen to it's DisplayObjects and it will interface with the Model on their behalf.
James Fassett
+1  A: 

i think, it is important to seperate input logic from application logic ... an approach is to convert input events (be it user input, or some data arriving through sockets/localconnections etc.) into application events (events in an abstract meaning of the word) ... this conversion is done by what i'd call "front controllers" for the lack of a better term ...

all of these front controllers simply convert events, and are thus completely independant of how the application logic responds to specific events ... the application logic on the other hand is decoupled from these front controllers ... the "protocol" is a predefined set of events ... when it comes to the notification mechanizm, it is up to you, whether you use AS3 event dispatching to get events from front controllers to application controllers, or whether you build them against some specified interface, which they will call to ...

people tend to write application logic into click-handlers for buttons ... and sometimes even to trigger those handlers manually, because they don't want to tidy up things ... nothing i haven't seen yet ...

so yeah, it's definitely version 1 ... in this case, the front controller for mouse inputs only needs to know the display list, and have logic about when to dispatch what event ... and the appliction controller needs to be able to handle some sort of PlayerEvent.SELECT event in this case ... (if later you decide, to have some sort of tutorial mode, or whatever, you can simply move around the fake mouse and dispatch this event in the case of fake clicks, or you can simply repeat everything in some sort of replay thing, or you can use this for recording macros, when it's not about games ... just to point out some scenarios where this separation is helpful)

hope this helps ... ;)

back2dos
+2  A: 

According to Applying UML and Patterns (Craig Larman) the User Interface (your mouse events) should never interact with your application classes, that is, the user interface should never drive the business logic directly.

Instead one or several controllers should be defined to act as a middle layer to the user interface, so option 1 does in fact follow a good object oriented approach.

If you think about it, it does make sense to couple as fewer classes as possible to the UI in order to make business logic as independent of the UI as possible.

Jorge Córdoba
+1  A: 

I'd have to disagree with your assessment of the two options. Option 2 is worse OO because it tightly couples the Player object to the specific user interface implementation. What happens when you want to re-use your Player class someplace where it's offscreen or a mouseless interface is being used?

Option 1 can still be improved on, though. The earlier suggestion of using an iClickable interface or Clickable superclass is an immense improvement, as it allows you to implement multiple types of clickable objects (not just Player) without having to give the controller a huge list of "Is the object this class? Is it that class?" to go through.

Your main objection to option 1 appears to be that it checks the Player's "clickable" property, which you feel violates encapsulation. It doesn't. It checks a property which is defined as part of the Player's public interface. This is no different than calling a method from the public interface.

Yes, I realize that, at this moment, the "clickable" property happens to be implemented such that it's a simple getter which does nothing more than query the internal state of the Player, but it doesn't have to be. The property could be redefined tomorrow to determine its return value in a completely different manner with no reference to the internal state and, so long as it still returns a boolean (i.e., the public interface remains the same), code using Player.clickable will still work just fine. That is the difference between a property and direct inspection of internal state - and it's a mighty big difference.

If that still makes you uneasy, though, it's easy enough to eliminate having the controller check Player.clickable: Just send the click event to every object under the mouse which implements iClickable/descends from Clickable. If the object is in a non-clickable state when it receives the click, it can just ignore it.

Dave Sherohman
+1  A: 

In any OOP language it is almost always the correct thing to do to follow the idiomatic approach when there is also the possibility of following the real-life emulation approach. So to answer your question, the second approach is almost certainly the better, or may prove to be the better as you delve deeper into the design or later feel the need to change or add to it.

This shouldn't stop you of finding other solutions. But try to always stay with the language idiom. OOP does not translate to real life in a 1 to 1 relationship, neither it is particularly good at emulating it. An illustrative example is the classic rectangle and square object relationship which you probably already know all about. In real life a square is a rectangle. In OOP (or at least in proper OOP) this relationship doesn't translate well into a base-derived relationship. So you feel the need to break away from real-life emulation because the language idiom speaks higher. It's either that, or a world of pain when you start seriously implementing both rectangle and square or later wish to make changes to them.

Krugar
Rectangle/Square relationship seems okay to me... Care to explain or cite another analogy?
Pup
Essentially, under most situations, If you decide a square is a type of rectangle you run into problems. That is if you derive square from rectangle you'll be mapping the taxonomical relationship in a is-a rule. But: http://stackoverflow.com/questions/1030521/is-deriving-square-from-rectangle-a-violation-of-liskovs-substitution-principle
Krugar
+1  A: 

This is always a struggle for me -- do I defy proper object-oriented design (e.g. option 1) or do I use an implementation that seems counterintuitive to the real world (e.g. option 2)?

Reality may be a good starting point for molding or evolving a design, but it is always a mistake to model an OO design to reality.

OO design is about interfaces, the objects that implement them, and the interaction between those objects (the messages they pass between them). Interfaces are contractual agreements between two components, modules, or software sub-systems. There are many qualities to an OO design but the most important quality to me is substitution. If I have an interface then the implementing code better adhere to it. But more importantly, if the implementation is swapped then the new implementation better adhere to it. Lastly, if the implementation is meant to be polymorphic then the various strategies and states of the polymorphic implementation better adhere to it.

Example 1

In mathematics a square is a rectangle. Sounds like a good idea to inherit class Square from class Rectangle. You do it and it leads to ruin. Why? Because the client's expectation or belief was violated. Width and height can vary idependently but Square violates that contract. I had a rectangle of dimension (10, 10) and I set the width to 20. Now I think I have a rectangle of dimension (20, 10) but the actual instance is a square instance with dimensions (20, 20) and I, the client, am in for a real big surprise. So now we have a violation of the Principle of Least Surprise.

Now you have buggy behavior, which leads to client code becoming complex as if statements are needed to work around the buggy behavior. You may also find your client code requiring RTTI to work around the buggy behavior by testing for conrete types (I have a reference to Rectange but I have to check if it is really a Square instance).

Example 2

In real life animals can be carnivores or herbivores. In real life meat and vegetables are food types. So you might think it is a good idea to have class Animal as a parent class for different animal types. You also think it is a good idea to have a FoodType parent class for class Meat and class Vegetable. Finally, you have class Animal sport a method called eat(), which accepts a FoodType as a formal argument.

Everything compiles, passes static analysis, and links. You run your program. What happens at runtime when a sub type of Animal, say a herbivore, recieves a FoodType that is an instance of the Meat class? Welcome to the world of covarience and contravarience. This is a problem for many programming languages. It's also an interesting and challenging problem for language designers.

In Conclusion...

So what do you do? You start with your problem domain, your user stories, your use cases, and your requirements. Let them drive design. Let them help you discover the entities you need to model into classes and interfaces. When you do you'll find that the end result isn't based on reality.

Check out Analysis Patterns by Martin Fowler. In there you'll see what drives his Object Oriented designs. It is mainly based on how his clients (medical people, financial people, etc.) perform their daily tasks. It has overlap with reality, but it isn't based or driven by reality.

Nicholas Salerno
+1  A: 

Another approach would be to create an IClickHandler interface. All objects that register for clicks do so by handing an IClickHandler to the controller. When an object is clicked the controller calls the clicked() method on the registered IClickHandler. The IClickHandler can forward or not forward the call to the method registered with it. Now neither your controller nor your object is making the decision about whether or not said object has truly been clicked.

The IClickHandler could also be chosen based upon other criteria (IOW the object doesn't choose the IClickHandler itself upon registration, some other algorithm chooses it). A good example would be all NPC objects are given an IClickHandler that forwards the clicks, while all tree's are given an IClickHandler that doesn't forward the clicks.

At a minimum you could have 3 handlers that implement the interface: AlwaysClickable, NeverClickable, ToggledClickable

Just keep in mind that the above does sport more moving parts as well as a small performance hit, but it gives you a lot of flexibility (its up to you to decide if the flexibility is worth the added complexity).

Also note that it's best not to simply adhere to principles of any type. Do whats best given the circumstances at the time you write the code. If you're writing a Tetris clone, the fact that Option 1 "violates" OOP principles is completely irrelevant, you'll never realize the benefits of strict OOP on a project that simple.

Fred