implemented simple aabb and circle collision

pull/5/head
Sara 2023-05-24 21:31:50 +02:00
parent 42ee0c4a26
commit 6c16e8cdbd
2 changed files with 125 additions and 2 deletions

View File

@ -26,6 +26,7 @@ object_t* make_object() {
object_t* o = _find_free_object(); object_t* o = _find_free_object();
o->active = 1; o->active = 1;
o->enabled = 1; o->enabled = 1;
o->collider = collider_default();
o->evt_draw = &object_draw_sprite; o->evt_draw = &object_draw_sprite;
o->evt_update = NULL; o->evt_update = NULL;
memset(&o->sprite, 0, sizeof(sprite_t)); memset(&o->sprite, 0, sizeof(sprite_t));
@ -39,6 +40,12 @@ object_t* instantiate_object(const object_t *original) {
return obj; return obj;
} }
collider_t collider_default() {
return (collider_t) {
.type=COLLIDERTYPE_NONE
};
}
void update_objects() { void update_objects() {
for(int i = 0; i < WORLD_NUM_OBJECTS; ++i) { for(int i = 0; i < WORLD_NUM_OBJECTS; ++i) {
if(g_objects[i].active == 1 if(g_objects[i].active == 1
@ -58,3 +65,92 @@ void draw_objects() {
} }
} }
} }
static inline
short _collision_aabb_aabb(const object_t* a, const object_t* b) {
const float aminx = a->collider.aabb.x + a->sprite.x, aminy = a->collider.aabb.y + a->sprite.x;
const float amaxx = aminx + a->collider.aabb.w, amaxy = aminy + a->collider.aabb.h;
const float bminx = b->collider.aabb.x, bminy = b->collider.aabb.y;
const float bmaxx = b->collider.aabb.x + b->collider.aabb.w, bmaxy = b->collider.aabb.y + b->collider.aabb.h;
return
(
(aminx < bmaxx && aminx > bmaxx)
||
(amaxx > bminx && amaxx < bmaxx)
) && (
(aminy < bmaxy && aminy > bmaxy)
||
(amaxy > bminy && amaxy < bmaxy)
);
}
static inline
short _collision_circle_circle(const object_t* a, const object_t* b) {
const float ax = a->sprite.x + a->collider.circle.x, ay = a->sprite.y + a->collider.circle.y,
bx = b->sprite.x + b->collider.circle.x, by = b->sprite.y + b->collider.circle.y;
const float dx = fabsf(ax-bx), dy = fabsf(ay-by);
const float sqrdist = dx*dx+dy*dy;
const float mindist = a->collider.circle.radius + b->collider.circle.radius;
const float mindistsqr = mindist*mindist;
return sqrdist < mindistsqr;
}
static inline
float fclampf(float x, float min_, float max_) {
return fminf(max_, fmaxf(min_, x));
}
static inline
short _collision_circle_aabb(const object_t* circle, const object_t* aabb) {
// generate a point on the edge of the rectangle that is closest to the circle
const float bbminx = aabb->collider.aabb.x + aabb->sprite.x, bbmaxx = bbminx + aabb->collider.aabb.w,
bbminy = aabb->collider.aabb.y + aabb->sprite.x, bbmaxy = bbminy + aabb->collider.aabb.h;
const float cx = circle->sprite.x + circle->collider.circle.x,
cy = circle->sprite.y + circle->collider.circle.y;
const float x = fclampf(cx, bbminx, bbmaxx),
y = fclampf(cy, bbminy, bbmaxy);
const float dx = fabsf(cx - x), dy = fabsf(cy - y);
// calculate the square distance from the centre of the circle to the edge of the aabb
const float distsqr = dx*dx+dy*dy;
const float rsqr = circle->collider.circle.radius*circle->collider.circle.radius;
// return if the square distance is larger than the square of the radius
return distsqr < rsqr;
}
static inline
short _collision_check(const object_t* a, const object_t* b) {
if(a->collider.type == COLLIDERTYPE_AABB && b->collider.type == COLLIDERTYPE_AABB) {
return _collision_aabb_aabb(a, b);
} else if(a->collider.type == COLLIDERTYPE_CIRCLE && b->collider.type == COLLIDERTYPE_CIRCLE) {
return _collision_circle_circle(a, b);
} else if(a->collider.type == COLLIDERTYPE_CIRCLE && b->collider.type == COLLIDERTYPE_AABB) {
return _collision_circle_aabb(a, b);
} else if(a->collider.type == COLLIDERTYPE_AABB && b->collider.type == COLLIDERTYPE_CIRCLE) {
return _collision_circle_aabb(b, a);
}
return 0;
}
static inline
short _can_collide(const object_t* object) {
return object->active && object->enabled && object->collider.evt_collision != NULL;
}
void update_collision() {
for(int outer = 0; outer < WORLD_NUM_OBJECTS; ++outer) {
object_t* oobject = g_objects + outer;
if(!_can_collide(oobject)) continue;
for(int inner = 0; inner < WORLD_NUM_OBJECTS/2; ++inner) {
object_t* iobject = g_objects + inner;
if(!_can_collide(oobject)) continue;
if(outer != inner && _collision_check(iobject, oobject)) {
oobject->collider.evt_collision(oobject, iobject);
iobject->collider.evt_collision(iobject, oobject);
}
}
}
}

View File

@ -10,11 +10,33 @@ typedef struct object_t object_t;
typedef void(*tick_fn)(struct object_t*); typedef void(*tick_fn)(struct object_t*);
typedef void(*draw_fn)(struct object_t*); typedef void(*draw_fn)(struct object_t*);
typedef void(*collided_fn)(struct object_t*, struct object_t*);
typedef enum collider_type_t {
COLLIDERTYPE_MIN,
COLLIDERTYPE_NONE,
COLLIDERTYPE_CIRCLE,
COLLIDERTYPE_AABB,
COLLIDERTYPE_MAX,
} collider_type_t;
typedef struct collider_t {
collider_type_t type;
collided_fn evt_collision;
union {
struct {
float x, y;
float radius;
} circle;
SDL_FRect aabb;
};
} collider_t;
struct object_t { struct object_t {
sprite_t sprite; sprite_t sprite;
int active; // 1 if this object is in use and should not be overriden int active; // 1 if this object is in use and should not be overriden.
int enabled; // 1 if this object's events should be triggered int enabled; // 1 if this object's events should be triggered.
collider_t collider; // the collider to use for this object's physics interaction.
uintptr_t timer; // free to use for whatever uintptr_t timer; // free to use for whatever
@ -26,12 +48,17 @@ extern object_t g_objects[WORLD_NUM_OBJECTS];
void world_clear(); void world_clear();
object_t* make_object(); object_t* make_object();
object_t* instantiate_object(const object_t* original); object_t* instantiate_object(const object_t* original);
collider_t collider_default();
void object_draw_sprite(object_t* object); void object_draw_sprite(object_t* object);
void update_objects(); void update_objects();
void draw_objects(); void draw_objects();
void update_collision();
#endif /* _world_h */ #endif /* _world_h */