From 6c16e8cdbd2bc6fa68647b852bf514f5599f0f6f Mon Sep 17 00:00:00 2001 From: Sara Date: Wed, 24 May 2023 21:31:50 +0200 Subject: [PATCH 1/6] implemented simple aabb and circle collision --- src/corelib/world.c | 96 +++++++++++++++++++++++++++++++++++++++++++++ src/corelib/world.h | 31 ++++++++++++++- 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/src/corelib/world.c b/src/corelib/world.c index 6f62c91..11a26f6 100644 --- a/src/corelib/world.c +++ b/src/corelib/world.c @@ -26,6 +26,7 @@ object_t* make_object() { object_t* o = _find_free_object(); o->active = 1; o->enabled = 1; + o->collider = collider_default(); o->evt_draw = &object_draw_sprite; o->evt_update = NULL; memset(&o->sprite, 0, sizeof(sprite_t)); @@ -39,6 +40,12 @@ object_t* instantiate_object(const object_t *original) { return obj; } +collider_t collider_default() { + return (collider_t) { + .type=COLLIDERTYPE_NONE + }; +} + void update_objects() { for(int i = 0; i < WORLD_NUM_OBJECTS; ++i) { 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); + } + } + } +} diff --git a/src/corelib/world.h b/src/corelib/world.h index 2fd381d..884b23f 100644 --- a/src/corelib/world.h +++ b/src/corelib/world.h @@ -10,11 +10,33 @@ typedef struct object_t object_t; typedef void(*tick_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 { sprite_t sprite; - 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 active; // 1 if this object is in use and should not be overriden. + 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 @@ -26,12 +48,17 @@ extern object_t g_objects[WORLD_NUM_OBJECTS]; void world_clear(); + object_t* make_object(); object_t* instantiate_object(const object_t* original); +collider_t collider_default(); + void object_draw_sprite(object_t* object); void update_objects(); void draw_objects(); +void update_collision(); + #endif /* _world_h */ From 17eef0eb2049561739b5f81c78f43f3c359925b6 Mon Sep 17 00:00:00 2001 From: Sara Date: Sun, 28 May 2023 23:59:24 +0200 Subject: [PATCH 2/6] added interpolate move --- src/corelib/world.c | 64 ++++++++++++++++++++++++++++++++++++++++----- src/corelib/world.h | 2 ++ 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/corelib/world.c b/src/corelib/world.c index 11a26f6..ce41dea 100644 --- a/src/corelib/world.c +++ b/src/corelib/world.c @@ -67,12 +67,7 @@ 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; - +int _rect_overlap(float aminx, float aminy, float amaxx, float amaxy, float bminx, float bminy, float bmaxx, float bmaxy) { return ( (aminx < bmaxx && aminx > bmaxx) @@ -85,6 +80,16 @@ short _collision_aabb_aabb(const object_t* a, const object_t* b) { ); } +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 _rect_overlap(aminx, aminy, amaxx, amaxy, bminx, bminy, bmaxx, 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, @@ -143,7 +148,7 @@ 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) { + for(int inner = 0; inner < WORLD_NUM_OBJECTS; ++inner) { object_t* iobject = g_objects + inner; if(!_can_collide(oobject)) continue; @@ -154,3 +159,48 @@ void update_collision() { } } } + +object_t* interpolate_move(object_t* object, float target_x, float target_y, float max_step_size) { + // calculate step delta + float dx = target_x - object->sprite.x, dy = target_y - object->sprite.y; + // calculate direction x,y + float m = sqrtf(dx*dx + dy*dy); + dx /= m; dy /= m; + dx *= max_step_size; dy *= max_step_size; + + // ensure this object would ever collide + // if it wouldn't collide anyway, just set position + if(_can_collide(object)) { + object->sprite.x = target_x; + object->sprite.y = target_y; + return NULL; + } + + /* + * 1. check collision with every other object + * 2. move towards target + */ + while(object->sprite.x != target_x || object->sprite.y != target_y) { + for(int i = 0; i < WORLD_NUM_OBJECTS; ++i) { + object_t* other = g_objects + i; + if(!_can_collide(other)) continue; + if(object != other && _collision_check(other, object)) { + other->collider.evt_collision(other, object); + object->collider.evt_collision(object, other); + return other; + } + } + + // move towards target, snap to target if distance is too low + float distx = fabsf(object->sprite.x - target_x), disty = fabsf(object->sprite.y - target_y); + if(distx < fabsf(dx) && disty < fabsf(dy)) { + object->sprite.x += dx; object->sprite.y += dy; + } else { + object->sprite.x = target_x; + object->sprite.y = target_y; + } + } + + // no collision, return nothing + return NULL; +} diff --git a/src/corelib/world.h b/src/corelib/world.h index 884b23f..d82287a 100644 --- a/src/corelib/world.h +++ b/src/corelib/world.h @@ -61,4 +61,6 @@ void draw_objects(); void update_collision(); +object_t* interpolate_move(object_t* object, float target_x, float target_y, float max_step_size); + #endif /* _world_h */ From eb39cb1432f4bcbd2b131cf92451200d551c17d7 Mon Sep 17 00:00:00 2001 From: Sara Date: Mon, 5 Jun 2023 18:11:31 +0200 Subject: [PATCH 3/6] added .nvimrc to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 717648a..988cb60 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# ide files +.nvimrc + # build dirs bin/ release/ From a0cc95873e0e10a7448870fb75e0469d851d1696 Mon Sep 17 00:00:00 2001 From: Sara Date: Wed, 14 Jun 2023 18:50:00 +0200 Subject: [PATCH 4/6] added .helix dir to ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 988cb60..7f17917 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ compile_commands.json # clangd cache .cache/ +.helix From 0a6242ef3cba2623ef54734e9ed53086c767a664 Mon Sep 17 00:00:00 2001 From: Sara Date: Wed, 14 Jun 2023 18:50:55 +0200 Subject: [PATCH 5/6] sprite origin is now applied to untransformed rect --- src/corelib/render.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/corelib/render.c b/src/corelib/render.c index 7eb4ded..abee531 100644 --- a/src/corelib/render.c +++ b/src/corelib/render.c @@ -80,10 +80,10 @@ static void _exec_sprite_cmd(const drawcmd_t* cmd) { const sprite_t* sprite = &cmd->sprite; SDL_FRect untransformed = {sprite->x, sprite->y, sprite->sx, sprite->sy}; + untransformed.x -= sprite->origin.x; + untransformed.y -= sprite->origin.y; SDL_FRect destrect = get_dest_with_size(untransformed, cmd->ui); SDL_FPoint origin = {destrect.w * sprite->origin.x, destrect.h * sprite->origin.y}; - destrect.x -= origin.x; - destrect.y -= origin.y; SDL_RenderCopyExF(g_context.renderer, sprite->texture, &sprite->uv, &destrect, sprite->rot, &origin, sprite->flip); From d067ea6df852e2f86d4a48d2e254e278b87d7a0e Mon Sep 17 00:00:00 2001 From: Sara Date: Fri, 16 Jun 2023 13:35:33 +0200 Subject: [PATCH 6/6] default sprite origin in is now 0,0 --- src/corelib/render.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/corelib/render.c b/src/corelib/render.c index abee531..da717ee 100644 --- a/src/corelib/render.c +++ b/src/corelib/render.c @@ -443,7 +443,7 @@ sprite_t make_sprite(const char* file, float x, float y) { sprite_t sprite=(sprite_t){ .texture=get_texture(file), .x=x,.y=y, - .origin=(SDL_FPoint){.x=0,.y=0}, + .origin=(SDL_FPoint){.x=0.0,.y=0.0}, .sx=1.0,.sy=1.0, .rot=0, .depth=RLAYER_SPRITES, @@ -451,7 +451,6 @@ sprite_t make_sprite(const char* file, float x, float y) { .flip=SDL_FLIP_NONE, }; SDL_QueryTexture(sprite.texture, NULL, NULL, &sprite.uv.w, &sprite.uv.h); - sprite.origin.x = -(float)sprite.uv.h/2.f; sprite.origin.y = -(float)sprite.uv.h/2.f; return sprite; }