Got the physics system applying gravity and drag correctly. (Mostly? Seems like it? Seems good.)

This commit is contained in:
2026-05-26 11:22:45 -04:00
parent 314ce5e10d
commit 941eeb2493
6 changed files with 148 additions and 75 deletions

View File

@@ -1,3 +1,4 @@
#include <math.h>
#include <akstdlib.h>
#include <akgl/physics.h>
#include <akgl/actor.h>
@@ -6,7 +7,7 @@
#include <akgl/heap.h>
#include <akgl/registry.h>
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);
}