diff --git a/CMakeLists.txt b/CMakeLists.txt index 330f641..4d9b887 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,14 +17,17 @@ find_package(SDL2) find_package(SDL2_image) find_package(SDL2_ttf) -file(GLOB_RECURSE +file( + GLOB_RECURSE SOURCE_C "${CMAKE_SOURCE_DIR}/src/**.c") -file(GLOB_RECURSE +file( + GLOB_RECURSE SOURCE_CPP "${CMAKE_SOURCE_DIR}/src/**.c*") -add_executable(${PROJECT} +add_executable( + ${PROJECT} ${SOURCE_C} ${SOURCE_CPP}) diff --git a/src/corelib/object.c b/src/corelib/object.c index 5872c1e..5664fbc 100644 --- a/src/corelib/object.c +++ b/src/corelib/object.c @@ -4,7 +4,7 @@ object_t object_default() { return (object_t){ .active = 1, .enabled = 1, - .collider = collider_default(), + .physics = physics_default(), .evt_draw = &object_draw_sprite, .evt_update = NULL, .sprite = sprite_default(), diff --git a/src/corelib/object.h b/src/corelib/object.h index 2b8686a..9d26ab9 100644 --- a/src/corelib/object.h +++ b/src/corelib/object.h @@ -5,7 +5,6 @@ #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*); @@ -14,7 +13,7 @@ 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. + physics_t physics; // the collider to use for this object's physics interaction. uintptr_t timer; // free to use for whatever diff --git a/src/corelib/physics.c b/src/corelib/physics.c index 64df3ef..001348c 100644 --- a/src/corelib/physics.c +++ b/src/corelib/physics.c @@ -9,15 +9,18 @@ float fclampf(float x, float min_, float max_) { } -collider_t collider_default() { - return (collider_t) { - .type=COLLIDERTYPE_NONE +physics_t physics_default() { + return (physics_t) { + .type=COLLIDERTYPE_NONE, + .velocity_x = 0.f, + .velocity_y = 0.f, + .solver = &solve_collision_slide }; } void object_broadcast_collision(object_t* this, object_t* other) { - if(this->collider.evt_collision != NULL) { - this->collider.evt_collision(this, other); + if(this->physics.evt_collision != NULL) { + this->physics.evt_collision(this, other); } } short can_collide(const object_t* this) { @@ -40,21 +43,21 @@ int _rect_overlap(float aminx, float aminy, float amaxx, float amaxy, float bmin 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; + const float aminx = a->physics.aabb.x + a->sprite.x, aminy = a->physics.aabb.y + a->sprite.x; + const float amaxx = aminx + a->physics.aabb.w, amaxy = aminy + a->physics.aabb.h; + const float bminx = b->physics.aabb.x, bminy = b->physics.aabb.y; + const float bmaxx = b->physics.aabb.x + b->physics.aabb.w, bmaxy = b->physics.aabb.y + b->physics.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 ax = a->sprite.x + a->physics.circle.x, ay = a->sprite.y + a->physics.circle.y, + bx = b->sprite.x + b->physics.circle.x, by = b->sprite.y + b->physics.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 mindist = a->physics.circle.radius + b->physics.circle.radius; const float mindistsqr = mindist*mindist; return sqrdist < mindistsqr; } @@ -62,17 +65,17 @@ short _collision_circle_circle(const object_t* a, const object_t* b) { 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 bbminx = aabb->physics.aabb.x + aabb->sprite.x, bbmaxx = bbminx + aabb->physics.aabb.w, + bbminy = aabb->physics.aabb.y + aabb->sprite.y, bbmaxy = bbminy + aabb->physics.aabb.h; + const float cx = circle->sprite.x + circle->physics.circle.x, + cy = circle->sprite.y + circle->physics.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; + const float rsqr = circle->physics.circle.radius*circle->physics.circle.radius; // return if the square distance is larger than the square of the radius return distsqr < rsqr; @@ -80,13 +83,13 @@ short _collision_circle_aabb(const object_t* circle, const object_t* aabb) { static inline short _collision_check(const object_t* a, const object_t* b) { - if(a->collider.type == COLLIDERTYPE_AABB && b->collider.type == COLLIDERTYPE_AABB) { + if(a->physics.type == COLLIDERTYPE_AABB && b->physics.type == COLLIDERTYPE_AABB) { return _collision_aabb_aabb(a, b); - } else if(a->collider.type == COLLIDERTYPE_CIRCLE && b->collider.type == COLLIDERTYPE_CIRCLE) { + } else if(a->physics.type == COLLIDERTYPE_CIRCLE && b->physics.type == COLLIDERTYPE_CIRCLE) { return _collision_circle_circle(a, b); - } else if(a->collider.type == COLLIDERTYPE_CIRCLE && b->collider.type == COLLIDERTYPE_AABB) { + } else if(a->physics.type == COLLIDERTYPE_CIRCLE && b->physics.type == COLLIDERTYPE_AABB) { return _collision_circle_aabb(a, b); - } else if(a->collider.type == COLLIDERTYPE_AABB && b->collider.type == COLLIDERTYPE_CIRCLE) { + } else if(a->physics.type == COLLIDERTYPE_AABB && b->physics.type == COLLIDERTYPE_CIRCLE) { return _collision_circle_aabb(b, a); } return 0; @@ -95,11 +98,11 @@ short _collision_check(const object_t* a, const object_t* b) { 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; + const float bbminx = aabb->physics.aabb.x + aabb->sprite.x, bbmaxx = bbminx + aabb->physics.aabb.w, + bbminy = aabb->physics.aabb.y + aabb->sprite.y, bbmaxy = bbminy + aabb->physics.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; + const float cx = circle->sprite.x + circle->physics.circle.x, + cy = circle->sprite.y + circle->physics.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); @@ -111,7 +114,7 @@ float _solve_circle_aabb(const object_t* circle, const object_t* aabb, float* ou 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; + const float solve_distance = circle->physics.circle.radius - dist; // distance to solve collision float solve_x, solve_y; normalize(dif_x, dif_y, &solve_x, &solve_y); @@ -122,11 +125,11 @@ float _solve_circle_aabb(const object_t* circle, const object_t* aabb, float* ou 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 x1 = a->physics.circle.x + a->sprite.x, y1 = a->physics.circle.y + a->sprite.y; + const float x2 = b->physics.circle.x + b->sprite.x, y2 = b->physics.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; + const float target_difference = a->physics.circle.radius + b->physics.circle.radius; float dir_x, dir_y; normalize(dif_x, dif_y, &dir_x, &dir_y); *out_px = dir_x * target_difference; @@ -136,10 +139,10 @@ float _solve_circle_circle(const object_t* a, const object_t* b, float* out_px, 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 right = (a->physics.aabb.x + a->physics.aabb.w + a->sprite.x) - (b->physics.aabb.x + b->sprite.x); + float left = (a->physics.aabb.x + a->sprite.x) - (b->physics.aabb.x + b->physics.aabb.w + b->sprite.x); + float top = (a->physics.aabb.y + a->sprite.y) - (b->physics.aabb.y + b->physics.aabb.w + b->sprite.y); + float bottom = (a->physics.aabb.y + a->physics.aabb.h) - (b->physics.aabb.y + b->sprite.y); float ret = right; *out_px = right; @@ -165,25 +168,24 @@ float _solve_aabb_aabb(const object_t* a, const object_t* b, float* out_px, floa } 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) { + if(a->physics.type == COLLIDERTYPE_AABB && b->physics.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) { + } else if(a->physics.type == COLLIDERTYPE_AABB && b->physics.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) { + } else if(a->physics.type == COLLIDERTYPE_CIRCLE && b->physics.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) { + } else if(a->physics.type == COLLIDERTYPE_CIRCLE && b->physics.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) { +void solve_collision_slide(object_t* left, object_t* right) { float dx, dy; - const float d = get_solve_force(this, other, &dx, &dy); - this->sprite.x += dx; - this->sprite.y += dy; + const float d = get_solve_force(left, right, &dx, &dy); + left->sprite.x += dx; + left->sprite.y += dy; } static inline @@ -196,22 +198,26 @@ void _solve_move(object_t* this) { if(can_collide(other) && this != other && _collision_check(other, this)) { object_broadcast_collision(other, this); object_broadcast_collision(this, other); - _solve_collision_slide(this, other); + this->physics.solver(this, other); } } } -void interpolate_move(object_t* this, const float target_x, const float target_y, const float max_step_size, const int slide) { +void physics_move(object_t* this, float delta_time) { + const float max_step_size = this->physics.max_interpolate_step_size; // calculate step delta - float dx = target_x - this->sprite.x, dy = target_y - this->sprite.y; + float dx = this->physics.velocity_x * delta_time, dy = this->physics.velocity_y * delta_time; + const float target_x = this->sprite.x + dx, + target_y = this->sprite.y + dy; if(dx == 0 && dy == 0) return; + // calculate direction x,y float m = sqrtf(dx*dx + dy*dy); - dx /= m; - dy /= m; - dx *= max_step_size; dy *= max_step_size; - int step_count = max_step_size / m; + dx = dx / m * max_step_size; + dy = dy / m * max_step_size; + + const int step_count = max_step_size / m; // ensure this object would ever collide // if it wouldn't collide anyway, just set position @@ -225,6 +231,7 @@ void interpolate_move(object_t* this, const float target_x, const float target_y this->sprite.x = target_x; this->sprite.y = target_y; _solve_move(this); + return; } /* @@ -233,21 +240,14 @@ void interpolate_move(object_t* this, const float target_x, const float target_y */ for(int steps = 0; steps <= step_count && (this->sprite.x != target_x || this->sprite.y != target_y); ++steps) { // move towards target, snap to target if distance is too low - const float old_x = this->sprite.x, old_y = this->sprite.y; - float new_x, new_y; - const float distx = fabsf(this->sprite.x - target_x), disty = fabsf(this->sprite.y - target_y); const float sqdist = distx*distx + disty*disty; if(sqdist > max_step_size*max_step_size) { this->sprite.x += dx; this->sprite.y += dy; - new_x = this->sprite.x; - new_y = this->sprite.y; } else { this->sprite.x = target_x; this->sprite.y = target_y; - new_x = target_x; - new_y = target_y; } _solve_move(this); diff --git a/src/corelib/physics.h b/src/corelib/physics.h index 93e145c..5a00e40 100644 --- a/src/corelib/physics.h +++ b/src/corelib/physics.h @@ -5,7 +5,8 @@ typedef struct object_t object_t; -typedef void(*collided_fn)(object_t*, struct object_t*); +typedef void(*collided_fn)(object_t*, object_t*); +typedef void(*solver_fn)(object_t* left, object_t* right); typedef enum collider_type_t { COLLIDERTYPE_MIN, @@ -15,27 +16,34 @@ typedef enum collider_type_t { COLLIDERTYPE_MAX, } collider_type_t; -typedef struct collider_t { +typedef struct circle_t { + float x, y; + float radius; +} circle_t; + +typedef struct physics_t { collider_type_t type; collided_fn evt_collision; + solver_fn solver; + float velocity_x, velocity_y; + float max_interpolate_step_size; union { - struct { - float x, y; - float radius; - } circle; + circle_t circle; SDL_FRect aabb; }; -} collider_t; +} physics_t; -collider_t collider_default(); +extern physics_t physics_default(); -void object_broadcast_evt_collision(object_t* this, object_t* other); +extern void object_broadcast_evt_collision(object_t* this, object_t* other); -void physics_update(); +extern void physics_update(); -void interpolate_move(object_t* object, float target_x, float target_y, float max_step_size, int slide); +extern void physics_move(object_t* this, float delta_time); extern short can_collide(const object_t* this); + +extern void solve_collision_slide(object_t* left, object_t* right); extern float get_solve_force(const object_t* this, const object_t* other, float* solve_x, float* solve_y); #endif /* _physics_h */