triggering beginnings.

This commit is contained in:
jacob 2024-08-09 22:27:02 -05:00
parent f5400b0205
commit 911208a926
4 changed files with 203 additions and 167 deletions

View File

@ -15,7 +15,8 @@ enum entity_prop {
ENTITY_PROP_EQUIPPER, ENTITY_PROP_EQUIPPER,
ENTITY_PROP_EQUIPABLE, ENTITY_PROP_EQUIPABLE,
ENTITY_PROP_FIRING_EQUIPPED, ENTITY_PROP_TRIGGERED,
ENTITY_PROP_TRIGGERING_EQUIPPED,
/* Test props */ /* Test props */
@ -62,6 +63,12 @@ struct entity {
struct entity_handle first; struct entity_handle first;
struct entity_handle last; struct entity_handle last;
/* ====================================================================== */
/* Allocation/Release timing */
u64 valid_tick; /* `valid` will be set to true when world tick >= `valid_tick` & `valid_tick` != 0 */
u64 release_tick; /* Entity will be released when world tick >= `release_tick` & `release_tick` != 0 */
/* ====================================================================== */ /* ====================================================================== */
/* Position */ /* Position */
@ -203,8 +210,6 @@ INLINE b32 entity_has_prop(struct entity *entity, enum entity_prop prop)
* Entity functions * Entity functions
* ========================== */ * ========================== */
INLINE struct entity_array entity_store_as_array(struct entity_store *store) { return (struct entity_array) { .count = store->count, .entities = store->entities }; }
/* Entity store */ /* Entity store */
struct entity_store *entity_store_alloc(void); struct entity_store *entity_store_alloc(void);
void entity_store_release(struct entity_store *store); void entity_store_release(struct entity_store *store);

View File

@ -188,8 +188,21 @@ INTERNAL void game_update(void)
* Begin frame * Begin frame
* ========================== */ * ========================== */
++G.world.tick_id;
G.world.tick_ts = sys_timestamp();
G.world.dt = max_f64(0.0, (1.0 / GAME_FPS) * G.world.timescale);
G.world.time += G.world.dt;
u64 world_tick = G.world.tick_id;
struct game_cmd_array game_cmds = pop_cmds(scratch.arena);
struct entity_store *store = G.world.entity_store;
struct entity *root = entity_from_handle(store, store->root);
struct sprite_scope *sprite_frame_scope = sprite_scope_begin(); struct sprite_scope *sprite_frame_scope = sprite_scope_begin();
/* ========================== *
* Spawn test entities
* ========================== */
/* TODO: remove this (testing) */ /* TODO: remove this (testing) */
/* Initialize entities */ /* Initialize entities */
static b32 run = 0; static b32 run = 0;
@ -198,20 +211,8 @@ INTERNAL void game_update(void)
spawn_test_entities(); spawn_test_entities();
} }
++G.world.tick_id;
G.world.tick_ts = sys_timestamp();
G.world.dt = max_f64(0.0, (1.0 / GAME_FPS) * G.world.timescale);
G.world.time += G.world.dt;
/* ========================== * /* ========================== *
* Pull cmds * Process pre-sim game cmds
* ========================== */
struct entity_store *store = G.world.entity_store;
struct game_cmd_array game_cmds = pop_cmds(scratch.arena);
/* ========================== *
* Pre-sim game cmds
* ========================== */ * ========================== */
for (u64 cmd_index = 0; cmd_index < game_cmds.count; ++cmd_index) { for (u64 cmd_index = 0; cmd_index < game_cmds.count; ++cmd_index) {
@ -239,18 +240,143 @@ INTERNAL void game_update(void)
}; };
} }
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */
struct entity *root = entity_from_handle(store, store->root);
struct entity_array entities_array = entity_store_as_array(store);
/* ========================== * /* ========================== *
* Update sprite * Make valid or release entities
* ========================== */ * ========================== */
for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { {
struct entity *ent = &entities_array.entities[entity_index]; u64 ents_to_release_count = 0;
struct entity **ents_to_release = arena_dry_push(scratch.arena, struct entity *);
for (u64 entity_index = 0; entity_index < store->count; ++entity_index) {
struct entity *ent = &store->entities[entity_index];
u64 valid_tick = ent->valid_tick;
u64 release_tick = ent->release_tick;
b32 valid = ent->valid;
if (valid_tick != 0 && valid_tick >= world_tick && !valid) {
ent->valid = true;
valid = true;
}
if (valid && release_tick != 0 && release_tick >= world_tick) {
*arena_push(scratch.arena, struct entity *) = ent;
++ents_to_release_count;
}
}
for (u64 i = 0; i < ents_to_release_count; ++i) {
struct entity *ent = ents_to_release[i];
entity_release(store, ent);
}
}
/* ========================== *
* Update entities from cmds
* ========================== */
for (u64 entity_index = 0; entity_index < store->count; ++entity_index) {
struct entity *ent = &store->entities[entity_index];
if (!ent->valid) continue;
/* ========================== *
* Initialize test
* ========================== */
/* ENTITY_PROP_TEST */
if (entity_has_prop(ent, ENTITY_PROP_TEST) && !ent->test_initialized) {
ent->test_initialized = true;
ent->test_start_local_xform = entity_get_local_xform(ent);
ent->test_start_sprite_xform = ent->sprite_local_xform;
}
/* ========================== *
* Update control from player cmds
* ========================== */
if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) {
/* Process cmds */
struct v2 move = ent->control.move;
struct v2 focus = ent->control.focus;
b32 firing = entity_has_prop(ent, ENTITY_PROP_TRIGGERING_EQUIPPED);
for (u64 i = 0; i < game_cmds.count; ++i) {
struct game_cmd cmd = game_cmds.cmds[i];
b32 start = cmd.state == GAME_CMD_STATE_START;
b32 stop = cmd.state == GAME_CMD_STATE_STOP;
(UNUSED)start;
(UNUSED)stop;
/* TODO: Combine movement from multiple inputs? E.G. a sudden
* start and immediate stop cmd should still move the player a
* tad. */
switch (cmd.kind) {
case GAME_CMD_KIND_PLAYER_MOVE: {
move = cmd.move_dir;
focus = v2_sub(cmd.aim_pos, entity_get_xform(ent).og);
} break;
case GAME_CMD_KIND_PLAYER_FIRE: {
if (start) {
firing = true;
} else if (stop) {
firing = false;
}
} break;
default: break;
}
}
/* Movement */
if (v2_len_squared(move) > 1) {
/* Cap movement vector magnitude at 1 */
move = v2_norm(move);
}
ent->control.move = move;
ent->control.focus = focus;
/* Firing */
if (firing) {
entity_enable_prop(ent, ENTITY_PROP_TRIGGERING_EQUIPPED);
} else {
entity_disable_prop(ent, ENTITY_PROP_TRIGGERING_EQUIPPED);
}
}
/* ========================== *
* Test
* ========================== */
/* ENTITY_PROP_TEST */
if (entity_has_prop(ent, ENTITY_PROP_TEST)) {
f32 t = ((f32)G.world.time);
struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3);
f32 r = t * 3;
struct v2 s = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1);
(UNUSED)og;
(UNUSED)r;
(UNUSED)s;
og = v2_add(og, ent->test_start_local_xform.og);
r += xform_get_rotation(ent->test_start_local_xform);
s = v2_add(s, xform_get_scale(ent->test_start_local_xform));
struct xform xf = entity_get_local_xform(ent);
xf.og = og;
xf= xform_with_rotation(xf, r);
xf= xform_with_scale(xf, s);
entity_set_local_xform(ent, xf);
}
}
/* ========================== *
* Update sprites
* ========================== */
for (u64 entity_index = 0; entity_index < store->count; ++entity_index) {
struct entity *ent = &store->entities[entity_index];
if (!ent->valid) continue; if (!ent->valid) continue;
if (sprite_tag_is_nil(ent->sprite)) continue; if (sprite_tag_is_nil(ent->sprite)) continue;
@ -301,66 +427,28 @@ INTERNAL void game_update(void)
ent->sprite_local_xform = xf; ent->sprite_local_xform = xf;
} }
/* ========================== * /* ========================== *
* Update entities pre-simulation * Update triggering
* ========================== */ * ========================== */
for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { for (u64 entity_index = 0; entity_index < store->count; ++entity_index) {
struct entity *ent = &entities_array.entities[entity_index]; struct entity *ent = &store->entities[entity_index];
if (!ent->valid) continue;
if (entity_has_prop(ent, ENTITY_PROP_TRIGGERED)) {
}
}
/* ========================== *
* Simulate entities
* ========================== */
for (u64 entity_index = 0; entity_index < store->count; ++entity_index) {
struct entity *ent = &store->entities[entity_index];
if (!ent->valid) continue; if (!ent->valid) continue;
/* ========================== * /* ========================== *
* Initialize test * Player angle
* ========================== */
/* ENTITY_PROP_TEST */
if (entity_has_prop(ent, ENTITY_PROP_TEST) && !ent->test_initialized) {
ent->test_initialized = true;
ent->test_start_local_xform = entity_get_local_xform(ent);
ent->test_start_sprite_xform = ent->sprite_local_xform;
}
/* ========================== *
* Update control from player cmds
* ========================== */
if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) {
for (u64 i = 0; i < game_cmds.count; ++i) {
struct game_cmd cmd = game_cmds.cmds[i];
b32 start = cmd.state == 1;
b32 stop = cmd.state == -1;
/* TODO: Combine movement from multiple inputs? E.G. a sudden
* start and immediate stop cmd should still move the player a
* tad. */
switch (cmd.kind) {
case GAME_CMD_KIND_PLAYER_MOVE: {
struct v2 move = cmd.move_dir;
if (v2_len_squared(move) > 1) {
/* Cap movement vector magnitude at 1 */
move = v2_norm(move);
}
ent->control.move = move;
ent->control.focus = v2_sub(cmd.aim_pos, entity_get_xform(ent).og);
} break;
case GAME_CMD_KIND_PLAYER_FIRE: {
if (start) {
entity_enable_prop(ent, ENTITY_PROP_FIRING_EQUIPPED);
} else if (stop) {
entity_disable_prop(ent, ENTITY_PROP_FIRING_EQUIPPED);
}
} break;
default: break;
}
}
}
/* ========================== *
* Update player angle
* ========================== */ * ========================== */
if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) {
@ -417,54 +505,7 @@ INTERNAL void game_update(void)
} }
/* ========================== * /* ========================== *
* Test * Player movement
* ========================== */
/* ENTITY_PROP_TEST */
if (entity_has_prop(ent, ENTITY_PROP_TEST)) {
f32 t = ((f32)G.world.time);
struct v2 og = v2_mul(V2(math_cos(t), math_sin(t)), 3);
f32 r = t * 3;
struct v2 s = V2(1 + (math_fabs(math_sin(t * 5)) * 3), 1);
(UNUSED)og;
(UNUSED)r;
(UNUSED)s;
og = v2_add(og, ent->test_start_local_xform.og);
r += xform_get_rotation(ent->test_start_local_xform);
s = v2_add(s, xform_get_scale(ent->test_start_local_xform));
struct xform xf = entity_get_local_xform(ent);
xf.og = og;
xf= xform_with_rotation(xf, r);
xf= xform_with_scale(xf, s);
entity_set_local_xform(ent, xf);
}
/* ENTITY_PROP_FIRING_EQUIPPED */
{
struct entity *eq = entity_from_handle(store, ent->equipped);
if (eq->valid) {
if (entity_has_prop(ent, ENTITY_PROP_FIRING_EQUIPPED)) {
eq->sprite_tint = RGBA_32_F(1, 0, 0, 1);
} else {
eq->sprite_tint = RGBA_32_F(1, 1, 1, 1);
}
}
}
}
/* ========================== *
* Simulate entities
* ========================== */
for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) {
struct entity *ent = &entities_array.entities[entity_index];
if (!ent->valid) continue;
/* ========================== *
* Player control
* ========================== */ * ========================== */
if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) {
@ -498,8 +539,8 @@ INTERNAL void game_update(void)
* Update entities post-simulation * Update entities post-simulation
* ========================== */ * ========================== */
for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { for (u64 entity_index = 0; entity_index < store->count; ++entity_index) {
struct entity *ent = &entities_array.entities[entity_index]; struct entity *ent = &store->entities[entity_index];
if (!ent->valid) continue; if (!ent->valid) continue;
/* ========================== * /* ========================== *

View File

@ -6,6 +6,12 @@ struct mixer_startup_receipt;
struct sprite_startup_receipt; struct sprite_startup_receipt;
struct sound_startup_receipt; struct sound_startup_receipt;
enum game_cmd_state {
GAME_CMD_STATE_STOP = -1,
GAME_CMD_STATE_NO_CHANGE = 0,
GAME_CMD_STATE_START = 1
};
enum game_cmd_kind { enum game_cmd_kind {
GAME_CMD_KIND_NONE, GAME_CMD_KIND_NONE,
@ -21,9 +27,7 @@ enum game_cmd_kind {
struct game_cmd { struct game_cmd {
enum game_cmd_kind kind; enum game_cmd_kind kind;
enum game_cmd_state state;
/* 1 = start, -1 = stop */
i32 state;
/* GAME_CMD_KIND_PLAYER_MOVE */ /* GAME_CMD_KIND_PLAYER_MOVE */
struct v2 move_dir; struct v2 move_dir;

View File

@ -394,23 +394,21 @@ INTERNAL void debug_draw_movement(struct entity *ent)
INTERNAL void user_update(void) INTERNAL void user_update(void)
{ {
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
struct game_cmd_list cmd_list = {
.arena = scratch.arena
};
struct entity_store *store = G.world.entity_store; /* ========================== *
* Begin frame
* ========================== */
/* Get time */
f64 cur_time = sys_timestamp_seconds(sys_timestamp()); f64 cur_time = sys_timestamp_seconds(sys_timestamp());
G.dt = max_f64(0.0, cur_time - G.time); G.dt = max_f64(0.0, cur_time - G.time);
G.time += G.dt; G.time += G.dt;
G.screen_size = sys_window_get_size(G.window); G.screen_size = sys_window_get_size(G.window);
/* ========================== * struct entity_store *store = G.world.entity_store;
* Begin frame cache scopes
* ========================== */
struct sprite_scope *sprite_frame_scope = sprite_scope_begin(); struct sprite_scope *sprite_frame_scope = sprite_scope_begin();
struct game_cmd_list cmd_list = {
.arena = scratch.arena
};
/* ========================== * /* ========================== *
* Produce interpolated tick * Produce interpolated tick
@ -450,17 +448,13 @@ INTERNAL void user_update(void)
G.world.time = math_lerp64(t0->time, t1->time, (f64)tick_blend); G.world.time = math_lerp64(t0->time, t1->time, (f64)tick_blend);
/* Blend entities */ /* Blend entities */
struct entity_array t0_entities = entity_store_as_array(t0->entity_store); u64 num_entities = min_u64(t0->entity_store->count, t1->entity_store->count);
struct entity_array t1_entities = entity_store_as_array(t1->entity_store);
struct entity_array world_entities = entity_store_as_array(store);
u64 num_entities = min_u64(t0_entities.count, t1_entities.count);
{ {
__profscope(tick_blending); __profscope(tick_blending);
for (u64 i = 0; i < num_entities; ++i) { for (u64 i = 0; i < num_entities; ++i) {
struct entity *e0 = &t0_entities.entities[i]; struct entity *e0 = &t0->entity_store->entities[i];
struct entity *e1 = &t1_entities.entities[i]; struct entity *e1 = &t1->entity_store->entities[i];
struct entity *e = &world_entities.entities[i]; struct entity *e = &store->entities[i];
ASSERT(e->cached_global_xform_dirty == false); /* Game thread should have cached all global xforms before publishing */ ASSERT(e->cached_global_xform_dirty == false); /* Game thread should have cached all global xforms before publishing */
if (e0->handle.gen == e1->handle.gen && e0->continuity_gen == e1->continuity_gen) { if (e0->handle.gen == e1->handle.gen && e0->continuity_gen == e1->continuity_gen) {
e->local_xform = xform_lerp(e0->local_xform, e1->local_xform, tick_blend); e->local_xform = xform_lerp(e0->local_xform, e1->local_xform, tick_blend);
@ -489,8 +483,6 @@ INTERNAL void user_update(void)
#endif #endif
} }
struct entity_array entities_array = entity_store_as_array(store);
/* ========================== * /* ========================== *
* Find important entities * Find important entities
* ========================== */ * ========================== */
@ -768,10 +760,10 @@ INTERNAL void user_update(void)
* ========================== */ * ========================== */
/* Iterate entities */ /* Iterate entities */
for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) { for (u64 entity_index = 0; entity_index < store->count; ++entity_index) {
__profscope(user_entity_iter); __profscope(user_entity_iter);
struct entity *ent = &entities_array.entities[entity_index]; struct entity *ent = &store->entities[entity_index];
if (!ent->valid) continue; if (!ent->valid) continue;
if (ent->is_root) continue; if (ent->is_root) continue;
@ -954,8 +946,9 @@ INTERNAL void user_update(void)
* Construct player control cmd * Construct player control cmd
* ========================== */ * ========================== */
/* Movement */
{ {
/* Queue player move cmd */
struct v2 input_move_dir = { 0 }; struct v2 input_move_dir = { 0 };
{ {
for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(G.bind_states); ++bind) { for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(G.bind_states); ++bind) {
@ -990,27 +983,20 @@ INTERNAL void user_update(void)
input_move_dir = xform_basis_invert_mul_v2(G.world_view, input_move_dir); /* Make move dir relative to world view */ input_move_dir = xform_basis_invert_mul_v2(G.world_view, input_move_dir); /* Make move dir relative to world view */
input_move_dir = v2_norm(input_move_dir); input_move_dir = v2_norm(input_move_dir);
} }
struct v2 input_aim_pos = G.world_cursor; struct v2 input_aim_pos = G.world_cursor;
i32 cmd_fire = (G.bind_states[USER_BIND_KIND_FIRE].num_presses > 0 || G.bind_states[USER_BIND_KIND_FIRE].is_held) ? 1 : -1;
if (!G.debug_camera) { if (!G.debug_camera) {
/* Queue player move cmd */
queue_game_cmd(&cmd_list, (struct game_cmd) { queue_game_cmd(&cmd_list, (struct game_cmd) {
.kind = GAME_CMD_KIND_PLAYER_MOVE, .kind = GAME_CMD_KIND_PLAYER_MOVE,
.move_dir = input_move_dir, .move_dir = input_move_dir,
.aim_pos = input_aim_pos .aim_pos = input_aim_pos
}); });
/* Queue player fire cmd */
if (cmd_fire) {
queue_game_cmd(&cmd_list, (struct game_cmd) {
.kind = GAME_CMD_KIND_PLAYER_FIRE,
.state = cmd_fire
});
}
} }
/* Queue player fire cmd */
queue_game_cmd(&cmd_list, (struct game_cmd) {
.kind = GAME_CMD_KIND_PLAYER_FIRE,
.state = (G.bind_states[USER_BIND_KIND_FIRE].num_presses > 0 || G.bind_states[USER_BIND_KIND_FIRE].is_held) ? GAME_CMD_STATE_START : GAME_CMD_STATE_STOP
});
} }
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */