From b8c856bf5647d5df54dd8511e1a1febdd88cbf1c Mon Sep 17 00:00:00 2001 From: Sara Date: Sat, 24 Jun 2023 22:03:28 +0200 Subject: [PATCH] split the world module into world, physics, and object --- src/corelib/object.c | 16 +++ src/corelib/object.h | 29 ++++ src/corelib/physics.c | 269 +++++++++++++++++++++++++++++++++++++ src/corelib/physics.h | 41 ++++++ src/corelib/render.c | 13 ++ src/corelib/render.h | 2 + src/corelib/world.c | 300 +----------------------------------------- src/corelib/world.h | 55 +------- src/engine.c | 4 +- 9 files changed, 380 insertions(+), 349 deletions(-) create mode 100644 src/corelib/object.c create mode 100644 src/corelib/object.h create mode 100644 src/corelib/physics.c create mode 100644 src/corelib/physics.h diff --git a/src/corelib/object.c b/src/corelib/object.c new file mode 100644 index 0000000..5872c1e --- /dev/null +++ b/src/corelib/object.c @@ -0,0 +1,16 @@ +#include "object.h" + +object_t object_default() { + return (object_t){ + .active = 1, + .enabled = 1, + .collider = collider_default(), + .evt_draw = &object_draw_sprite, + .evt_update = NULL, + .sprite = sprite_default(), + }; +} + +void object_draw_sprite(object_t* object) { + draw_sprite(&object->sprite); +} diff --git a/src/corelib/object.h b/src/corelib/object.h new file mode 100644 index 0000000..2b8686a --- /dev/null +++ b/src/corelib/object.h @@ -0,0 +1,29 @@ +#ifndef _object_h +#define _object_h + +#include "render.h" +#include "physics.h" + +typedef struct object_t object_t; +typedef struct collider_t collider_t; + +typedef void(*tick_fn)(struct object_t*); +typedef void(*draw_fn)(struct object_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. + collider_t collider; // the collider to use for this object's physics interaction. + + uintptr_t timer; // free to use for whatever + + tick_fn evt_update; + draw_fn evt_draw; +}; + +object_t object_default(); + +void object_draw_sprite(object_t* object); + +#endif /* _object_h */ \ No newline at end of file diff --git a/src/corelib/physics.c b/src/corelib/physics.c new file mode 100644 index 0000000..2f6ad1c --- /dev/null +++ b/src/corelib/physics.c @@ -0,0 +1,269 @@ +#include "physics.h" +#include "object.h" +#include "world.h" +#include "math/vec.h" + +static inline +float fclampf(float x, float min_, float max_) { + return fminf(max_, fmaxf(min_, x)); +} + + +collider_t collider_default() { + return (collider_t) { + .type=COLLIDERTYPE_NONE + }; +} + +void object_broadcast_collision(object_t* this, object_t* other) { + if(this->collider.evt_collision != NULL) { + this->collider.evt_collision(this, other); + } +} +short can_collide(const object_t* this) { + return this->active && this->enabled; +} + +static inline +int _rect_overlap(float aminx, float aminy, float amaxx, float amaxy, float bminx, float bminy, float bmaxx, float bmaxy) { + return + ( + (aminx < bmaxx && aminx > bmaxx) + || + (amaxx > bminx && amaxx < bmaxx) + ) && ( + (aminy < bmaxy && aminy > bmaxy) + || + (amaxy > bminy && amaxy < bmaxy) + ); +} + +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, + 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 +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.y, 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 +float _solve_circle_aabb(const object_t* circle, const object_t* aabb, float* out_px, float* out_py) { + // 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.y, bbmaxy = bbminy + aabb->collider.aabb.h; + // the centre of the circle in world space + const float cx = circle->sprite.x + circle->collider.circle.x, + cy = circle->sprite.y + circle->collider.circle.y; + // the point on the rectangle closest to the centre of the circle + const float x = fclampf(cx, bbminx, bbmaxx), + y = fclampf(cy, bbminy, bbmaxy); + // the relative position of the point on the rectangle + const float dif_x = cx - x, + dif_y = cy - y; + // absolute difference for use in calculating euclidean distance + const float dist_x = fabsf(dif_x), + dist_y = fabsf(dif_y); + // euclidean distance + const float dist = sqrt(dist_x*dist_x + dist_y*dist_y); + const float solve_distance = circle->collider.circle.radius - dist; + // distance to solve collision + float solve_x, solve_y; + normalize(dif_x, dif_y, &solve_x, &solve_y); + *out_px = solve_x * solve_distance; + *out_py = solve_y * solve_distance; + return solve_distance; +} + +static inline +float _solve_circle_circle(const object_t* a, const object_t* b, float* out_px, float* out_py) { + const float x1 = a->collider.circle.x + a->sprite.x, y1 = a->collider.circle.y + a->sprite.y; + const float x2 = b->collider.circle.x + b->sprite.x, y2 = b->collider.circle.y + b->sprite.y; + const float dif_x = x1 - x2, dif_y = y1 - y2; + const float difference = sqrtf(fabsf(dif_x*dif_x) + fabsf(dif_y*dif_y)); + const float target_difference = a->collider.circle.radius + b->collider.circle.radius; + float dir_x, dir_y; + normalize(dif_x, dif_y, &dir_x, &dir_y); + *out_px = dir_x * target_difference; + *out_py = dir_y * target_difference; + return target_difference; +} + +static inline +float _solve_aabb_aabb(const object_t* a, const object_t* b, float* out_px, float* out_py) { + float right = (a->collider.aabb.x + a->collider.aabb.w + a->sprite.x) - (b->collider.aabb.x + b->sprite.x); + float left = (a->collider.aabb.x + a->sprite.x) - (b->collider.aabb.x + b->collider.aabb.w + b->sprite.x); + float top = (a->collider.aabb.y + a->sprite.y) - (b->collider.aabb.y + b->collider.aabb.w + b->sprite.y); + float bottom = (a->collider.aabb.y + a->collider.aabb.h) - (b->collider.aabb.y + b->sprite.y); + + float ret = right; + *out_px = right; + *out_py = 0.f; + if(fabsf(left) < fabsf(ret)) { + *out_px = left; + *out_py = 0.f; + ret = left; + } + if(fabsf(top) < fabsf(ret)) { + *out_px = 0.f; + *out_py = top; + ret = top; + } + + if(fabsf(bottom) < fabsf(ret)) { + *out_px = 0.f; + *out_py = bottom; + return bottom; + } + + return ret; +} + +float get_solve_force(const object_t* a, const object_t* b, float* out_px, float* out_py) { + if(a->collider.type == COLLIDERTYPE_AABB && b->collider.type == COLLIDERTYPE_AABB) { + return _solve_aabb_aabb(a, b, out_px, out_py); + } else if(a->collider.type == COLLIDERTYPE_AABB && b->collider.type == COLLIDERTYPE_CIRCLE) { + float penetration_distance = _solve_circle_aabb(b, a, out_px, out_py); + *out_px = -(*out_px); + *out_py = -(*out_py); + } else if(a->collider.type == COLLIDERTYPE_CIRCLE && b->collider.type == COLLIDERTYPE_AABB) { + return _solve_circle_aabb(a, b, out_px, out_py); + } else if(a->collider.type == COLLIDERTYPE_CIRCLE && b->collider.type == COLLIDERTYPE_CIRCLE) { + return _solve_circle_circle(a, b, out_px, out_py); + } +} + +static inline +void _solve_collision_slide(object_t* this, object_t* other, float old_x, float old_y, float new_x, float new_y) { + float dx, dy; + const float d = get_solve_force(this, other, &dx, &dy); + this->sprite.x += dx; + this->sprite.y += dy; + + return; + this->sprite.x = old_x; + this->sprite.y = new_y; + if(!_collision_check(other, this)) { + return; + } + + this->sprite.x = new_x; + this->sprite.y = old_y; + if(!_collision_check(other, this)) { + return; + } + + this->sprite.x = old_x; + this->sprite.y = old_y; + if(!_collision_check(other, this)) { + return; + } +} + +void interpolate_move(object_t* object, const float target_x, const float target_y, const float max_step_size, const int slide) { + // calculate step delta + float dx = target_x - object->sprite.x, dy = target_y - object->sprite.y; + if(dx == 0 && dy == 0) + return; + // calculate direction x,y + float m = sqrtf(dx*dx + dy*dy); + if(dx != 0) + dx /= m; + if(dy != 0) + dy /= m; + dx *= max_step_size; dy *= max_step_size; + int step_count = max_step_size / m; + + // 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; + } + + /* + * 1. move towards target + * 2. check collision with every other object + */ + for(int steps = 0; steps < step_count && (object->sprite.x != target_x || object->sprite.y != target_y); ++steps) { + // move towards target, snap to target if distance is too low + const float old_x = object->sprite.x, old_y = object->sprite.y; + float new_x, new_y; + + const float distx = fabsf(object->sprite.x - target_x), disty = fabsf(object->sprite.y - target_y); + const float sqdist = distx*distx + disty*disty; + if(sqdist > max_step_size) { + object->sprite.x += dx; + object->sprite.y += dy; + new_x = object->sprite.x; + new_y = object->sprite.y; + } else { + new_x = object->sprite.x = target_x; + new_y = object->sprite.y = target_y; + } + + // loop over all objects and check collision if applicable + for(int i = 0; i < WORLD_NUM_OBJECTS; ++i) { + // get pointer to other object + object_t* other = g_objects + i; + // check collision, return if found + if(can_collide(other) && object != other && _collision_check(other, object)) { + object_broadcast_collision(other, object); + object_broadcast_collision(object, other); + if(slide) { + _solve_collision_slide(object, other, old_x, old_y, new_x, new_y); + } else { + object->sprite.x = old_x; + object->sprite.y = old_y; + return; + } + } + } + } +} diff --git a/src/corelib/physics.h b/src/corelib/physics.h new file mode 100644 index 0000000..93e145c --- /dev/null +++ b/src/corelib/physics.h @@ -0,0 +1,41 @@ +#ifndef _physics_h +#define _physics_h + +#include "SDL2/SDL_rect.h" + +typedef struct object_t object_t; + +typedef void(*collided_fn)(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; + +collider_t collider_default(); + +void object_broadcast_evt_collision(object_t* this, object_t* other); + +void physics_update(); + +void interpolate_move(object_t* object, float target_x, float target_y, float max_step_size, int slide); + +extern short can_collide(const object_t* this); +extern float get_solve_force(const object_t* this, const object_t* other, float* solve_x, float* solve_y); + +#endif /* _physics_h */ diff --git a/src/corelib/render.c b/src/corelib/render.c index da717ee..678f409 100644 --- a/src/corelib/render.c +++ b/src/corelib/render.c @@ -253,6 +253,19 @@ void _exec_text_cmd(const drawcmd_t* cmd) { free(cmd->text.text); } +sprite_t sprite_default() { + return (sprite_t){ + .texture = NULL, + .x = 0.f, .y = 0.f, + .origin = (SDL_FPoint){0.f, 0.f}, + .sx = 1.f, .sy = 1.f, + .rot = 0.f, + .depth = RLAYER_SPRITES, + .uv = (SDL_Rect){0.f, 0.f, 0.f, 0.f}, + .flip = SDL_FLIP_NONE, + }; +} + sprite_t render_text(const char* str, SDL_FRect area, text_style_t style) { SDL_FRect r = area; int fh = TTF_FontHeight(style.font); diff --git a/src/corelib/render.h b/src/corelib/render.h index 8fba629..d2fc2e4 100644 --- a/src/corelib/render.h +++ b/src/corelib/render.h @@ -91,6 +91,8 @@ extern void screen_to_view(float* x, float* y); extern void clear_buffer(); extern void swap_buffer(); +extern sprite_t sprite_default(); + extern sprite_t render_text(const char* str, SDL_FRect area, text_style_t style); extern void draw_sprite(const sprite_t* sprite); extern void draw_rect(const rectshape_t* rect); diff --git a/src/corelib/world.c b/src/corelib/world.c index a46e6bc..341607e 100644 --- a/src/corelib/world.c +++ b/src/corelib/world.c @@ -10,10 +10,6 @@ void world_clear() { } } -void object_draw_sprite(object_t* object) { - draw_sprite(&object->sprite); -} - object_t* _find_free_object() { for(int i = 0; i < WORLD_NUM_OBJECTS; ++i) { if(g_objects[i].active == 0) { @@ -25,12 +21,7 @@ object_t* _find_free_object() { 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)); + *o = object_default(); return o; } @@ -41,13 +32,7 @@ object_t* instantiate_object(const object_t *original) { return obj; } -collider_t collider_default() { - return (collider_t) { - .type=COLLIDERTYPE_NONE - }; -} - -void update_objects() { +void world_update() { for(int i = 0; i < WORLD_NUM_OBJECTS; ++i) { if(g_objects[i].active == 1 && g_objects[i].enabled == 1 @@ -57,7 +42,7 @@ void update_objects() { } } -void draw_objects() { +void world_draw() { for(int i = 0; i < WORLD_NUM_OBJECTS; ++i) { if(g_objects[i].active == 1 && g_objects[i].enabled == 1 @@ -65,281 +50,4 @@ void draw_objects() { g_objects[i].evt_draw(g_objects + i); } } -} - -static inline -int _rect_overlap(float aminx, float aminy, float amaxx, float amaxy, float bminx, float bminy, float bmaxx, float bmaxy) { - return - ( - (aminx < bmaxx && aminx > bmaxx) - || - (amaxx > bminx && amaxx < bmaxx) - ) && ( - (aminy < bmaxy && aminy > bmaxy) - || - (amaxy > bminy && amaxy < bmaxy) - ); -} - -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, - 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 -float _circle_aabb_overlap(const object_t* circle, const object_t* aabb, float* out_px, float* out_py) { - // 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.y, bbmaxy = bbminy + aabb->collider.aabb.h; - // the centre of the circle in world space - const float cx = circle->sprite.x + circle->collider.circle.x, - cy = circle->sprite.y + circle->collider.circle.y; - // the point on the rectangle closest to the centre of the circle - const float x = fclampf(cx, bbminx, bbmaxx), - y = fclampf(cy, bbminy, bbmaxy); - // the relative position of the point on the rectangle - const float dif_x = cx - x, - dif_y = cy - y; - // absolute difference for use in calculating euclidean distance - const float dist_x = fabsf(dif_x), - dist_y = fabsf(dif_y); - // euclidean distance - const float dist = sqrt(dist_x*dist_x + dist_y*dist_y); - const float solve_distance = circle->collider.circle.radius - dist; - // distance to solve collision - float solve_x, solve_y; - normalize(dif_x, dif_y, &solve_x, &solve_y); - *out_px = solve_x * solve_distance; - *out_py = solve_y * solve_distance; - return solve_distance; -} - -static inline -float _circle_circle_overlap(const object_t* a, const object_t* b, float* out_px, float* out_py) { - const float x1 = a->collider.circle.x + a->sprite.x, y1 = a->collider.circle.y + a->sprite.y; - const float x2 = b->collider.circle.x + b->sprite.x, y2 = b->collider.circle.y + b->sprite.y; - const float dif_x = x1 - x2, dif_y = y1 - y2; - const float difference = sqrtf(fabsf(dif_x*dif_x) + fabsf(dif_y*dif_y)); - const float target_difference = a->collider.circle.radius + b->collider.circle.radius; - float dir_x, dir_y; - normalize(dif_x, dif_y, &dir_x, &dir_y); - *out_px = dir_x * target_difference; - *out_py = dir_y * target_difference; - return target_difference; -} - -static inline -float _aabb_aabb_overlap(const object_t* a, const object_t* b, float* out_px, float* out_py) { - float right = (a->collider.aabb.x + a->collider.aabb.w + a->sprite.x) - (b->collider.aabb.x + b->sprite.x); - float left = (a->collider.aabb.x + a->sprite.x) - (b->collider.aabb.x + b->collider.aabb.w + b->sprite.x); - float top = (a->collider.aabb.y + a->sprite.y) - (b->collider.aabb.y + b->collider.aabb.w + b->sprite.y); - float bottom = (a->collider.aabb.y + a->collider.aabb.h) - (b->collider.aabb.y + b->sprite.y); - - float ret = right; - *out_px = right; - *out_py = 0.f; - if(fabsf(left) < fabsf(ret)) { - *out_px = left; - *out_py = 0.f; - ret = left; - } - if(fabsf(top) < fabsf(ret)) { - *out_px = 0.f; - *out_py = top; - ret = top; - } - - if(fabsf(bottom) < fabsf(ret)) { - *out_px = 0.f; - *out_py = bottom; - return bottom; - } - - return ret; -} - -static inline -float _get_overlap(const object_t* a, const object_t* b, float* out_px, float* out_py) { - if(a->collider.type == COLLIDERTYPE_AABB && b->collider.type == COLLIDERTYPE_AABB) { - return _aabb_aabb_overlap(a, b, out_px, out_py); - } else if(a->collider.type == COLLIDERTYPE_AABB && b->collider.type == COLLIDERTYPE_CIRCLE) { - float penetration_distance = _circle_aabb_overlap(b, a, out_px, out_py); - *out_px = -(*out_px); - *out_py = -(*out_py); - } else if(a->collider.type == COLLIDERTYPE_CIRCLE && b->collider.type == COLLIDERTYPE_AABB) { - return _circle_aabb_overlap(a, b, out_px, out_py); - } else if(a->collider.type == COLLIDERTYPE_CIRCLE && b->collider.type == COLLIDERTYPE_CIRCLE) { - return _circle_circle_overlap(a, b, out_px, out_py); - } -} - -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.y, 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; -} - -void object_broadcast_collision(object_t* this, object_t* other) { - if(this->collider.evt_collision != NULL) { - this->collider.evt_collision(this, other); - } -} - -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; ++inner) { - object_t* iobject = g_objects + inner; - if(!_can_collide(oobject)) continue; - - if(outer != inner && _collision_check(iobject, oobject)) { - object_broadcast_collision(oobject, iobject); - object_broadcast_collision(iobject, oobject); - } - } - } -} - -static inline -void _slide_collision(object_t* this, object_t* other, float old_x, float old_y, float new_x, float new_y) { - float dx, dy; - const float d = _get_overlap(this, other, &dx, &dy); - this->sprite.x += dx; - this->sprite.y += dy; - - return; - this->sprite.x = old_x; - this->sprite.y = new_y; - if(!_collision_check(other, this)) { - return; - } - - this->sprite.x = new_x; - this->sprite.y = old_y; - if(!_collision_check(other, this)) { - return; - } - - this->sprite.x = old_x; - this->sprite.y = old_y; - if(!_collision_check(other, this)) { - return; - } -} - -void interpolate_move(object_t* object, const float target_x, const float target_y, const float max_step_size, const int slide) { - // calculate step delta - float dx = target_x - object->sprite.x, dy = target_y - object->sprite.y; - if(dx == 0 && dy == 0) - return; - // calculate direction x,y - float m = sqrtf(dx*dx + dy*dy); - if(dx != 0) - dx /= m; - if(dy != 0) - dy /= m; - dx *= max_step_size; dy *= max_step_size; - int step_count = max_step_size / m; - - // 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; - } - - /* - * 1. move towards target - * 2. check collision with every other object - */ - for(int steps = 0; steps < step_count && (object->sprite.x != target_x || object->sprite.y != target_y); ++steps) { - // move towards target, snap to target if distance is too low - const float old_x = object->sprite.x, old_y = object->sprite.y; - float new_x, new_y; - - const float distx = fabsf(object->sprite.x - target_x), disty = fabsf(object->sprite.y - target_y); - const float sqdist = distx*distx + disty*disty; - if(sqdist > max_step_size) { - object->sprite.x += dx; - object->sprite.y += dy; - new_x = object->sprite.x; - new_y = object->sprite.y; - } else { - new_x = object->sprite.x = target_x; - new_y = object->sprite.y = target_y; - } - - // loop over all objects and check collision if applicable - for(int i = 0; i < WORLD_NUM_OBJECTS; ++i) { - // get pointer to other object - object_t* other = g_objects + i; - // check collision, return if found - if(_can_collide(other) && object != other && _collision_check(other, object)) { - object_broadcast_collision(other, object); - object_broadcast_collision(object, other); - if(slide) { - _slide_collision(object, other, old_x, old_y, new_x, new_y); - } else { - object->sprite.x = old_x; - object->sprite.y = old_y; - return; - } - } - } - } -} +} \ No newline at end of file diff --git a/src/corelib/world.h b/src/corelib/world.h index 27de091..c86124a 100644 --- a/src/corelib/world.h +++ b/src/corelib/world.h @@ -1,67 +1,20 @@ #ifndef _world_h #define _world_h -#include "render.h" -#include +#include "object.h" #define WORLD_NUM_OBJECTS 255 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. - collider_t collider; // the collider to use for this object's physics interaction. - - uintptr_t timer; // free to use for whatever - - tick_fn evt_update; - draw_fn evt_draw; -}; - 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 world_clear(); -void object_draw_sprite(object_t* object); -void object_broadcast_evt_collision(object_t* this, object_t* other); - -void update_objects(); -void draw_objects(); - -void update_collision(); - -void interpolate_move(object_t* object, float target_x, float target_y, float max_step_size, int slide); +void world_update(); +void world_draw(); #endif /* _world_h */ diff --git a/src/engine.c b/src/engine.c index 8a5d514..a1c857f 100644 --- a/src/engine.c +++ b/src/engine.c @@ -82,8 +82,8 @@ int _engine_run() { update_ui(); _render_mode = 0; update_game(); - update_objects(); // update world objects - draw_objects(); // draw world objects + world_update(); // update world objects + world_draw(); // draw world objects swap_buffer(); do { timespec_get(&next_time, TIME_UTC);