2
\$\begingroup\$

I'm currently building a small 2D game in Processing (and it has to be Processing) for a school project. The basic makeup of the game is 4 different cells (objects, all are circles): An avatar, controlled by the player, a number of cowardly cells fleeing from all other cells, huntercells hunting and attempting to eat everything, and passive disruptorcells which will burst apart other cells in differnt ways depending on what they hit. Also, whether one cell is bigger than another affects the outcome of collisions. All these different cells are child classes of the cell superclass, all stored in a cells ArrayList, which is handled inside a World class, which is updated/displayed in the main Draw loop.

My main problem right now is how I should handle the outcome of all these different collisions, without too much code-duplication: My idea is to do it through a double for-loop, which would then again need 16 different if-like methods to figure out who's colliding with who, and what to do about it.

E.g. if a predator cell collides with prey cell, the latter will be eaten and the predator will grow a certain amount in size and speed. The outcome should be the same (predator grows, prey dies) if the tested subjects are found in reverse order through the for-loops. Cells also need to know where they are in relation to eachother all the time, so they know when/what to flee from/chase.

Any thoughts/suggestions on best practices and how to do this most efficiently are much appreciated.

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

Checking collision on circles should be easy and has been described very well by Valkea. If your entity amount doesn't go into the thousands, checking all collisions shouldn't be a performance problem, since circle vs. circle tests are very fast.

As for the resolving of the collisions: Don't write if-statements for all possible collision types. Consider something like this instead: Each of your Cell-classes has a method handleCollision which takes a cell-class as parameter. Your collision check could look like this:

// perform a circle vs. circle test
if(checkCollision(cellA, cellB)){
    cellA.handleCollision(cellB);
    cellB.handleCollision(cellA);
}

So it's in the responsibility of the cell class to react to a collision. The handleCollision method of the Predator cell could look like this:

public void handleCollision(Cell otherCell){
    switch(otherCell.type){
        case PREY:
            speed += SOME_SPEED_INCREASE;
            size += SOME_SIZE_INCREASE;
            break;
        case DISRUPTOR:
            // other stuff...
            break;
        default:
            // handle anything else
            break;
    }
}

If your cells are dependent on the current state of the other cell during collision (eg. if the predator gains more speed if he "eats" bigger prey), then you can't apply effects of the collision directly. Here's why:

// prey cell will be eaten, size = 0
prey.handleCollision(predator); 

// predator won't have anything to eat.. prey is already marked as eaten
predator.handleCollision(prey); 

So instead of directly applying state-changes in the handleCollision method, you should store the result and apply it after all collisions have been resolved.

\$\endgroup\$
2
\$\begingroup\$

To test for the collision in 2D, I would recommend something like this :

struct AABB { int x, y, width, height; }

bool isColliding( AABB box1, AABB box2 )
{
    if( box1.x + box1.width < box2.x || box1.x > box2.x + box2.width ) return false;
    if( box1.y + box1.height < box2.y || box1.y > box2.y + box2.eight ) return false;

    return true;
}

Thus you can test the collision with small or big entities, and they can evolve in size and position.


You can also use a circle test collision using a circle center and a radius if circles are most adapted to the context of your game.

struct Circle { int x, y, radius; }

bool isColliding( Circle cir1, Circle cir2 )
{
    // Calculate the vector from cir1 center to cir2 center
    // then calculate the (non root-squared) distance using cross product to compare with radius
    int distX = cir2.x - cir1.x;
    int distY = cir2.y - cir1.y;
    int distanceCenters = distX * distX  + distY * distY;

    int radiusSum = cir1.radius + cir2.radius;
    return (distanceCenters < radiusSum*radiusSum );
}

The distance is not square-rooted as it should, because square-root operations are often expensive compared to products. Thus we don't use square-root on distanceCenters, but we use product with itself for radiusSum.


To avoid testing all entities over all others entities, I would split the "game area" in some smaller areas. Thus when you move the entities, you record in which "small" area of your game area they currently are (by adding a zone ID to the entity), and next you can test only the entities which are registered to be in the same "small" area (you need to test multiple area when an entity is at the edge of 2+ small areas).

Finally, as you need to know "where they are in relation to eachother all the time, so they know when/what to flee from/chase", I would recommend to test for the nearest cells of each types using small areas once again (and greater area if not found) and distance from center to center (as in the circle collision test).

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .