rework collision_t component into a more wholistic physics_t component #11

Merged
Sara merged 7 commits from object-physics into main 2023-06-26 15:28:34 +00:00
5 changed files with 83 additions and 73 deletions

View File

@ -17,14 +17,17 @@ find_package(SDL2)
find_package(SDL2_image) find_package(SDL2_image)
find_package(SDL2_ttf) find_package(SDL2_ttf)
file(GLOB_RECURSE file(
GLOB_RECURSE
SOURCE_C SOURCE_C
"${CMAKE_SOURCE_DIR}/src/**.c") "${CMAKE_SOURCE_DIR}/src/**.c")
file(GLOB_RECURSE file(
GLOB_RECURSE
SOURCE_CPP SOURCE_CPP
"${CMAKE_SOURCE_DIR}/src/**.c*") "${CMAKE_SOURCE_DIR}/src/**.c*")
add_executable(${PROJECT} add_executable(
${PROJECT}
${SOURCE_C} ${SOURCE_C}
${SOURCE_CPP}) ${SOURCE_CPP})

View File

@ -4,7 +4,7 @@ object_t object_default() {
return (object_t){ return (object_t){
.active = 1, .active = 1,
.enabled = 1, .enabled = 1,
.collider = collider_default(), .physics = physics_default(),
.evt_draw = &object_draw_sprite, .evt_draw = &object_draw_sprite,
.evt_update = NULL, .evt_update = NULL,
.sprite = sprite_default(), .sprite = sprite_default(),

View File

@ -5,7 +5,6 @@
#include "physics.h" #include "physics.h"
typedef struct object_t object_t; typedef struct object_t object_t;
typedef struct collider_t collider_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*);
@ -14,7 +13,7 @@ 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. physics_t physics; // 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

View File

@ -9,15 +9,18 @@ float fclampf(float x, float min_, float max_) {
} }
collider_t collider_default() { physics_t physics_default() {
return (collider_t) { return (physics_t) {
.type=COLLIDERTYPE_NONE .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) { void object_broadcast_collision(object_t* this, object_t* other) {
if(this->collider.evt_collision != NULL) { if(this->physics.evt_collision != NULL) {
this->collider.evt_collision(this, other); this->physics.evt_collision(this, other);
} }
} }
short can_collide(const object_t* this) { 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 static inline
short _collision_aabb_aabb(const object_t* a, const object_t* b) { 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 aminx = a->physics.aabb.x + a->sprite.x, aminy = a->physics.aabb.y + a->sprite.x;
const float amaxx = aminx + a->collider.aabb.w, amaxy = aminy + a->collider.aabb.h; const float amaxx = aminx + a->physics.aabb.w, amaxy = aminy + a->physics.aabb.h;
const float bminx = b->collider.aabb.x, bminy = b->collider.aabb.y; const float bminx = b->physics.aabb.x, bminy = b->physics.aabb.y;
const float bmaxx = b->collider.aabb.x + b->collider.aabb.w, bmaxy = b->collider.aabb.y + b->collider.aabb.h; 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); return _rect_overlap(aminx, aminy, amaxx, amaxy, bminx, bminy, bmaxx, bmaxy);
} }
static inline static inline
short _collision_circle_circle(const object_t* a, const object_t* b) { 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, const float ax = a->sprite.x + a->physics.circle.x, ay = a->sprite.y + a->physics.circle.y,
bx = b->sprite.x + b->collider.circle.x, by = b->sprite.y + b->collider.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 dx = fabsf(ax-bx), dy = fabsf(ay-by);
const float sqrdist = dx*dx+dy*dy; 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; const float mindistsqr = mindist*mindist;
return sqrdist < mindistsqr; return sqrdist < mindistsqr;
} }
@ -62,17 +65,17 @@ short _collision_circle_circle(const object_t* a, const object_t* b) {
static inline static inline
short _collision_circle_aabb(const object_t* circle, const object_t* aabb) { 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 // 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, const float bbminx = aabb->physics.aabb.x + aabb->sprite.x, bbmaxx = bbminx + aabb->physics.aabb.w,
bbminy = aabb->collider.aabb.y + aabb->sprite.y, bbmaxy = bbminy + aabb->collider.aabb.h; bbminy = aabb->physics.aabb.y + aabb->sprite.y, bbmaxy = bbminy + aabb->physics.aabb.h;
const float cx = circle->sprite.x + circle->collider.circle.x, const float cx = circle->sprite.x + circle->physics.circle.x,
cy = circle->sprite.y + circle->collider.circle.y; cy = circle->sprite.y + circle->physics.circle.y;
const float x = fclampf(cx, bbminx, bbmaxx), const float x = fclampf(cx, bbminx, bbmaxx),
y = fclampf(cy, bbminy, bbmaxy); y = fclampf(cy, bbminy, bbmaxy);
const float dx = fabsf(cx - x), dy = fabsf(cy - y); 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 // 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 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 if the square distance is larger than the square of the radius
return distsqr < rsqr; return distsqr < rsqr;
@ -80,13 +83,13 @@ short _collision_circle_aabb(const object_t* circle, const object_t* aabb) {
static inline static inline
short _collision_check(const object_t* a, const object_t* b) { 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); 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); 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); 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 _collision_circle_aabb(b, a);
} }
return 0; return 0;
@ -95,11 +98,11 @@ short _collision_check(const object_t* a, const object_t* b) {
static inline static inline
float _solve_circle_aabb(const object_t* circle, const object_t* aabb, float* out_px, float* out_py) { 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 // 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, const float bbminx = aabb->physics.aabb.x + aabb->sprite.x, bbmaxx = bbminx + aabb->physics.aabb.w,
bbminy = aabb->collider.aabb.y + aabb->sprite.y, bbmaxy = bbminy + aabb->collider.aabb.h; bbminy = aabb->physics.aabb.y + aabb->sprite.y, bbmaxy = bbminy + aabb->physics.aabb.h;
// the centre of the circle in world space // the centre of the circle in world space
const float cx = circle->sprite.x + circle->collider.circle.x, const float cx = circle->sprite.x + circle->physics.circle.x,
cy = circle->sprite.y + circle->collider.circle.y; cy = circle->sprite.y + circle->physics.circle.y;
// the point on the rectangle closest to the centre of the circle // the point on the rectangle closest to the centre of the circle
const float x = fclampf(cx, bbminx, bbmaxx), const float x = fclampf(cx, bbminx, bbmaxx),
y = fclampf(cy, bbminy, bbmaxy); 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); dist_y = fabsf(dif_y);
// euclidean distance // euclidean distance
const float dist = sqrt(dist_x*dist_x + dist_y*dist_y); 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 // distance to solve collision
float solve_x, solve_y; float solve_x, solve_y;
normalize(dif_x, dif_y, &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 static inline
float _solve_circle_circle(const object_t* a, const object_t* b, float* out_px, float* out_py) { 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 x1 = a->physics.circle.x + a->sprite.x, y1 = a->physics.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 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 dif_x = x1 - x2, dif_y = y1 - y2;
const float difference = sqrtf(fabsf(dif_x*dif_x) + fabsf(dif_y*dif_y)); 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; float dir_x, dir_y;
normalize(dif_x, dif_y, &dir_x, &dir_y); normalize(dif_x, dif_y, &dir_x, &dir_y);
*out_px = dir_x * target_difference; *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 static inline
float _solve_aabb_aabb(const object_t* a, const object_t* b, float* out_px, float* out_py) { 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 right = (a->physics.aabb.x + a->physics.aabb.w + a->sprite.x) - (b->physics.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 left = (a->physics.aabb.x + a->sprite.x) - (b->physics.aabb.x + b->physics.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 top = (a->physics.aabb.y + a->sprite.y) - (b->physics.aabb.y + b->physics.aabb.w + b->sprite.y);
float bottom = (a->collider.aabb.y + a->collider.aabb.h) - (b->collider.aabb.y + b->sprite.y); float bottom = (a->physics.aabb.y + a->physics.aabb.h) - (b->physics.aabb.y + b->sprite.y);
float ret = right; float ret = right;
*out_px = 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) { 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); 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); float penetration_distance = _solve_circle_aabb(b, a, out_px, out_py);
*out_px = -(*out_px); *out_px = -(*out_px);
*out_py = -(*out_py); *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); 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); return _solve_circle_circle(a, b, out_px, out_py);
} }
} }
static inline void solve_collision_slide(object_t* left, object_t* right) {
void _solve_collision_slide(object_t* this, object_t* other) {
float dx, dy; float dx, dy;
const float d = get_solve_force(this, other, &dx, &dy); const float d = get_solve_force(left, right, &dx, &dy);
this->sprite.x += dx; left->sprite.x += dx;
this->sprite.y += dy; left->sprite.y += dy;
} }
static inline static inline
@ -196,22 +198,26 @@ void _solve_move(object_t* this) {
if(can_collide(other) && this != other && _collision_check(other, this)) { if(can_collide(other) && this != other && _collision_check(other, this)) {
object_broadcast_collision(other, this); object_broadcast_collision(other, this);
object_broadcast_collision(this, other); 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 // 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) if(dx == 0 && dy == 0)
return; return;
// calculate direction x,y // calculate direction x,y
float m = sqrtf(dx*dx + dy*dy); float m = sqrtf(dx*dx + dy*dy);
dx /= m; dx = dx / m * max_step_size;
dy /= m; dy = dy / m * max_step_size;
dx *= max_step_size; dy *= max_step_size;
int step_count = max_step_size / m; const int step_count = max_step_size / m;
// ensure this object would ever collide // ensure this object would ever collide
// if it wouldn't collide anyway, just set position // 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.x = target_x;
this->sprite.y = target_y; this->sprite.y = target_y;
_solve_move(this); _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) { 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 // 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 distx = fabsf(this->sprite.x - target_x), disty = fabsf(this->sprite.y - target_y);
const float sqdist = distx*distx + disty*disty; const float sqdist = distx*distx + disty*disty;
if(sqdist > max_step_size*max_step_size) { if(sqdist > max_step_size*max_step_size) {
this->sprite.x += dx; this->sprite.x += dx;
this->sprite.y += dy; this->sprite.y += dy;
new_x = this->sprite.x;
new_y = this->sprite.y;
} else { } else {
this->sprite.x = target_x; this->sprite.x = target_x;
this->sprite.y = target_y; this->sprite.y = target_y;
new_x = target_x;
new_y = target_y;
} }
_solve_move(this); _solve_move(this);

View File

@ -5,7 +5,8 @@
typedef struct object_t object_t; 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 { typedef enum collider_type_t {
COLLIDERTYPE_MIN, COLLIDERTYPE_MIN,
@ -15,27 +16,34 @@ typedef enum collider_type_t {
COLLIDERTYPE_MAX, COLLIDERTYPE_MAX,
} collider_type_t; } 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; collider_type_t type;
collided_fn evt_collision; collided_fn evt_collision;
solver_fn solver;
float velocity_x, velocity_y;
float max_interpolate_step_size;
union { union {
struct { circle_t circle;
float x, y;
float radius;
} circle;
SDL_FRect aabb; 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 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); extern float get_solve_force(const object_t* this, const object_t* other, float* solve_x, float* solve_y);
#endif /* _physics_h */ #endif /* _physics_h */