3
\$\begingroup\$

I'm building a top-down RPG specifically to practice Object Oriented thinking and design. I am keeping in mind coupling, single-responsibility, and so forth. One design issue that's been bugging me is how to ultimately have objects talk while maintaining the loosest coupling possible. As an example, I'm designing the input class now, which will be made up of two layers, like so:

bad flowchart

The conundrum is that I don't know how to bridge that gap between Input and HeroActor, which is initialized from the PlayState class, which is also where the hero.update() method is called. Let's say Keyboard has just executed

public void keyPressed(KeyEvent e) {
    switch(e.getKeyCode()) {
        //Some cases

        case KeyEvent.VK_UP:
            //Assume InputEvent.INPUT1 is a defined enumeration in InputEvent
            inputHandler.passInput(InputEvent.INPUT1);
            break;

        //Some more cases
    }
}

I've basically landed here:

public class Input {

    public void passInput(InputEvent e) {
        switch(e) {
            //Some cases

            case InputEvent.INPUT1:
                ????????????????????

            //Some more cases
        }
    }

}

My ideas have been:

  • Pass a reference to the instance of HeroActor to the Input object, then have Input directly call the moveUp() method on the HeroActor instance.

    case InputEvent.INPUT1:
        actor.moveUp();
    

    My instinct tells me this is bad OO design as it tightly couples the two classes.

  • Make the instance of HeroActor global and basically do the same as above. Even worse OO design.
  • Write a registerInput(Input inputHandler) function in the Actor class, like so:

    public class HeroActor extends Actor {
    
        //Existing fields and methods
    
        private Input inputHandler;
    
        public void registerInput(Input inputHandler) {
            this.inputHandler = inputHandler;
        }
    

    Still, then the HeroActor would have to poll the inputHandler every frame to find out if any actions have applied to it.

I'm in the same situation trying to figure out how to write the collision engine without passing it specific references to the player's HeroActor instance or the dozens of EnemyActor instances in the PlayState constructor.

Any help would be greatly appreciated. My request is not for specific code, but rather common techniques for handling the many objects in a game without ending up with a god object. I've seen some material suggesting an Observer pattern being applied for things like the input layer above, but before I go too far, I'd like some feedback from the community since I've got so many different answers among all of my Googling today.

\$\endgroup\$
4
\$\begingroup\$

You are right that directly coupling input handlers with actors introduces messy situations: for example, what happens if the actor dies - do you add alive checks in your input handler, or do you go through all your input handlers and invalidate their actor references? What if you are in a different context, like a modal menu?

I'm reminded of the MVC pattern which deals with a similar problem: by introducing an intermediary controller between the models (your actors) and views (your input handlers), you can interchange different views and models without affecting the other, and you get a lot of flexibility. Therefore I think you should have some intermediary between your input handlers and actors.

A common solution is to, yes, use the Observer pattern. One of your actors and input handlers agree to observe/notify on a common subject: movements for player number 1, so any time the input handler notifies about player 1 movement, the observing actor will move in reaction. The neat thing about this little bit of indirection is the flexibility you get:

  • Anything can control the actor as long as they talk about "movements for player number 1" - think AI or scripted scenes. But you know this already, since you've implemented the Command pattern.
  • The input handler can control anything as long as they observe "movements for player number 1". You can control multiple actors, or no actors (when the actor dies), or sneaky beaky like enemies.
  • A typical use case for that last point is driving menu systems.
\$\endgroup\$
  • \$\begingroup\$ Thanks a lot for the reply! That pretty much answered my question. It was well-written, well-grounded, and I appreciate the additional reading. Thanks again! \$\endgroup\$ – Richard Pressler Dec 17 '15 at 6:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.