samples / collisions.c¶
This sample demonstrates a method for pixel-perfect collision detection. While this may not be the most optimized way to do this, it is clear and effective.
#include "cage.h"
Sample setup¶
For this demo we will use a star image. Each star will have its own position, motion vector and bounding box.
#define MAX_STARS 12
struct star {
vec star_pos;
vec star_vec;
bbox star_bbox;
};
struct state {
struct image* star_img;
struct star stars[MAX_STARS];
};
Web begin by creating, positioning and propelling the stars.
static void* create_sample(void)
{
int i;
struct state* state = malloc(sizeof(struct state));
state->star_img = create_image("res/star.png");
for (i = 0; i < MAX_STARS; i++) {
state->stars[i].star_pos = xy_vec(i * 16, i * 16);
state->stars[i].star_vec = xy_vec(2 - rand() % 4, 2 - rand() % 4);
state->stars[i].star_bbox.p1 = state->stars[i].star_pos;
state->stars[i].star_bbox.p2 =
add_vec(state->stars[i].star_pos, xy_vec(16, 16));
}
return state;
}
Moving the stars¶
update_star() moves a star on the screen using the star motion vector and tests for screen bounds collisions.
static void update_star(struct star* star, float elapsed_ms)
{
bbox screen_bbox = { { 0, 0 }, { 192, 108 } };
star->star_pos = add_vec(star->star_pos, star->star_vec);
star->star_bbox = translate_bbox(star->star_bbox, star->star_pos);
if (bbox_in_bbox(star->star_bbox, screen_bbox) == 0) {
if (star->star_bbox.p1.x < screen_bbox.p1.x) star->star_vec.x = 1;
if (star->star_bbox.p1.y < screen_bbox.p1.y) star->star_vec.y = 1;
if (star->star_bbox.p2.x > screen_bbox.p2.x) star->star_vec.x = -1;
if (star->star_bbox.p2.y > screen_bbox.p2.y) star->star_vec.y = -1;
}
UNUSED(elapsed_ms);
}
Collision detection¶
When updating a frame, we traverse the star pairs graph and test for a bounding box intersection. To detect pixel-level collisions, we us pixels_collide() using the portion of intersection as a test area. If we detect a collision, we swap the star pair motion vectors to create a deflection effect.
When done, we update and draw the stars.
static void update_sample(void* data, float elapsed_ms)
{
struct state* state = data;
int visited[MAX_STARS][MAX_STARS] = { { 0 } };
int i, j;
for (i = 0; i < MAX_STARS; i++) {
for (j = 0; j < MAX_STARS; j++) {
if (i != j && visited[i][j] == 0 && visited[j][i] == 0) {
bbox sub;
struct star* a = &state->stars[i];
struct star* b = &state->stars[j];
if (bbox_intersect(a->star_bbox, b->star_bbox, &sub)) {
struct rectangle r1, r2;
r1 = rect_from_sub_bbox(a->star_bbox, sub);
r2 = rect_from_sub_bbox(b->star_bbox, sub);
if (pixels_collide(state->star_img, &r1, state->star_img,
&r2))
swap_vecs(&a->star_vec, &b->star_vec);
}
visited[i][j] = 1;
}
}
}
screen_color(color_from_RGB(10, 20, 50));
for (i = 0; i < MAX_STARS; i++) {
update_star(&state->stars[i], elapsed_ms);
draw_image(state->star_img, VEC_XY(state->stars[i].star_pos), NULL, 0);
}
}
Cleanup¶
Clean up is simple enough. Just destroy the star image and free the state structure memory.
static void destroy_sample(void* data)
{
struct state* state = data;
destroy_image(state->star_img);
free(data);
}
Main¶
The usual game_loop call, using the usual state functions.
int main(void)
{
return game_loop(create_sample, update_sample, destroy_sample);
}