1

I am designing a basic game engine. I have an abstract GameObject class which are inherited by two classes Player and Bullet. I am trying to handle collision of two game objects. If both of the collided objects are the same, nothing happens. If one of them is bullet and other is player, the player's health is set to 0. Here is a snippet showing the required method and class definitions:

public interface GameObject {
    void onCollision(GameObject gameObject);
}

public class GameObjectImpl implements GameObject {
    ...
    // By default, do nothing.
    @Override
    public void onCollision(GameObject object) {}    
}


public class Player extends GameObjectImpl {
   ...
   @Override
   public void onCollision(GameObject gameObject) {
      if (gameObject instanceof Player) {
          // Do nothing
      } else if (gameObject instanceof Bullet) {
          this.health = 0;
      }
   }
}

I want to avoid instanceof but I could not think of any way to do so. I wanted to implement it with using polymorphism as clients of GameObject interface should not know the details about bullets or players etc. It will just call onCollision method for objects in each frame. How can I achieve this with a cleaner code? Thank you.

Onur Arı
  • 502
  • 1
  • 4
  • 16
  • 2
    That kind of inheritance hierarchy doesn't work very well. While players and bullets may both be "game objects" in the abstract sense, they have very little in common. Methods can't be nicely written to handle different game objects, so you'd have to resort to `instanceof`. That's why game engines tend to use the [Entity-Component-System](https://en.wikipedia.org/wiki/Entity_component_system) model, which avoids inheritance (and [composition should be preferred over inheritance](https://stackoverflow.com/questions/49002/prefer-composition-over-inheritance) anyway). – Kayaman Mar 25 '20 at 11:59

1 Answers1

1

It might look cumbersome because Javas mechanism of resolving target methods is not the best, but you can try something similar to

public class Player extends GameObjectImpl {
   ...
    @Override
    public void onCollision(GameObject gameObject) {
        gameObject.collideWith(this);
    }
}

and then extend the GameObject with the appropriate "callback":

public interface GameObject {
    void onCollision(GameObject gameObject);
    void collideWith(Player player);
}

and then

public class Player extends GameObjectImpl {
   ...
    @Override
    public void onCollision(GameObject gameObject) {
        gameObject.collideWith(this);
    }

    @Override
    public void collideWith(Player player) {
        // ... do nothing, shouldn't happen
    }
}

and

public class Bullet extends GameObjectImpl {
   ...
    @Override
    public void onCollision(GameObject gameObject) {
        gameObject.collideWith(this);
    }

    @Override
    public void collideWith(Player player) {
        player.setHealth(0);
    }
}

For further reading you might search for the Visitor pattern in Java.

Smutje
  • 17,733
  • 4
  • 24
  • 41
  • 1
    Actually, that is not the “visitor” pattern, it’s “double dispatch”, which the visitor pattern *uses*. Misnaming double dispatch as visitor is a common mistake. Nevertheless, this is the best approach. – Bohemian Mar 25 '20 at 12:00
  • That's why I wrote "Visitor pattern in Java" ;-) But yes "Visitor implementation in Java" would be less confusing maybe. – Smutje Mar 25 '20 at 12:01
  • 1
    But it’s not the visitor pattern at all. Not even slightly. – Bohemian Mar 25 '20 at 12:01
  • True, but I didn't wrote that I implemented the Visitor pattern – Smutje Mar 25 '20 at 12:03
  • But, isn't it bad that I will end up with many overloaded `collideWith` methods and they will be seen by the client (which I tend to avoid)? – Onur Arı Mar 25 '20 at 12:08
  • You can put empty implementations in the super class as you already did it so the subclasses only contain the collisions they need – Smutje Mar 25 '20 at 12:10
  • But as I already wrote - look up the Visitor pattern how to extract the logic per subclass into another class who's only responsibility is to handle the collisions and then your GameObjects will only contain an interface to "accept" visitors. – Smutje Mar 25 '20 at 12:12