entity_prop_kinematic

This commit is contained in:
jacob 2024-08-14 18:22:06 -05:00
parent 2a357c127f
commit 38075e5efd
5 changed files with 169 additions and 129 deletions

BIN
res/graphics/bullet.ase (Stored with Git LFS)

Binary file not shown.

View File

@ -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) INTERNAL struct xform entity_get_xform_w_store(struct entity_store *store, struct entity *ent)
{ {
struct xform xf; struct xform xf;
if (ent->is_top) { if (ent->cached_global_xform_dirty) {
xf = ent->local_xform; if (ent->is_top) {
} else if (!ent->cached_global_xform_dirty) { xf = ent->local_xform;
xf = ent->cached_global_xform; } else {
} else { struct entity *parent = entity_from_handle(store, ent->parent);
struct entity_handle parent_handle = ent->parent; xf = entity_get_xform_w_store(store, parent);
struct entity *parent = entity_from_handle(store, parent_handle); xf = xform_mul(xf, ent->local_xform);
xf = entity_get_xform_w_store(store, parent); ent->cached_global_xform = xf;
xf = xform_mul(xf, ent->local_xform); ent->cached_global_xform_dirty = false;
}
ent->cached_global_xform = xf; ent->cached_global_xform = xf;
ent->cached_global_xform_dirty = false; ent->cached_global_xform_dirty = false;
} else {
xf = ent->cached_global_xform;
} }
return xf; 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 entity_get_xform(struct entity *ent)
{ {
struct xform xf; struct xform xf;
if (ent->is_top) { if (ent->cached_global_xform_dirty) {
xf = ent->local_xform; if (ent->is_top) {
} else if (!ent->cached_global_xform_dirty) { xf = ent->local_xform;
xf = ent->cached_global_xform; } else {
} else { struct entity_store *store = entity_get_store(ent);
struct entity_store *store = entity_get_store(ent); struct entity *parent = entity_from_handle(store, ent->parent);
struct entity *parent = entity_from_handle(store, ent->parent); xf = entity_get_xform_w_store(store, parent);
xf = entity_get_xform_w_store(store, parent); xf = xform_mul(xf, ent->local_xform);
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 = xf;
ent->cached_global_xform_dirty = false; ent->cached_global_xform_dirty = false;
} else {
xf = ent->cached_global_xform;
} }
return xf; 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) void entity_set_xform(struct entity *ent, struct xform xf)
{ {
struct entity_store *store = entity_get_store(ent); struct entity_store *store = entity_get_store(ent);
/* Update local xform */
if (ent->is_top) { if (ent->is_top) {
ent->local_xform = xf; ent->local_xform = xf;
} else { } else {
/* Update child local xform */
struct entity *parent = entity_from_handle(store, ent->parent); struct entity *parent = entity_from_handle(store, ent->parent);
struct xform parent_global = entity_get_xform_w_store(store, parent); struct xform parent_global = entity_get_xform_w_store(store, parent);
ent->local_xform = xform_mul(xform_invert(parent_global), xf); 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); entity_mark_child_xforms_dirty(store, ent);
} }

View File

@ -8,18 +8,22 @@ enum entity_prop {
ENTITY_PROP_NONE, ENTITY_PROP_NONE,
ENTITY_PROP_ACTIVE, ENTITY_PROP_ACTIVE,
ENTITY_PROP_RELEASE_AT_END_OF_TICK,
ENTITY_PROP_RELEASE_NEXT_TICK,
ENTITY_PROP_KINEMATIC,
ENTITY_PROP_PLAYER_CONTROLLED, ENTITY_PROP_PLAYER_CONTROLLED,
ENTITY_PROP_CAMERA, ENTITY_PROP_CAMERA,
ENTITY_PROP_CAMERA_ACTIVE, ENTITY_PROP_CAMERA_ACTIVE,
ENTITY_PROP_BULLET,
ENTITY_PROP_WEAPON, ENTITY_PROP_WEAPON,
ENTITY_PROP_TRIGGERING_EQUIPPED, ENTITY_PROP_TRIGGERING_EQUIPPED,
ENTITY_PROP_TRIGGERED_THIS_TICK, ENTITY_PROP_TRIGGERED_THIS_TICK,
ENTITY_PROP_TRIGGER_NEXT_TICK, ENTITY_PROP_TRIGGER_NEXT_TICK,
ENTITY_PROP_BULLET,
/* Test props */ /* Test props */
ENTITY_PROP_TEST, ENTITY_PROP_TEST,
@ -66,12 +70,18 @@ struct entity {
struct entity_handle first; struct entity_handle first;
struct entity_handle last; 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 */ /* Position */
/* Access with xform getters/setters */ /* 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 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 cached_global_xform; /* Calculated from entity tree */
b32 cached_global_xform_dirty; b32 cached_global_xform_dirty;
/* ====================================================================== */ /* ====================================================================== */
@ -92,6 +102,7 @@ struct entity {
/* ====================================================================== */ /* ====================================================================== */
/* Physics */ /* Physics */
/* ENTITY_PROP_KINEMATIC */
struct v2 acceleration; struct v2 acceleration;
struct v2 velocity; struct v2 velocity;

View File

@ -129,7 +129,7 @@ INTERNAL void spawn_test_entities(void)
player_ent = e; player_ent = e;
//entity_enable_prop(e, ENTITY_PROP_TEST); entity_enable_prop(e, ENTITY_PROP_KINEMATIC);
} }
/* Weapon */ /* Weapon */
@ -156,6 +156,8 @@ INTERNAL void spawn_test_entities(void)
e->trigger_delay = 1.0 / 10.0; e->trigger_delay = 1.0 / 10.0;
player_ent->equipped = e->handle; player_ent->equipped = e->handle;
//entity_enable_prop(e, ENTITY_PROP_KINEMATIC);
} }
/* Camera */ /* 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 * Activate entities
* ========================== */ * ========================== */
@ -255,8 +286,11 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
if (!ent->valid) continue; if (!ent->valid) continue;
if (!entity_has_prop(ent, ENTITY_PROP_ACTIVE)) { if (!entity_has_prop(ent, ENTITY_PROP_ACTIVE)) {
entity_enable_prop(ent, ENTITY_PROP_ACTIVE); if (ent->activation_tick == 0 || G.tick.tick_id >= ent->activation_tick) {
++ent->continuity_gen; 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 * Simulate entity physics
* ========================== */ * ========================== */
@ -500,7 +619,7 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
/* Integrate acceleration & velocity */ /* Integrate acceleration & velocity */
{ if (entity_has_prop(ent, ENTITY_PROP_KINEMATIC)) {
f32 dt = (f32)G.tick.dt; f32 dt = (f32)G.tick.dt;
/* Apply acceleration to velocity */ /* 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 * 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 * Publish tick
* ========================== */ * ========================== */

View File

@ -804,6 +804,7 @@ INTERNAL void user_update(void)
struct xform sprite_xform = xf; struct xform sprite_xform = xf;
/* Draw bullet trail */ /* Draw bullet trail */
#if 0
if (entity_has_prop(ent, ENTITY_PROP_BULLET)) { if (entity_has_prop(ent, ENTITY_PROP_BULLET)) {
f32 max_len = 0.75; f32 max_len = 0.75;
@ -829,6 +830,7 @@ INTERNAL void user_update(void)
draw_solid_line(G.world_canvas, start, end, thickness, color); draw_solid_line(G.world_canvas, start, end, thickness, color);
} }
#endif
/* Draw sprite */ /* Draw sprite */
if (!sprite_tag_is_nil(sprite)) { if (!sprite_tag_is_nil(sprite)) {