2
\$\begingroup\$

I am writing a game with Java. There are many different kinds of entities on the screen, like the player, NPCs, monsters, bullets, items, shiftable blocks and so on.

This question is not about collision detection, but about collision handling. If two entities collide, it depends on their type what should happen. Some examples:

  • If the player and a monster collide, the player should loose a life.
  • If a monster and a bullet collide, the monster should die.
  • If the player and an NPC collide, a dialog should start.
  • If the player and an item collide, the player should pick it up.

Now this could be done like this:

public void checkCollision() {
   for (Entity a: entities) {
      for (Entity b: entities) {
         if (collision(a,b)) {
            if (a instanceof Player && b instanceof Monster) a.loseLife();
            if (a instanceof Player && b instanceof Item) a.pickUp(b);
            ...
         }
      }
   }
}

But I heard that instanceof should not be used for this purpose. So I wonder what is the best way to handle the collision of the different combinations? I heard about the visitor pattern, but this would require me to change the classes of the entities and handle the collision there, what seems very wrong to me.

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

Well, as some kind of solution I may suggest that in your case you should use something called Double Dispatch: the pattern Visitor actually.

Let's look at your example briefly:

  1. If the player and a monster collide, the player should loose a life.
  2. If a monster and a bullet collide, the monster should die.
  3. If the player and an NPC collide, a dialog should start.
  4. If the player and an item collide, the player should pick it up.

The code can be like this:

public interface ICollideable
{
    void resolveCollision( ICollideable collideable );

    void resolveCollision( Monster monster );
    void resolveCollision( Npc npc );
    void resolveCollision( Player npc );
    void resolveCollision( Item npc );
    void resolveCollision( Bullet npc );
}

public class Entity implements ICollideable
{
    public abstract void resolveCollision( ICollideable collideable );

    // virtual methods.
    public void resolveCollision( Monster monster ) { }
    public void resolveCollision( Npc npc ) { }
    public void resolveCollision( Player npc ) { }
    public void resolveCollision( Item npc ) { }
    public void resolveCollision( Bullet npc ) { }
}

public class Player extends Entity
{
    @Override
    public void resolveCollision( ICollideable collideable )
    {
        collideable.ResolveCollision( this );
    }

    @Override
    public void resolveCollision( Monster monster )
    {
        this.health -= monster.getDamage();
    }

    @Override
    public void resolveCollision( Item item )
    {
        this.inventory.addItem( item );
    }

    @Override
    public void resolveCollision( Npc npc )
    {
        NavigationManager.openQuestListUI( this, npc );
    }
}

public class Monster extends Entity
{
    @Override
    public void resolveCollision( ICollideable collideable )
    {
        collideable.ResolveCollision( this );
    }

    @Override
    public void resolveCollision( Bullet bullet )
    {
        this.die();
    }
}

Sorry, there may be some mistakes, because I'm not really good with java syntax. Such approach is useful if your language supports methods overloading (like java or c#).

P.S. You can extend the example and implement both reactions of player and monster (remove health from player and monster on interaction).

Links:

  1. https://en.wikipedia.org/wiki/Visitor_pattern
  2. https://sourcemaking.com/design_patterns/visitor/java/2

Update 1.

There are of course some alternatives floating around, however they are more about whole-game-architect patterns. I will try shortly discuss them:

  1. Entity Component Systems (ECS)

In ECS-like code you want to move far away from holistic classes and objects, so you split the whole state and behaviour into three parts:

a) Component - the pure data. For example Position is just a Component with X, Y and Z (depends on business-logic ofc).

b) Entity - just an ID with which you can associate a set of components with no intersections in types (no way to get two Position in one Entity at all).

c) System - static "whole-universe" things, that can handle Entities with specified set of Components. Good example - GravitySystem. It takes all the Entities with Position, Mass and changes their Velocity. Then MovementSystem or VelocitySystem then process the velocity and turn it into change of Position.

This is just a starting explanation. There are a lot of questions about ECS with nice answers. You can find them by tag entity-system.

  1. Dynamic message-passing (event-passing)

For example, instead of calling resolveCollision method, you pass the message (like CollisionWithAnotherEntityOccured) to whole Entity. It then will use something called MessageBus to re-transfer the message to all interested Components. Maybe there will be AttackerModule with code:

public void onCollision( CollisionWithAnotherEntityOccured message )
{
    var weapon = GetComponent<Weapon>(); // Unity-style.
    message.Entity.Pass( new AttackMessage( weapon.getDamage() ) );
}

The responsibility to handle AttackMessage is now on attacked entity, and if and only if it has HealthComponent with AttackReceiver you can perform a success attack.

This is of course simplified example to demonstrate some powers of such decoupling. It is greatly used in SQRS-pattern.

I hope this additional explanation will help you to choose some way to deal with your current and future problems.

\$\endgroup\$
  • \$\begingroup\$ Thank you! This is the kind of solution that I already read about and it is definitely one way to do it. But I feel that this separates the logic over many different classes and every entity knows about every other entities existence. Everytime a new kind of Entity gets added to the game, all other entity classes have to be changed. My OOP feeling says that the collision handling does not belong into the entity class. So am I just too worried or are there better solutions for this? \$\endgroup\$ – Leif Sabellek Feb 13 '17 at 19:01
  • 1
    \$\begingroup\$ @LeifSabellek I totally agree with your OOP-feeling, so I've added some simple explanations of alternatives in the main answer. Hope it helps. \$\endgroup\$ – Ashkariel Eter Feb 15 '17 at 9:08
  • \$\begingroup\$ What happens if Monster doesn't implement resolveCollision(Player) \$\endgroup\$ – lozzajp Feb 15 '17 at 11:56

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.