Technology Temerity

Code Snip – Collision Checking

Notes from 2016-11-26 – Revamping OpenBOR collision detection.

With the possibility of several dozen or more entities on screen, collision detection must be precise with minimal resource intensity.
With the possibility of several dozen or more entities on screen, collision detection must be precise with minimal resource intensity.

Currently coordinates (s_hitbox) exist as static sub-structures in s_collision_attack and s_collision_body. See below…

typedef struct
{
	int x;
	int y;
	int width;
	int height;
	int z1;
	int z2;
} s_hitbox;

// s_collision_attack
typedef struct
{
    int                 attack_drop;        // now be a knock-down factor, how many this attack will knock victim down
    int                 attack_force;
    int                 attack_type;        // Reaction animation, death, etc.
    int                 blast;              // Attack box active on hit opponent's fall animation.
    int                 blockflash;         // Custom bflash for each animation, model id
    int                 blocksound;         // Custom sound for when an attack is blocked
    s_hitbox            coords;
    int                 counterattack;      // Treat other attack boxes as body box.
    ...

This was done for simplicity, and with current logic wastes no memory as coordinates are always required for a collision box.

However, the addition of multiple collision box support has exposed the need to break collision detection down into smaller functions. This in turn requires a lot of passing around the entire s_hitbox structure. Given the rate this functionality is used (multiple collision evaluations on every entity on every animation frame @200 frames per second), efficiency is absolutely imperative. Replacing the static coords declaration with a pointer and using dynamic allocation will add some code complexity initially, but in the long term should simplify breaking down collision logic and save substantial resources.

The following are in progress logic functions, they will need reworking to accommodate new pointer.

// Caskey, Damon V.
// 2016-11-25
//
// Get 2D size and position of collision box.
s_coords_box_2D collision_final_coords_2D(entity *entity, s_hitbox coords)
{
    s_hitbox        temp;
    s_coords_box_2D result;

    temp.z1 = 0;

    // If Z coords are reversed, let's correct them.
    // Otherwise we use
    if(coords.z2 > coords.z1)
    {
        temp.z1 = coords.z1 + (coords.z2 - coords.z1) / 2;
    }

    // Get entity positions with Z offset
    // included, and cast to integer.
    temp.x    = (int)(entity->position.x);
    temp.y    = (int)(temp.z1 - entity->position.y);

    // Use temporary positions to get final dimensions
    // for collision boxes.
    if(entity->direction == DIRECTION_LEFT)
    {
        result.position.x   = temp.x - coords.width;
        result.size.x       = temp.x - coords.x;
    }
    else
    {
        result.position.x   = temp.x + coords.x;
        result.size.x       = temp.x + coords.width;
    }
    result.position.y   = temp.y + coords.y;
    result.size.y       = temp.y + coords_owner.height;

    return result;
}

bool collision_check_contact_2D(s_coords_box_2D owner, s_coords_box_2D target)
{
    // Compare the calculated boxes. If any one check
    // fails, then the boxes are not in contact.
    if(owner.position.x > target.size.x)
    {
        return FALSE;
    }
    if(target.position.x > target.size.x)
    {
        return FALSE;
    }
    if(owner.position.y > target.size.y)
    {
        return FALSE;
    }
    if(target.position.y > target.size.y)
    {
        return FALSE;
    }
}

bool collision_check_contact_Z(entity *owner, s_hitbox coords_owner, s_hitbox coords_target)
{
    int Z_distance = 0;
    int z1 = 0;
    int z2 = 0;

    if(coords_owner.z2 > coords_owner.z1)
    {
        z1 += coords_owner.z1 + (coords_owner.z2 - coords_owner.z1) / 2;
        zdist = (coords_owner.z2 - coords_owner.z1) / 2;
    }
    else if(coords_owner.z1)
    {
        zdist += coords_owner.z1;
    }
    else
    {
        zdist += attacker->modeldata.grabdistance / 3 + 1;    //temporay fix for integer to float conversion
    }

    if(coords_target.z2 > coords_target.z1)
    {
        z2 += coords_target.z1 + (coords_target.z2 - coords_target.z1) / 2;
        zdist += (coords_target.z2 - coords_target.z1) / 2;
    }
    else if(coords_target.z1)
    {
        zdist += coords_target.z1;
    }

    zdist++; // pass >= <= check if(diff(z1, z2) > zdist)
    {
        return FALSE;
    }
    
    return TRUE; 
}

// Caskey, Damon V.
// 2016-11-25
//
// Compare collision boxes and return
// TRUE if they are in contact.
bool checkhit_collision(entity *owner, entity *target, s_hitbox coords_owner, s_hitbox coords_target)
{
    s_coords_box_2D owner_final;
    s_coords_box_2D target_final;

    bool result;
    
    // First check Z contact.
    result = collision_check_contact_Z(owner, coords_owner, coords_target);    

    // If result is TRUE, then run
    // 2D plane checks.
    if(result)
    {
        // Get final collision box 2D plane sizes.
        owner_final     = collision_final_coords_2D(owner, coords_owner);
        target_final    = collision_final_coords_2D(target, coords_target);
        
        // Compare the 2D boxes and get result.
        result = collision_check_contact_2D(owner_final, target_final);
    }
    
    // return final result.
    return result;
}

// Find center of attack area
s_axis_f_2d collision_center()
{

    leftleast = attack_pos_x;

    if(leftleast < detect_pos_x) { leftleast = detect_pos_x; } rightleast = attack_size_x; if(rightleast > detect_size_x)
    {
        rightleast = detect_size_x;
    }

    medx = (float)(leftleast + rightleast) / 2;
}

Author: Damon Caskey

Hello all, Damon Caskey here - the esteemed owner of this little slice of cyberspace. Welcome!

Leave a Reply