Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

(I asked a similar question, but this one is far more specific).

How can I handle collisions without having to do a lot of type checking and if statements?

People here suggested that when spotting a collision, the collision detector should notify both entities in the collision, and the entities themselves will encapsulate the logic to react to the collision.

I like the idea of having the entities themselves decide how to handle the collisions. However I'm not sure how to avoid doing a lot of type checking and if statements inside the handleCollision methods.

For example (in psuedocode):

class CollisionDetector{
    foreach entity in entities
        foreach otherEntity in entities
            if(collision(entity,otherEntity)) entity.handleCollision(otherEntity);
}

class Entity{
    // .. stuff omitted
    void abstract handleCollision();
}

class Alien extends Entity{
    // .. stuff omitted
    void handleCollision(Entity entity){
        // lots of ugly conditionals and type checking
        if(entity instanceof Missile) die();
        if(entity instanceof Alien) lifePoints -= ((Alien)entity).getDamagePoints();
        if(entity instanceof Wall) setVelocity(0,0);
        // .. etc
    }
}

In OOP we should try to avoid type checking and employ Polymorphism. But I don't see how this can be done in this case.

Is there a way to avoid the ugly type checking and lots of conditionals? Or should I just make my peace with it? How is this most commonly done?

share|improve this question
    
This is closely related to this question which may be useful to look at. –  NauticalMile Jun 15 at 17:53
    
Also, this. –  NauticalMile Jun 15 at 17:53
    
So you would rather do something that looks prettier in code and is far slower than an if? –  badweasel Jun 16 at 9:13
    
@badweasel I'm used to thinking that having lots of else ifs, and more importantly type checking, is bad practice, and that Polimorphism should be used instead. However I`m not an experienced game dev, so I'm not sure how and if this applies to game development. In your opinion, is the situation I described okay? Having lots of type checking but encapsulated in the relevant classes? –  Prog Jun 16 at 11:06
    
Well use logic and math and avoid a bunch of unnecessary branching. But I hate the modernism abstraction bs. Write fast code that works. –  badweasel Jun 16 at 18:56

2 Answers 2

You're correct that the problem (well, nuisance) is the if statements. Sadly, no matter what language your working in or what programming fanciness you employ you are going to have to check some component data / entity types / other flags on the colliding entity that will tell the present entity how to react to a collision.

Good Practices (my experience)

There are 2 practices that I have found useful to minimize these checks.

  1. First of all, I find it helpful to think about which entities would actually dictate the collision outcome in real life. Take your Alien for example. Imagine a real alien colliding with a real missile, and consider two scenarios:

    • The alien, upon making contact with the missile says to himself:

      By jove, it appears I've struck a missile. My alien behaviour manual says that I must promptly commit suicide.

      after which he pops out a potassium cyanide capsule from between his teeth and swallows.

    • Another possibility is that the missile, upon colliding with the alien says to itself:

      Wot 'ave we got 'ere? An alien et seems. Looks loike I'm 'upposed to make a right massive explosion.

      The missle then detonates itself.

    Neither of these scenarios are realistic, but the second one is a lot closer to what would actually happen than the first. When the Missile collides with an Alien the reaction should be to blow up. This should, in turn, trigger another event / function call to deal damage to surrounding entities (And methods for doing that have already been discussed). Once the Alien's health reaches 0, then he can kill himself1. The code for when the Alien's reaction when colliding with a Missile is ... nothing; there's nothing the Alien can really do at this point.

    I found that when I continually asked myself Who's really dictating this collision outcome?, the elseif chains for checking types became remarkably short. For example, for a large number of Entity types it doesn't matter what the missile is colliding with, the response will be to explode. This might be the default behaviour (i.e. the else at the end of your if-elseif chain). In a similar way it's the Wall who stops the Alien, not the Alien who stops when it collides with a Wall. Then all of a sudden it matters little what the moving entity is when it collides with a Wall; the reaction will almost always be to stop it (otherEnt.setVelocity(0,0)).

  2. If you use some variation of an Entity-Component System you can (in some cases, I have found) check for the existence of a component on the opposite entity to determine a subsequent reaction. I won't go into detail about that here since it will make the answer much longer than it already is, and you don't seem to be using an ECS (yet) anyway.

Long elseif chains: A Game Design flaw?

Finally, I will say that if you find yourself coding long elseif chains, it could be symptomatic of a design issue: the rules of your game could be too complicated. If you have a long list of different behaviours for each combination of colliding entities, your players will need to memorize (through frustrating trial-and-error or mind-numbing tutorials) all of these potential collision outcomes. If the challenge of memorizing possible collision outcomes is part of what makes the game fun (and you carefully introduce these rules), then long elseif chains are to be expected, otherwise, they're sure to be the source of many curse words and damaged controllers / keyboards. If players are confident about the effects certain actions will have, they spend more time thinking about strategy and less time wondering how the game world works.

An example: Portals

Consider Portal for example: there are walls that support portals (white panels, tan walls, etc...) and the rest don't. There are no walls that make small portals, nor are there walls that permit only orange portals, nor are there walls that periodically support portals... It's easy for game designers and players to understand. So the game could be designed so that when the blue/orange projectile that is shot from the portal gun (not sure what that thing's called) hits a wall it does this:

listOfPortalWalls = {'White Panel', 'Tan Wall', 'Concrete Floor', /*etc...*/}

...

class PortalBall{

  ...

  void handleCollision{
    if(listOfPortalWalls.contains(otherEnt.type)) //We can make a portal :)
      /* Test if there's already a portal there (edge case, don't sweat the if statement here) */
      /* Make a portal */
    else //We can't make a portal :(
      /* Explode the portal ball with a discouraging sound effect */
   }
}

That elseif chain was so short it didn't even have an elseif! Notice that it's the PortalBall's job to make the portal (as I would expect if portals were real). The wall wouldn't do anything. All the player has to figure out is which walls support portals. The portal gun never runs out of energy and it has a pretty short cooldown (not to mention that there are seldom time pressure situations), so experimenting to figure out which walls support portals is a painless experience.

Edit: To clarify, the elseif chains are a fine way of going about things IMO; but you can make things easier by carefully probing which entities are actually responsible for the outcomes of a collision, and placing that code in the relevant Entity subclasses.


1 This case could potentially be made more realistic by making a system that slowly removes the Alien's available actions restricts behaviours as the health is lowered (e.g. when health is below 50% can no longer attack, and max movement speed is proportional to health). So that when the health reaches zero the alien is a complete vegetable (effectively dead). I would not recommend doing this excessively because if you keep trying to approximate realism your game may become needlessly complicated, making both debugging harder (more code) and game design harder (more complicated game states).

share|improve this answer
    
Thanks for the answer :) So in your opinion the approach described in my question, even though not perfect, is fine? –  Prog Jun 15 at 21:38
    
The structure is good, but I wanted to emphasize that if(entity instanceof Missile) die(); and if(entity instanceof Wall) setVelocity(0,0); are not good candidates for the Alien class. Things will be much easier if you put them on the Missle and Wall classes respectively –  NauticalMile Jun 15 at 22:00

While the conditional block is inevitable, your implementation has one serious flaw:

Every new type of Entity you create will require you to alter the handleCollision() for every entity it could affect - in order for it to respond to the new type of collision. To prevent this you could rather use an attribute in the base Entitiy that "classes" each one's collision response.

For example, walls, trees, rocks, etc might all be of an "obstacle" type and they would all stop the player, whereas rockets, bullets, lasers, etc would be "weapon" types, which would cause damage...and so on. Then, for stuff like rockets which have a secondary response, i.e. to explode, this should be taken care of in it's own handleCollision() instead of the alien's one - because it will always explode, no matter what it collides with, but it may cause no damage to a wall (for instance).

Using this your code would become something like follows (in the alien's handleCollision()):

class Entity{
    Type type;   // entity type
    int damageAmount;  // damage amount (for required types)
    float bounceCoef;  // a bounce coefficient for static object
    // any other attributes you can think of goes here
    void abstract handleCollision();
}

class Alien extends Entity{
   // .. stuff omitted
   void handleCollision(Entity entity){
      // lots of ugly conditionals and type checking
       if(entity.type == weapon) damage(entity.damageAmount);
       if(entity.type == obstacle) setVelocity(0,0);
       // .. etc
   }
}

class Missile extends Entity{
   Missile(){
      type = weapon;          // just an example
      damageAmount = 100;      // again, example :)
      // setup other attributes here
   }

   void handleCollision(Entity entity){
      // the damage is already done, so this only needs to do it's
      // entity specific action - which is to...
      explode();   
   }
}

Of course that is a very very basic implementation, but the main benefit here is that when you add new types of entities that fit into the current "types" no further changes are required - you just setup their attributes and create their entity specific collision behavior.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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