From 941eeb2493939fad2793c0c8a499509a2bcbe914 Mon Sep 17 00:00:00 2001 From: Andrew Kesterson Date: Tue, 26 May 2026 11:22:45 -0400 Subject: [PATCH] Got the physics system applying gravity and drag correctly. (Mostly? Seems like it? Seems good.) --- CMakeLists.txt | 4 + include/akgl/SDL_GameControllerDB.h | 2 +- include/akgl/actor.h | 26 +++++- include/akgl/physics.h | 12 +-- src/actor.c | 51 ++++++++--- src/physics.c | 128 ++++++++++++++++------------ 6 files changed, 148 insertions(+), 75 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e7c5f1..7ca7763 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,8 @@ add_library(akgl SHARED src/heap.c src/json_helpers.c src/registry.c + src/renderer.c + src/physics.c src/sprite.c src/staticstring.c src/tilemap.c @@ -142,6 +144,8 @@ install(FILES "include/akgl/controller.h" DESTINATION "include/akgl/") install(FILES "include/akgl/heap.h" DESTINATION "include/akgl/") install(FILES "include/akgl/iterator.h" DESTINATION "include/akgl/") install(FILES "include/akgl/json_helpers.h" DESTINATION "include/akgl/") +install(FILES "include/akgl/renderer.h" DESTINATION "include/akgl/") +install(FILES "include/akgl/physics.h" DESTINATION "include/akgl/") install(FILES "include/akgl/registry.h" DESTINATION "include/akgl/") install(FILES "include/akgl/sprite.h" DESTINATION "include/akgl/") install(FILES "include/akgl/staticstring.h" DESTINATION "include/akgl/") diff --git a/include/akgl/SDL_GameControllerDB.h b/include/akgl/SDL_GameControllerDB.h index 41ea51d..31d8a5e 100644 --- a/include/akgl/SDL_GameControllerDB.h +++ b/include/akgl/SDL_GameControllerDB.h @@ -1,7 +1,7 @@ #ifndef _SDL_GAMECONTROLLERDB_H_ #define _SDL_GAMECONTROLLERDB_H_ -// Taken from https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt on Tue May 26 10:27:58 AM EDT 2026 +// Taken from https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt on Tue May 26 04:12:10 PM EDT 2026 #define AKGL_SDL_GAMECONTROLLER_DB_LEN 2228 diff --git a/include/akgl/actor.h b/include/akgl/actor.h index cfa4fda..a5d16e9 100644 --- a/include/akgl/actor.h +++ b/include/akgl/actor.h @@ -71,9 +71,31 @@ typedef struct akgl_Actor { void *actorData; bool visible; SDL_Time movetimer; + // Velocity. Combined effect of all forces acting on the actor resulting + // in energy along an axis. float32_t vx; float32_t vy; float32_t vz; + // Environmental velocity. These are the forces acting on the actor by the + // environment (such as gravity and atmospheric drag) + float32_t ex; + float32_t ey; + float32_t ez; + // Thrust. Energy originating only from the actor's own acceleration on a + // given axis, before the effects of gravity and drag. + float32_t tx; + float32_t ty; + float32_t tz; + // Acceleration. These are borrowed from the base character object. + // A given axis resets to 0 when the actor stops moving in a given axis. + float32_t ax; + float32_t ay; + float32_t az; + // Max speed. These are borrowed from the base character object. + float32_t sx; + float32_t sy; + float32_t sz; + // Position. float32_t x; float32_t y; float32_t z; @@ -83,7 +105,7 @@ typedef struct akgl_Actor { akerr_ErrorContext AKERR_NOIGNORE *(*updatefunc)(struct akgl_Actor *obj); akerr_ErrorContext AKERR_NOIGNORE *(*renderfunc)(struct akgl_Actor *obj); akerr_ErrorContext AKERR_NOIGNORE *(*facefunc)(struct akgl_Actor *obj); - akerr_ErrorContext AKERR_NOIGNORE *(*movementlogicfunc)(struct akgl_Actor *obj, SDL_Time curtimems); + akerr_ErrorContext AKERR_NOIGNORE *(*movementlogicfunc)(struct akgl_Actor *obj, float32_t dt); akerr_ErrorContext AKERR_NOIGNORE *(*changeframefunc)(struct akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtimems); akerr_ErrorContext AKERR_NOIGNORE *(*addchild)(struct akgl_Actor *obj, struct akgl_Actor *child); } akgl_Actor; @@ -92,7 +114,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_initialize(akgl_Actor *obj, char * akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_set_character(akgl_Actor *obj, char *basecharname); akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_render(akgl_Actor *obj); akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_update(akgl_Actor *obj); -akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_logic_movement(akgl_Actor *obj, SDL_Time curtimems); +akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_logic_movement(akgl_Actor *obj, float32_t dt); akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_logic_changeframe(akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtimems); akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_automatic_face(akgl_Actor *obj); akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_add_child(akgl_Actor *obj, akgl_Actor *child); diff --git a/include/akgl/physics.h b/include/akgl/physics.h index ccaf33b..19659ae 100644 --- a/include/akgl/physics.h +++ b/include/akgl/physics.h @@ -8,9 +8,9 @@ typedef struct akgl_PhysicsBackend { akerr_ErrorContext AKERR_NOIGNORE *(*simulate)(struct akgl_PhysicsBackend *self, akgl_Iterator *opflags); - akerr_ErrorContext AKERR_NOIGNORE *(*gravity)(struct akgl_PhysicsBackend *self, akgl_Actor *actor, SDL_Time curtime); + akerr_ErrorContext AKERR_NOIGNORE *(*gravity)(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt); akerr_ErrorContext AKERR_NOIGNORE *(*collide)(struct akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2); - akerr_ErrorContext AKERR_NOIGNORE *(*move)(struct akgl_PhysicsBackend *self, akgl_Actor *actor, SDL_Time curtime); + akerr_ErrorContext AKERR_NOIGNORE *(*move)(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt); double drag_x; double drag_y; @@ -23,15 +23,15 @@ typedef struct akgl_PhysicsBackend { } akgl_PhysicsBackend; -akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, SDL_Time curtime); +akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt); akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2); -akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_move(akgl_PhysicsBackend *self, akgl_Actor *actor, SDL_Time curtime); +akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_move(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt); akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_null(akgl_PhysicsBackend *self); -akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, SDL_Time curtime); +akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt); akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2); -akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_move(akgl_PhysicsBackend *self, akgl_Actor *actor, SDL_Time curtime); +akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_move(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt); akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_sidescroller(akgl_PhysicsBackend *self); diff --git a/src/actor.c b/src/actor.c index 2c587c1..39de9e7 100644 --- a/src/actor.c +++ b/src/actor.c @@ -51,6 +51,10 @@ akerr_ErrorContext *akgl_actor_set_character(akgl_Actor *obj, char *basecharname obj->basechar = SDL_GetPointerProperty(AKGL_REGISTRY_CHARACTER, basecharname, NULL); FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "Character not found in the registry"); + obj->ax = 0; + obj->ay = 0; + obj->sx = obj->basechar->sx; + obj->sy = obj->basechar->sy; SUCCEED_RETURN(errctx); } @@ -113,20 +117,25 @@ akerr_ErrorContext *akgl_actor_logic_changeframe(akgl_Actor *obj, akgl_Sprite *c SUCCEED_RETURN(errctx); } -// raises AKGL_ERR_LOGICINTERRUPT if we don't want the physics object to process us -akerr_ErrorContext *akgl_actor_logic_movement(akgl_Actor *obj, SDL_Time curtime) +// raises AKGL_ERR_LOGICINTERRUPT if we don't want the physics simulator to process us +akerr_ErrorContext *akgl_actor_logic_movement(akgl_Actor *actor, float32_t dt) { PREPARE_ERROR(errctx); - FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "obj"); - FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "obj->basechar"); - if ( obj->vx > obj->basechar->sx ) { - obj->vx = obj->basechar->sx; + FAIL_ZERO_RETURN(errctx, actor, AKERR_NULLPOINTER, "actor"); + FAIL_ZERO_RETURN(errctx, actor, AKERR_NULLPOINTER, "actor->basechar"); + actor->sx = actor->basechar->sx; + actor->sy = actor->basechar->sy; + actor->sz = actor->basechar->sz; + if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_LEFT) ) { + actor->ax = -actor->basechar->ax; + } else if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_RIGHT) ) { + actor->ax = actor->basechar->ax; } - if ( obj->vy > obj->basechar->sy ) { - obj->vy = obj->basechar->sy; + if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_UP) ) { + actor->ay = -actor->basechar->ay; + } else if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_DOWN) ) { + actor->ay = actor->basechar->ay; } - // Effectively a NOOP, this is handled by the physics engine now - // These functions are still present in case the library user wants per-actor behavior SUCCEED_RETURN(errctx); } @@ -268,10 +277,12 @@ akerr_ErrorContext *akgl_actor_add_child(akgl_Actor *obj, akgl_Actor *child) akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_left_on(akgl_Actor *obj, SDL_Event *event) { PREPARE_ERROR(errctx); - FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); - FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); + FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "actor"); + FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "event"); + FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "actor->basechar"); //SDL_Log("event %d (button %d / key %d) moves actor left", event->type, event->gbutton.which, event->key.key); AKGL_BITMASK_DEL(obj->state, (AKGL_ACTOR_STATE_FACE_ALL | AKGL_ACTOR_STATE_MOVING_ALL)); + obj->ax = -(obj->basechar->ax); AKGL_BITMASK_ADD(obj->state, (AKGL_ACTOR_STATE_MOVING_LEFT | AKGL_ACTOR_STATE_FACE_LEFT)); //SDL_Log("new target actor state: %b", obj->state); SUCCEED_RETURN(errctx); @@ -283,6 +294,9 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_left_off(akgl_Actor *obj, SDL FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); //SDL_Log("event %d (button %d / key %d) stops moving actor left", event->type, event->gbutton.which, event->key.key); + obj->ax = 0; + obj->ex = 0; + obj->tx = 0; obj->vx = 0; AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_LEFT); //SDL_Log("new target actor state: %b", obj->state); @@ -294,7 +308,9 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_right_on(akgl_Actor *obj, SDL PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); + FAIL_ZERO_RETURN(errctx, obj->basechar, AKERR_NULLPOINTER, "actor->basechar"); //SDL_Log("event %d (button %d / key %d) moves actor right", event->type, event->gbutton.which, event->key.key); + obj->ax = obj->basechar->ax; AKGL_BITMASK_DEL(obj->state, (AKGL_ACTOR_STATE_FACE_ALL | AKGL_ACTOR_STATE_MOVING_ALL)); AKGL_BITMASK_ADD(obj->state, (AKGL_ACTOR_STATE_MOVING_RIGHT | AKGL_ACTOR_STATE_FACE_RIGHT)); //SDL_Log("new target actor state: %b", obj->state); @@ -307,6 +323,9 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_right_off(akgl_Actor *obj, SD FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); //SDL_Log("event %d (button %d / key %d) stops moving actor right", event->type, event->gbutton.which, event->key.key); + obj->ax = 0; + obj->ex = 0; + obj->tx = 0; obj->vx = 0; AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_RIGHT); //SDL_Log("new target actor state: %b", obj->state); @@ -319,6 +338,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_up_on(akgl_Actor *obj, SDL_Ev FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); //SDL_Log("event %d (button %d / key %d) moves actor up", event->type, event->gbutton.which, event->key.key); + obj->ay = -(obj->basechar->ay); AKGL_BITMASK_DEL(obj->state, (AKGL_ACTOR_STATE_FACE_ALL | AKGL_ACTOR_STATE_MOVING_ALL)); AKGL_BITMASK_ADD(obj->state, (AKGL_ACTOR_STATE_FACE_UP | AKGL_ACTOR_STATE_MOVING_UP)); //SDL_Log("new target actor state: %b", obj->state); @@ -331,6 +351,9 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_up_off(akgl_Actor *obj, SDL_E FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); //SDL_Log("event %d (button %d / key %d) stops moving actor up", event->type, event->gbutton.which, event->key.key); + obj->ay = 0; + obj->ey = 0; + obj->ty = 0; obj->vy = 0; AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_UP); //SDL_Log("new target actor state: %b", obj->state); @@ -343,6 +366,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_down_on(akgl_Actor *obj, SDL_ FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); //SDL_Log("event %d (button %d / key %d) moves actor down", event->type, event->gbutton.which, event->key.key); + obj->ay = obj->basechar->ay; AKGL_BITMASK_DEL(obj->state, (AKGL_ACTOR_STATE_FACE_ALL | AKGL_ACTOR_STATE_MOVING_ALL)); AKGL_BITMASK_ADD(obj->state, (AKGL_ACTOR_STATE_MOVING_DOWN | AKGL_ACTOR_STATE_FACE_DOWN)); //SDL_Log("new target actor state: %b", obj->state); @@ -355,6 +379,9 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_Actor_cmhf_down_off(akgl_Actor *obj, SDL FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor"); FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event"); //SDL_Log("event %d (button %d / key %d) stops moving actor down", event->type, event->gbutton.which, event->key.key); + obj->ty = 0; + obj->ey = 0; + obj->ay = 0; obj->vy = 0; AKGL_BITMASK_DEL(obj->state, AKGL_ACTOR_STATE_MOVING_DOWN); //SDL_Log("new target actor state: %b", obj->state); diff --git a/src/physics.c b/src/physics.c index 61a45ec..10477b7 100644 --- a/src/physics.c +++ b/src/physics.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -6,7 +7,7 @@ #include #include -akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, SDL_Time curtime) +akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt) { PREPARE_ERROR(e); FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self"); @@ -20,7 +21,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_collide(akgl_PhysicsBackend SUCCEED_RETURN(e); } -akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, SDL_Time curtime) +akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt) { PREPARE_ERROR(e); FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self"); @@ -41,25 +42,19 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_null(akgl_PhysicsBackend *s } -akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, SDL_Time curtime) +akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt) { PREPARE_ERROR(e); FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self"); - // Do nothing - // Calculate the drop of all relevant actors which is a function of their mass and the - // world's gravity + FAIL_ZERO_RETURN(e, actor, AKERR_NULLPOINTER, "actor"); + + // Assume the X origin is - (screen left) + actor->ex -= (self->gravity_x * dt); + // Assume Y origin is + (down screen) + actor->ey += (self->gravity_y * dt); + // Assume Z origin is - (behind the camera) + actor->ez -= (self->gravity_z * dt); - //Gravity is applied in units per second. So we apply - // (unit / milliseconds per second) * (milliseconds since last update) - actor->vx += ((self->gravity_x / AKGL_TIME_ONESEC_NS) * (curtime - self->gravity_time)); - actor->vy += ((self->gravity_y / AKGL_TIME_ONESEC_NS) * (curtime - self->gravity_time)); - actor->vz += ((self->gravity_z / AKGL_TIME_ONESEC_NS) * (curtime - self->gravity_time)); - - // Apply atmospheric drag - actor->vx -= actor->vx * self->drag_x * (curtime - self->gravity_time); - actor->vy -= actor->vy * self->drag_x * (curtime - self->gravity_time); - actor->vz -= actor->vz * self->drag_x * (curtime - self->gravity_time); - // Need a euler function SUCCEED_RETURN(e); } @@ -71,42 +66,14 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_collide(akgl_PhysicsBackend * SUCCEED_RETURN(e); } -akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, SDL_Time curtime) +akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_ss_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt) { PREPARE_ERROR(e); FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self"); FAIL_ZERO_RETURN(e, actor, AKERR_NULLPOINTER, "actor"); - - if ( actor->parent != NULL ) { - // Children don't move independently of their parents, they just have an offset - SUCCEED_RETURN(e); - } else if ( actor->basechar == NULL ) { - SUCCEED_RETURN(e); - } else { - if ( (curtime - actor->movetimer) >= actor->basechar->speedtime ) { - actor->movetimer = curtime; - ATTEMPT { - CATCH(e, actor->movementlogicfunc(actor,curtime)); - } CLEANUP { - } PROCESS(e) { - } HANDLE(e, AKGL_ERR_LOGICINTERRUPT) { - // The actor told us NOT to process them, they handled their own update - SUCCEED_RETURN(e); - } FINISH(e, true); - if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_LEFT) ) { - actor->x += -actor->vx; - } - if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_RIGHT) ) { - actor->x += actor->vx; - } - if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_UP) ) { - actor->y += -actor->vy; - } - if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_DOWN) ) { - actor->y += actor->vy; - } - } - } + actor->x += actor->vx * dt; + actor->y += actor->vy * dt; + actor->z += actor->vz * dt; SUCCEED_RETURN(e); } @@ -151,6 +118,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_simulate(akgl_PhysicsBackend *se .layerid = 0 }; SDL_Time curtime = SDL_GetTicksNS(); + float32_t dt = (float32_t)(curtime - self->gravity_time) / (float32_t)AKGL_TIME_ONESEC_NS; akgl_Actor *actor = NULL; FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self"); @@ -160,27 +128,79 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_simulate(akgl_PhysicsBackend *se opflags = &defflags; } + for ( int i = 0; i < AKGL_MAX_HEAP_ACTOR; i++ ) { actor = &HEAP_ACTOR[i]; if ( actor->refcount == 0 ) { continue; } + if ( actor->parent != NULL ) { + // Children don't move independently of their parents, they just have an offset + actor->x = actor->parent->x + actor->vx; + actor->y = actor->parent->y + actor->vy; + actor->z = actor->parent->z + actor->vz; + continue; + } else if ( actor->basechar == NULL ) { + continue; + } if ( AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_LAYERMASK) ) { if ( actor->layer != opflags->layerid ) { continue; } } + + // thrust is a function of acceleration on a given axis if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_LEFT) || AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_RIGHT) ) { - actor->vx += actor->basechar->ax; + actor->tx += actor->ax * dt; } if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_UP) || AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_DOWN) ) { - actor->vy += actor->basechar->ay; + actor->ty += actor->ay * dt; } - PASS(e, self->gravity(self, actor, curtime)); - PASS(e, self->move(self, actor, curtime)); - self->gravity_time = curtime; + + // velocity equals thrust unless thrust exceeds max speed + if ( fabsf(actor->tx) > fabsf(actor->sx) ) { + if ( actor->tx < 0 ) { + actor->tx = -actor->sx; + } else { + actor->tx = actor->sx; + } + } + if ( fabsf(actor->ty) > fabsf(actor->sy) ) { + if ( actor->ty < 0 ) { + actor->ty = -actor->sy; + } else { + actor->ty = actor->sy; + } + } + if ( fabsf(actor->tz) > fabsf(actor->sz) ) { + if ( actor->tz < 0 ) { + actor->tz = -actor->sz; + } else { + actor->tz = actor->sz; + } + } + ATTEMPT { + CATCH(e, actor->movementlogicfunc(actor, dt)); + PASS(e, self->gravity(self, actor, dt)); + + // Counteract velocity with atmospheric drag + actor->ex -= actor->ex * self->drag_x * dt; + actor->ey -= actor->ey * self->drag_y * dt; + actor->ez -= actor->ez * self->drag_z * dt; + + actor->vx = actor->ex + actor->tx; + actor->vy = actor->ey + actor->ty; + actor->vz = actor->ez + actor->tz; + + PASS(e, self->move(self, actor, dt)); + } CLEANUP { + } PROCESS(e) { + } HANDLE(e, AKGL_ERR_LOGICINTERRUPT) { + // noop + } FINISH(e, true); } + self->gravity_time = curtime; SUCCEED_RETURN(e); }