From 38075e5efdbb62459f61174c25c7b65859955b7f Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 14 Aug 2024 18:22:06 -0500 Subject: [PATCH] entity_prop_kinematic --- res/graphics/bullet.ase | 2 +- src/entity.c | 51 ++++----- src/entity.h | 19 +++- src/game.c | 224 ++++++++++++++++++++++------------------ src/user.c | 2 + 5 files changed, 169 insertions(+), 129 deletions(-) diff --git a/res/graphics/bullet.ase b/res/graphics/bullet.ase index 329b2c96..0a86df4e 100644 --- a/res/graphics/bullet.ase +++ b/res/graphics/bullet.ase @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b92f866a624fbf8c5305fb140fbf5c689d839900683a8cfc677003e9a5953a15 +oid sha256:e8809d664eca72331f6e31e0a32aaf17fd70df1f17067b2262b8c290cf78b8df size 419 diff --git a/src/entity.c b/src/entity.c index 5c373bf6..f22afc09 100644 --- a/src/entity.c +++ b/src/entity.c @@ -218,17 +218,20 @@ INTERNAL void entity_mark_child_xforms_dirty(struct entity_store *store, struct INTERNAL struct xform entity_get_xform_w_store(struct entity_store *store, struct entity *ent) { struct xform xf; - if (ent->is_top) { - xf = ent->local_xform; - } else if (!ent->cached_global_xform_dirty) { - xf = ent->cached_global_xform; - } else { - struct entity_handle parent_handle = ent->parent; - struct entity *parent = entity_from_handle(store, parent_handle); - xf = entity_get_xform_w_store(store, parent); - xf = xform_mul(xf, ent->local_xform); + if (ent->cached_global_xform_dirty) { + if (ent->is_top) { + xf = ent->local_xform; + } else { + struct entity *parent = entity_from_handle(store, ent->parent); + xf = entity_get_xform_w_store(store, parent); + xf = xform_mul(xf, ent->local_xform); + ent->cached_global_xform = xf; + ent->cached_global_xform_dirty = false; + } ent->cached_global_xform = xf; ent->cached_global_xform_dirty = false; + } else { + xf = ent->cached_global_xform; } return xf; } @@ -236,17 +239,21 @@ INTERNAL struct xform entity_get_xform_w_store(struct entity_store *store, struc struct xform entity_get_xform(struct entity *ent) { struct xform xf; - if (ent->is_top) { - xf = ent->local_xform; - } else if (!ent->cached_global_xform_dirty) { - xf = ent->cached_global_xform; - } else { - struct entity_store *store = entity_get_store(ent); - struct entity *parent = entity_from_handle(store, ent->parent); - xf = entity_get_xform_w_store(store, parent); - xf = xform_mul(xf, ent->local_xform); + if (ent->cached_global_xform_dirty) { + if (ent->is_top) { + xf = ent->local_xform; + } else { + struct entity_store *store = entity_get_store(ent); + struct entity *parent = entity_from_handle(store, ent->parent); + xf = entity_get_xform_w_store(store, parent); + xf = xform_mul(xf, ent->local_xform); + ent->cached_global_xform = xf; + ent->cached_global_xform_dirty = false; + } ent->cached_global_xform = xf; ent->cached_global_xform_dirty = false; + } else { + xf = ent->cached_global_xform; } return xf; } @@ -259,18 +266,16 @@ struct xform entity_get_local_xform(struct entity *ent) void entity_set_xform(struct entity *ent, struct xform xf) { struct entity_store *store = entity_get_store(ent); + /* Update local xform */ if (ent->is_top) { ent->local_xform = xf; } else { - /* Update child local xform */ struct entity *parent = entity_from_handle(store, ent->parent); struct xform parent_global = entity_get_xform_w_store(store, parent); ent->local_xform = xform_mul(xform_invert(parent_global), xf); - - /* Set child global xform */ - ent->cached_global_xform = xf; - ent->cached_global_xform_dirty = false; } + ent->cached_global_xform = xf; + ent->cached_global_xform_dirty = false; entity_mark_child_xforms_dirty(store, ent); } diff --git a/src/entity.h b/src/entity.h index 85f65034..7fa59ab3 100644 --- a/src/entity.h +++ b/src/entity.h @@ -8,18 +8,22 @@ enum entity_prop { ENTITY_PROP_NONE, ENTITY_PROP_ACTIVE, - ENTITY_PROP_RELEASE_AT_END_OF_TICK, + + ENTITY_PROP_RELEASE_NEXT_TICK, + + ENTITY_PROP_KINEMATIC, ENTITY_PROP_PLAYER_CONTROLLED, ENTITY_PROP_CAMERA, ENTITY_PROP_CAMERA_ACTIVE, - ENTITY_PROP_BULLET, ENTITY_PROP_WEAPON, ENTITY_PROP_TRIGGERING_EQUIPPED, ENTITY_PROP_TRIGGERED_THIS_TICK, ENTITY_PROP_TRIGGER_NEXT_TICK, + ENTITY_PROP_BULLET, + /* Test props */ ENTITY_PROP_TEST, @@ -66,12 +70,18 @@ struct entity { struct entity_handle first; struct entity_handle last; + /* ====================================================================== */ + /* Activation */ + + /* If 0, the entity will auto activate at start of next tick if not already active. */ + u64 activation_tick; + /* ====================================================================== */ /* Position */ /* Access with xform getters/setters */ - struct xform local_xform; /* Transform in relation to parent entity (or the world if entity has no parent) */ - struct xform cached_global_xform; /* Calculated from entity tree */ + struct xform local_xform; /* Transform in relation to parent entity (or the world if entity has no parent) */ + struct xform cached_global_xform; /* Calculated from entity tree */ b32 cached_global_xform_dirty; /* ====================================================================== */ @@ -92,6 +102,7 @@ struct entity { /* ====================================================================== */ /* Physics */ + /* ENTITY_PROP_KINEMATIC */ struct v2 acceleration; struct v2 velocity; diff --git a/src/game.c b/src/game.c index d8ae3170..b809c2f5 100644 --- a/src/game.c +++ b/src/game.c @@ -129,7 +129,7 @@ INTERNAL void spawn_test_entities(void) player_ent = e; - //entity_enable_prop(e, ENTITY_PROP_TEST); + entity_enable_prop(e, ENTITY_PROP_KINEMATIC); } /* Weapon */ @@ -156,6 +156,8 @@ INTERNAL void spawn_test_entities(void) e->trigger_delay = 1.0 / 10.0; player_ent->equipped = e->handle; + + //entity_enable_prop(e, ENTITY_PROP_KINEMATIC); } /* Camera */ @@ -246,6 +248,35 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) }; } + /* ========================== * + * Release entities + * ========================== */ + + /* TODO: Breadth first iteration to only release parent entities (since + * child entities will be released along with parent anyway) */ + + { + struct temp_arena temp = arena_temp_begin(scratch.arena); + + struct entity **ents_to_release = arena_dry_push(temp.arena, struct entity *); + u64 ents_to_release_count = 0; + for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + if (!ent->valid) continue; + + if (entity_has_prop(ent, ENTITY_PROP_RELEASE_NEXT_TICK)) { + *arena_push(temp.arena, struct entity *) = ent; + ++ents_to_release_count; + } + } + + for (u64 i = 0; i < ents_to_release_count; ++i) { + entity_release(store, ents_to_release[i]); + } + + arena_temp_end(temp); + } + /* ========================== * * Activate entities * ========================== */ @@ -255,8 +286,11 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) if (!ent->valid) continue; if (!entity_has_prop(ent, ENTITY_PROP_ACTIVE)) { - entity_enable_prop(ent, ENTITY_PROP_ACTIVE); - ++ent->continuity_gen; + if (ent->activation_tick == 0 || G.tick.tick_id >= ent->activation_tick) { + entity_enable_prop(ent, ENTITY_PROP_ACTIVE); + ent->activation_tick = G.tick.tick_id; + ++ent->continuity_gen; + } } } @@ -424,6 +458,91 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) } } + /* ========================== * + * Activate triggers + * ========================== */ + + for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; + + /* Trigger equipped */ + if (entity_has_prop(ent, ENTITY_PROP_TRIGGERING_EQUIPPED)) { + struct entity *eq = entity_from_handle(store, ent->equipped); + if (eq->valid) { + entity_enable_prop(eq, ENTITY_PROP_TRIGGERED_THIS_TICK); + } + } + + } + + /* ========================== * + * Fire triggers + * ========================== */ + + for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { + struct entity *ent = &store->entities[entity_index]; + if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; + if (!entity_has_prop(ent, ENTITY_PROP_TRIGGERED_THIS_TICK)) continue; + if ((time - ent->last_triggered < ent->trigger_delay) && ent->last_triggered != 0) continue; + + ent->last_triggered = time; + + /* Fire weapon */ + if (entity_has_prop(ent, ENTITY_PROP_WEAPON)) { + struct xform xf = entity_get_xform(ent); + + struct sprite_tag sprite = ent->sprite; + u32 animation_frame = ent->animation_frame; + struct xform sprite_xform = xform_mul(xf, ent->sprite_local_xform); + struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, sprite); + + struct sprite_sheet_slice out_slice = sprite_sheet_get_slice(sheet, STR("out"), animation_frame); + struct v2 out_pos = xform_mul_v2(sprite_xform, out_slice.center); + struct v2 out_vec = xform_basis_mul_v2(sprite_xform, out_slice.dir); + out_vec = v2_norm(out_vec); + out_vec = v2_mul(out_vec, 25); + + { + /* FIXME: Weapon velocity should affect bullet velocity + * (This should also make bullet_src_ent unnecessary for + * drawing bullet trails) */ + +#if 1 + /* Spawn bullet */ + struct entity *bullet = entity_alloc(root); + bullet->sprite = sprite_tag_from_path(STR("res/graphics/bullet.ase")); + struct xform bullet_xf = XFORM_TRS(.t = out_pos, .r = v2_angle(out_vec) + PI / 2); + entity_set_xform(bullet, bullet_xf); + bullet->velocity = out_vec; + bullet->bullet_src = ent->handle; + entity_enable_prop(bullet, ENTITY_PROP_BULLET); +#elif 1 + /* TODO: Remove this (testing) */ + struct v2 barrel_velocity = entity_from_handle(store, ent->parent)->velocity; + + /* Create bullet */ + /* TODO: Move bullet infront of barrel by hitbox */ + struct entity *bullet = entity_alloc(root); + bullet->sprite = sprite_tag_from_path(STR("res/graphics/bullet.ase")); + struct xform bullet_xf = XFORM_TRS(.t = out_pos, .r = v2_angle(out_vec) + PI / 2); + entity_set_xform(bullet, bullet_xf); + entity_enable_prop(bullet, ENTITY_PROP_BULLET); + bullet->bullet_src = ent->handle; + bullet->activation_tick = G.tick.tick_id + 1; + bullet->velocity = barrel_velocity; + + /* Create impulse */ + struct entity *impulse = entity_alloc(root); + entity_enable_prop(impulse, ENTITY_PROP_IMPULSE); + impulse->impulse_target = bullet->handle; + impulse->impulse_vec = out_vec; + impulse->activation_tick = bullet->activation_tick + 1; /* Ensure bullet exists for 1 tick at the start of the barrel before impulse is applied */ +#endif + } + } + } + /* ========================== * * Simulate entity physics * ========================== */ @@ -500,7 +619,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) /* Integrate acceleration & velocity */ - { + if (entity_has_prop(ent, ENTITY_PROP_KINEMATIC)) { f32 dt = (f32)G.tick.dt; /* Apply acceleration to velocity */ @@ -514,74 +633,6 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) } } - /* ========================== * - * Activate triggers - * ========================== */ - - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; - if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; - - /* Trigger equipped */ - if (entity_has_prop(ent, ENTITY_PROP_TRIGGERING_EQUIPPED)) { - struct entity *eq = entity_from_handle(store, ent->equipped); - if (entity_has_prop(eq, ENTITY_PROP_ACTIVE)) { - entity_enable_prop(eq, ENTITY_PROP_TRIGGERED_THIS_TICK); - } - } - - } - - /* ========================== * - * Fire triggers - * ========================== */ - - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; - if (!(ent->valid && entity_has_prop(ent, ENTITY_PROP_ACTIVE))) continue; - if (!entity_has_prop(ent, ENTITY_PROP_TRIGGERED_THIS_TICK)) continue; - if ((time - ent->last_triggered < ent->trigger_delay) && ent->last_triggered != 0) continue; - - ent->last_triggered = time; - - /* Fire weapon */ - if (entity_has_prop(ent, ENTITY_PROP_WEAPON)) { - struct xform xf = entity_get_xform(ent); - - struct sprite_tag sprite = ent->sprite; - u32 animation_frame = ent->animation_frame; - struct xform sprite_xform = xform_mul(xf, ent->sprite_local_xform); - struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, sprite); - - struct sprite_sheet_slice out_slice = sprite_sheet_get_slice(sheet, STR("out"), animation_frame); - struct v2 out_pos = xform_mul_v2(sprite_xform, out_slice.center); - struct v2 out_vec = xform_basis_mul_v2(sprite_xform, out_slice.dir); - out_vec = v2_norm(out_vec); - out_vec = v2_mul(out_vec, 25); - - /* TODO: Remove this (testing) */ - { - struct entity *parent = entity_from_handle(store, ent->parent); - out_vec = v2_add(out_vec, parent->velocity); - } - - { - /* FIXME: Weapon velocity should affect bullet velocity - * (This should also make bullet_src_ent unnecessary for - * drawing bullet trails) */ - - /* Spawn bullet */ - struct entity *bullet = entity_alloc(root); - bullet->sprite = sprite_tag_from_path(STR("res/graphics/bullet.ase")); - struct xform bullet_xf = XFORM_TRS(.t = out_pos, .r = v2_angle(out_vec) + PI / 2); - entity_set_xform(bullet, bullet_xf); - bullet->velocity = out_vec; - bullet->bullet_src = ent->handle; - entity_enable_prop(bullet, ENTITY_PROP_BULLET); - } - } - } - /* ========================== * * Update camera position * ========================== */ @@ -656,35 +707,6 @@ INTERNAL void game_update(struct game_cmd_array game_cmds) } } - /* ========================== * - * Release entities - * ========================== */ - - /* TODO: Breadth first iteration to only release parent entities (since - * child entities will be released along with parent anyway) */ - - { - struct temp_arena temp = arena_temp_begin(scratch.arena); - - struct entity **ents_to_release = arena_dry_push(temp.arena, struct entity *); - u64 ents_to_release_count = 0; - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; - if (!ent->valid) continue; - - if (entity_has_prop(ent, ENTITY_PROP_RELEASE_AT_END_OF_TICK)) { - *arena_push(temp.arena, struct entity *) = ent; - ++ents_to_release_count; - } - } - - for (u64 i = 0; i < ents_to_release_count; ++i) { - entity_release(store, ents_to_release[i]); - } - - arena_temp_end(temp); - } - /* ========================== * * Publish tick * ========================== */ diff --git a/src/user.c b/src/user.c index 501e2dca..e54ae56c 100644 --- a/src/user.c +++ b/src/user.c @@ -804,6 +804,7 @@ INTERNAL void user_update(void) struct xform sprite_xform = xf; /* Draw bullet trail */ +#if 0 if (entity_has_prop(ent, ENTITY_PROP_BULLET)) { f32 max_len = 0.75; @@ -829,6 +830,7 @@ INTERNAL void user_update(void) draw_solid_line(G.world_canvas, start, end, thickness, color); } +#endif /* Draw sprite */ if (!sprite_tag_is_nil(sprite)) {