code: add vehicle handling

isolation_bkp/dynres
Dominik Madarász 2021-08-09 20:58:52 +02:00
parent d1016f68a0
commit e00b8d7e5f
10 changed files with 88 additions and 9 deletions

View File

@ -22,6 +22,9 @@ pkt_desc pkt_entity_view_desc[] = {
{ PKT_HALF(entity_view, hp) }, { PKT_HALF(entity_view, hp) },
{ PKT_HALF(entity_view, max_hp) }, { PKT_HALF(entity_view, max_hp) },
{ PKT_KEEP_IF(entity_view, kind, EKIND_VEHICLE, 1) }, // NOTE(zaklaus): keep for vehicles
{ PKT_HALF(entity_view, heading) },
{ PKT_END }, { PKT_END },
}; };

View File

@ -48,6 +48,9 @@ typedef struct entity_view {
uint8_t is_dirty; uint8_t is_dirty;
int64_t tex; int64_t tex;
// NOTE(zaklaus): vehicle
float heading, theading;
// NOTE(zaklaus): internals // NOTE(zaklaus): internals
uint8_t layer_id; uint8_t layer_id;
uint64_t last_update; uint64_t last_update;

View File

@ -115,6 +115,7 @@ void lerp_entity_positions(uint64_t key, entity_view * data);
void do_entity_fadeinout(uint64_t key, entity_view * data); void do_entity_fadeinout(uint64_t key, entity_view * data);
float zpl_lerp(float,float,float); float zpl_lerp(float,float,float);
float zpl_to_degrees(float);
void platform_render() { void platform_render() {
profile(PROF_ENTITY_LERP) { profile(PROF_ENTITY_LERP) {
@ -220,9 +221,9 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) {
case EKIND_VEHICLE: { case EKIND_VEHICLE: {
float x = data->x; float x = data->x;
float y = data->y; float y = data->y;
float const w = 50; float const w = 80;
float const h = 80; float const h = 50;
DrawRectanglePro((Rectangle){x,y,w,h}, (Vector2){w/2.0f,h/2.0f}, 0.0f, ColorAlpha(RED, data->tran_time)); DrawRectanglePro((Rectangle){x,y,w,h}, (Vector2){w/2.0f,h/2.0f}, zpl_to_degrees(data->heading), ColorAlpha(RED, data->tran_time));
}break; }break;
default:break; default:break;
} }
@ -237,9 +238,11 @@ void lerp_entity_positions(uint64_t key, entity_view *data) {
#if 1 #if 1
data->x = smooth_val(data->x, data->tx, view->delta_time[data->layer_id]); data->x = smooth_val(data->x, data->tx, view->delta_time[data->layer_id]);
data->y = smooth_val(data->y, data->ty, view->delta_time[data->layer_id]); data->y = smooth_val(data->y, data->ty, view->delta_time[data->layer_id]);
data->heading = smooth_val_spherical(data->heading, data->theading, view->delta_time[data->layer_id]);
#else #else
data->x = data->tx; data->x = data->tx;
data->y = data->ty; data->y = data->ty;
data->heading = data->theading;
#endif #endif
} }
} }

View File

@ -11,6 +11,26 @@ static inline float map_factor(float x) {
return 1.0f - x*x*x*x*x*x*x*x; return 1.0f - x*x*x*x*x*x*x*x;
} }
static inline float base_angle(float x) {
while (x > ZPL_TAU) x -= ZPL_TAU;
while (x < 0.0f) x += ZPL_TAU;
return x;
}
static inline float spherical_lerp(float a, float b, float t) {
a = base_angle(a);
b = base_angle(b);
float d = b - a;
if (d < -ZPL_PI) {
b += ZPL_TAU;
} else if (d > ZPL_PI) {
b -= ZPL_TAU;
}
return base_angle(zpl_lerp(a, b, t));
}
float smooth_val(float cur, float tgt, uint64_t dt) { float smooth_val(float cur, float tgt, uint64_t dt) {
float factor = zpl_clamp01(map_factor(zpl_unlerp(dt, WORLD_TRACKER_UPDATE_FAST_MS, WORLD_TRACKER_UPDATE_SLOW_MS))); float factor = zpl_clamp01(map_factor(zpl_unlerp(dt, WORLD_TRACKER_UPDATE_FAST_MS, WORLD_TRACKER_UPDATE_SLOW_MS)));
@ -24,15 +44,24 @@ float smooth_val(float cur, float tgt, uint64_t dt) {
return zpl_lerp(cur, tgt, zpl_lerp(PREDICT_SMOOTH_FACTOR_LO, PREDICT_SMOOTH_FACTOR_HI, factor)); return zpl_lerp(cur, tgt, zpl_lerp(PREDICT_SMOOTH_FACTOR_LO, PREDICT_SMOOTH_FACTOR_HI, factor));
} }
float smooth_val_spherical(float cur, float tgt, uint64_t dt) {
float factor = zpl_clamp01(map_factor(zpl_unlerp(dt, WORLD_TRACKER_UPDATE_FAST_MS, WORLD_TRACKER_UPDATE_SLOW_MS)));
return spherical_lerp(cur, tgt, zpl_lerp(PREDICT_SMOOTH_FACTOR_LO, PREDICT_SMOOTH_FACTOR_HI, factor));
}
void predict_receive_update(entity_view *d, entity_view *data) { void predict_receive_update(entity_view *d, entity_view *data) {
if (d && data->flag & EFLAG_INTERP) { if (d && data->flag & EFLAG_INTERP) {
// NOTE(zaklaus): store target pos but keep x,y unchanged // NOTE(zaklaus): store target pos but keep x,y unchanged
float tx = data->x; float tx = data->x;
float ty = data->y; float ty = data->y;
float theading = data->heading;
data->x = d->x; data->x = d->x;
data->y = d->y; data->y = d->y;
data->heading = d->heading;
data->tx = tx; data->tx = tx;
data->ty = ty; data->ty = ty;
data->theading = theading;
} }
data->tran_effect = d->tran_effect; data->tran_effect = d->tran_effect;

View File

@ -3,3 +3,4 @@
float smooth_val(float cur, float tgt, uint64_t dt); float smooth_val(float cur, float tgt, uint64_t dt);
void predict_receive_update(entity_view *d, entity_view *data); void predict_receive_update(entity_view *d, entity_view *data);
float smooth_val_spherical(float cur, float tgt, uint64_t dt);

View File

@ -8,7 +8,9 @@
uint64_t vehicle_spawn(void) { uint64_t vehicle_spawn(void) {
ecs_entity_t e = entity_spawn(EKIND_VEHICLE); ecs_entity_t e = entity_spawn(EKIND_VEHICLE);
ecs_set(world_ecs(), e, Vehicle, {0}); Vehicle *veh = ecs_get_mut(world_ecs(), e, Vehicle, NULL);
zpl_zero_item(veh);
veh->wheel_base = 60.0f;
return (uint64_t)e; return (uint64_t)e;
} }

View File

@ -45,6 +45,11 @@ entity_view world_build_entity_view(int64_t e) {
view.max_hp = health->max_hp; view.max_hp = health->max_hp;
} }
if (ecs_get(world_ecs(), e, Vehicle)) {
Vehicle const* veh = ecs_get(world_ecs(), e, Vehicle);
view.heading = veh->heading;
}
if (ecs_get(world_ecs(), e, Chunk)) { if (ecs_get(world_ecs(), e, Chunk)) {
Chunk *chpos = ecs_get_mut(world_ecs(), e, Chunk, 0); Chunk *chpos = ecs_get_mut(world_ecs(), e, Chunk, 0);
view.x = chpos->x; view.x = chpos->x;

View File

@ -59,6 +59,7 @@ int32_t tracker_read_create(librg_world *w, librg_event *e) {
if (data.flag & EFLAG_INTERP) { if (data.flag & EFLAG_INTERP) {
data.tx = data.x; data.tx = data.x;
data.ty = data.y; data.ty = data.y;
data.theading = data.heading;
} }
entity_view_update_or_create(&view->entities, entity_id, data); entity_view_update_or_create(&view->entities, entity_id, data);
entity_view_mark_for_fadein(&view->entities, entity_id); entity_view_mark_for_fadein(&view->entities, entity_id);

View File

@ -51,6 +51,11 @@ ECS_STRUCT(Classify, {
ECS_STRUCT(Vehicle, { ECS_STRUCT(Vehicle, {
uint64_t seats[4]; uint64_t seats[4];
float speed;
float heading;
float steer;
float wheel_base;
}); });
ECS_COMPONENT_EXTERN(Chunk); ECS_COMPONENT_EXTERN(Chunk);

View File

@ -201,12 +201,35 @@ void EnterOrLeaveVehicle(ecs_iter_t *it) {
} }
} }
#define VEHICLE_MAX_SPEED 500.0f
#define VEHICLE_ACCEL 2.1f
#define VEHICLE_STEER 0.01f
void VehicleHandling(ecs_iter_t *it) { void VehicleHandling(ecs_iter_t *it) {
Vehicle *veh = ecs_column(it, Vehicle, 1); Vehicle *veh = ecs_column(it, Vehicle, 1);
Position *p = ecs_column(it, Position, 2); Position *p = ecs_column(it, Position, 2);
Velocity *v = ecs_column(it, Velocity, 3); Velocity *v = ecs_column(it, Velocity, 3);
for (int i = 0; i < it->count; i++) { for (int i = 0; i < it->count; i++) {
Vehicle *car = &veh[i];
car->speed *= 0.99f;
car->steer *= 0.97f;
float fr_x = p[i].x + (car->wheel_base/2.0f) * zpl_cos(car->heading);
float fr_y = p[i].y + (car->wheel_base/2.0f) * zpl_sin(car->heading);
float bk_x = p[i].x - (car->wheel_base/2.0f) * zpl_cos(car->heading);
float bk_y = p[i].y - (car->wheel_base/2.0f) * zpl_sin(car->heading);
bk_x += car->speed * zpl_cos(car->heading);
bk_y += car->speed * zpl_sin(car->heading);
fr_x += car->speed * zpl_cos(car->heading + zpl_to_radians(car->steer));
fr_y += car->speed * zpl_sin(car->heading + zpl_to_radians(car->steer));
v[i].x = (fr_x + bk_x) / 2.0f - p[i].x;
v[i].y = (fr_y + bk_y) / 2.0f - p[i].y;
car->heading = zpl_arctan2(fr_y - bk_y, fr_x - bk_x);
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
// NOTE(zaklaus): Perform seat cleanup // NOTE(zaklaus): Perform seat cleanup
if (!ecs_is_alive(world_ecs(), veh[i].seats[j])) { if (!ecs_is_alive(world_ecs(), veh[i].seats[j])) {
@ -219,15 +242,19 @@ void VehicleHandling(ecs_iter_t *it) {
// NOTE(zaklaus): Update passenger position // NOTE(zaklaus): Update passenger position
{ {
Position *p2 = ecs_get_mut(world_ecs(), pe, Position, NULL); Position *p2 = ecs_get_mut(world_ecs(), pe, Position, NULL);
Velocity *v2 = ecs_get_mut(world_ecs(), pe, Velocity, NULL);
*p2 = p[i]; *p2 = p[i];
*v2 = v[i];
} }
// NOTE(zaklaus): Handle driver input // NOTE(zaklaus): Handle driver input
if (j == 0) { if (j == 0) {
// TODO(zaklaus): Be lazy about it for now, implement wheels later
Input const* in = ecs_get(world_ecs(), pe, Input); Input const* in = ecs_get(world_ecs(), pe, Input);
v[i].x += in->x;
v[i].y += in->y; car->speed += in->y * VEHICLE_ACCEL;
car->speed = zpl_clamp(car->speed, -VEHICLE_MAX_SPEED, VEHICLE_MAX_SPEED);
car->steer += in->x * -VEHICLE_STEER * (zpl_abs(car->speed*2.5f) / VEHICLE_MAX_SPEED);
car->steer = zpl_clamp(car->steer, -40.0f, 40.0f);
} }
} }
} }
@ -242,7 +269,7 @@ void SystemsImport(ecs_world_t *ecs) {
ECS_SYSTEM(ecs, DemoNPCMoveAround, EcsOnLoad, components.Velocity, components.EcsDemoNPC); ECS_SYSTEM(ecs, DemoNPCMoveAround, EcsOnLoad, components.Velocity, components.EcsDemoNPC);
ECS_SYSTEM(ecs, EnterOrLeaveVehicle, EcsOnLoad, components.Input, components.Position); ECS_SYSTEM(ecs, EnterOrLeaveVehicle, EcsOnLoad, components.Input, components.Position);
ECS_SYSTEM(ecs, MoveWalk, EcsOnUpdate, components.Position, components.Velocity); ECS_SYSTEM(ecs, MoveWalk, EcsOnUpdate, components.Position, components.Velocity, !components.Vehicle);
ECS_SYSTEM(ecs, HurtOnHazardBlock, EcsOnUpdate, components.Position, components.Health); ECS_SYSTEM(ecs, HurtOnHazardBlock, EcsOnUpdate, components.Position, components.Health);
ECS_SYSTEM(ecs, RegenerateHP, EcsOnUpdate, components.Health); ECS_SYSTEM(ecs, RegenerateHP, EcsOnUpdate, components.Health);
ECS_SYSTEM(ecs, VehicleHandling, EcsOnUpdate, components.Vehicle, components.Position, components.Velocity); ECS_SYSTEM(ecs, VehicleHandling, EcsOnUpdate, components.Vehicle, components.Position, components.Velocity);