2026-05-26 11:22:45 -04:00
|
|
|
#include <math.h>
|
2026-05-26 10:36:31 -04:00
|
|
|
#include <akstdlib.h>
|
|
|
|
|
#include <akgl/physics.h>
|
|
|
|
|
#include <akgl/actor.h>
|
|
|
|
|
#include <akgl/game.h>
|
|
|
|
|
#include <akgl/error.h>
|
|
|
|
|
#include <akgl/heap.h>
|
|
|
|
|
#include <akgl/registry.h>
|
|
|
|
|
|
2026-05-26 11:22:45 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
|
2026-05-26 10:36:31 -04:00
|
|
|
{
|
|
|
|
|
PREPARE_ERROR(e);
|
|
|
|
|
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
|
|
|
|
SUCCEED_RETURN(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2)
|
|
|
|
|
{
|
|
|
|
|
PREPARE_ERROR(e);
|
|
|
|
|
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
|
|
|
|
SUCCEED_RETURN(e);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-26 11:22:45 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
|
2026-05-26 10:36:31 -04:00
|
|
|
{
|
|
|
|
|
PREPARE_ERROR(e);
|
|
|
|
|
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
|
|
|
|
SUCCEED_RETURN(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_null(akgl_PhysicsBackend *self)
|
|
|
|
|
{
|
|
|
|
|
PREPARE_ERROR(e);
|
|
|
|
|
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
|
|
|
|
|
|
|
|
|
self->gravity = akgl_physics_null_gravity;
|
|
|
|
|
self->collide = akgl_physics_null_collide;
|
|
|
|
|
self->move = akgl_physics_null_move;
|
|
|
|
|
self->simulate = akgl_physics_simulate;
|
|
|
|
|
|
|
|
|
|
SUCCEED_RETURN(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2026-06-02 13:15:26 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
|
2026-05-26 10:36:31 -04:00
|
|
|
{
|
|
|
|
|
PREPARE_ERROR(e);
|
|
|
|
|
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
2026-05-26 11:22:45 -04:00
|
|
|
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);
|
|
|
|
|
|
2026-05-26 10:36:31 -04:00
|
|
|
SUCCEED_RETURN(e);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-02 13:15:26 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2)
|
2026-05-26 10:36:31 -04:00
|
|
|
{
|
|
|
|
|
PREPARE_ERROR(e);
|
|
|
|
|
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
|
|
|
|
FAIL_RETURN(e, AKERR_API, "Not implemented");
|
|
|
|
|
SUCCEED_RETURN(e);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-02 13:15:26 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
|
2026-05-26 10:36:31 -04:00
|
|
|
{
|
|
|
|
|
PREPARE_ERROR(e);
|
|
|
|
|
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
|
|
|
|
FAIL_ZERO_RETURN(e, actor, AKERR_NULLPOINTER, "actor");
|
2026-05-26 11:22:45 -04:00
|
|
|
actor->x += actor->vx * dt;
|
|
|
|
|
actor->y += actor->vy * dt;
|
|
|
|
|
actor->z += actor->vz * dt;
|
2026-05-26 10:36:31 -04:00
|
|
|
SUCCEED_RETURN(e);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-02 13:15:26 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_arcade(akgl_PhysicsBackend *self)
|
2026-05-26 10:36:31 -04:00
|
|
|
{
|
|
|
|
|
akgl_String *tmp;
|
|
|
|
|
PREPARE_ERROR(e);
|
|
|
|
|
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
|
|
|
|
PASS(e, akgl_heap_next_string(&tmp));
|
|
|
|
|
|
2026-06-02 13:15:26 -04:00
|
|
|
self->gravity = akgl_physics_arcade_gravity;
|
|
|
|
|
self->collide = akgl_physics_arcade_collide;
|
|
|
|
|
self->move = akgl_physics_arcade_move;
|
2026-05-26 10:36:31 -04:00
|
|
|
self->simulate = akgl_physics_simulate;
|
|
|
|
|
|
|
|
|
|
ATTEMPT {
|
|
|
|
|
CATCH(e, akgl_get_property("physics.gravity.x", &tmp, "0.0"));
|
|
|
|
|
CATCH(e, aksl_atof(tmp->data, &self->gravity_x));
|
|
|
|
|
CATCH(e, akgl_get_property("physics.gravity.y", &tmp, "0.0"));
|
|
|
|
|
CATCH(e, aksl_atof(tmp->data, &self->gravity_y));
|
|
|
|
|
CATCH(e, akgl_get_property("physics.gravity.z", &tmp, "0.0"));
|
|
|
|
|
CATCH(e, aksl_atof(tmp->data, &self->gravity_z));
|
|
|
|
|
CATCH(e, akgl_get_property("physics.drag.x", &tmp, "0.0"));
|
|
|
|
|
CATCH(e, aksl_atof(tmp->data, &self->drag_x));
|
|
|
|
|
CATCH(e, akgl_get_property("physics.drag.y", &tmp, "0.0"));
|
|
|
|
|
CATCH(e, aksl_atof(tmp->data, &self->drag_y));
|
|
|
|
|
CATCH(e, akgl_get_property("physics.drag.z", &tmp, "0.0"));
|
|
|
|
|
CATCH(e, aksl_atof(tmp->data, &self->drag_z));
|
|
|
|
|
} CLEANUP {
|
|
|
|
|
IGNORE(akgl_heap_release_string(tmp));
|
|
|
|
|
} PROCESS(e) {
|
|
|
|
|
} FINISH(e, true);
|
|
|
|
|
|
|
|
|
|
SUCCEED_RETURN(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_simulate(akgl_PhysicsBackend *self, akgl_Iterator *opflags)
|
|
|
|
|
{
|
|
|
|
|
PREPARE_ERROR(e);
|
|
|
|
|
akgl_Iterator defflags = {
|
|
|
|
|
.flags = 0,
|
|
|
|
|
.layerid = 0
|
|
|
|
|
};
|
|
|
|
|
SDL_Time curtime = SDL_GetTicksNS();
|
2026-05-26 11:22:45 -04:00
|
|
|
float32_t dt = (float32_t)(curtime - self->gravity_time) / (float32_t)AKGL_TIME_ONESEC_NS;
|
2026-05-26 10:36:31 -04:00
|
|
|
akgl_Actor *actor = NULL;
|
|
|
|
|
|
|
|
|
|
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
|
|
|
|
FAIL_ZERO_RETURN(e, self->move, AKERR_NULLPOINTER, "self->move");
|
|
|
|
|
|
|
|
|
|
if ( opflags == NULL ) {
|
|
|
|
|
opflags = &defflags;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-26 11:22:45 -04:00
|
|
|
|
2026-05-26 10:36:31 -04:00
|
|
|
for ( int i = 0; i < AKGL_MAX_HEAP_ACTOR; i++ ) {
|
|
|
|
|
actor = &HEAP_ACTOR[i];
|
|
|
|
|
if ( actor->refcount == 0 ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-05-26 11:22:45 -04:00
|
|
|
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;
|
|
|
|
|
}
|
2026-05-26 10:36:31 -04:00
|
|
|
if ( AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_LAYERMASK) ) {
|
|
|
|
|
if ( actor->layer != opflags->layerid ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-26 11:22:45 -04:00
|
|
|
|
|
|
|
|
// thrust is a function of acceleration on a given axis
|
2026-05-26 10:36:31 -04:00
|
|
|
if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_LEFT) ||
|
|
|
|
|
AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_RIGHT) ) {
|
2026-05-26 11:22:45 -04:00
|
|
|
actor->tx += actor->ax * dt;
|
2026-05-26 10:36:31 -04:00
|
|
|
}
|
|
|
|
|
if ( AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_UP) ||
|
|
|
|
|
AKGL_BITMASK_HAS(actor->state, AKGL_ACTOR_STATE_MOVING_DOWN) ) {
|
2026-05-26 11:22:45 -04:00
|
|
|
actor->ty += actor->ay * dt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
2026-05-26 10:36:31 -04:00
|
|
|
}
|
2026-05-26 11:22:45 -04:00
|
|
|
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);
|
2026-05-26 10:36:31 -04:00
|
|
|
}
|
2026-05-26 11:22:45 -04:00
|
|
|
self->gravity_time = curtime;
|
2026-05-26 10:36:31 -04:00
|
|
|
SUCCEED_RETURN(e);
|
|
|
|
|
}
|