equip/fire beginnings. player input cmd change.

This commit is contained in:
jacob 2024-08-09 21:03:51 -05:00
parent eb3c59ac13
commit f5400b0205
6 changed files with 158 additions and 125 deletions

View File

@ -12,6 +12,11 @@ enum entity_prop {
ENTITY_PROP_CAMERA,
ENTITY_PROP_CAMERA_ACTIVE,
ENTITY_PROP_EQUIPPER,
ENTITY_PROP_EQUIPABLE,
ENTITY_PROP_FIRING_EQUIPPED,
/* Test props */
ENTITY_PROP_TEST,
@ -58,17 +63,26 @@ struct entity {
struct entity_handle last;
/* ====================================================================== */
/* 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 */
b32 cached_global_xform_dirty;
/* ====================================================================== */
/* Control */
struct {
struct v2 move;
struct v2 focus;
} control;
/* ====================================================================== */
/* Physics */
struct v2 acceleration;
struct v2 velocity;
struct v2 focus; /* Focus is a vector relative to the entity */
/* ENTITY_PROP_PLAYER_CONTROLLED */
f32 player_max_speed;
@ -90,6 +104,11 @@ struct entity {
f64 animation_time_in_frame;
u32 animation_frame;
/* ====================================================================== */
/* Equip */
struct entity_handle equipped;
/* ====================================================================== */
/* Testing */

View File

@ -121,9 +121,10 @@ INTERNAL void spawn_test_entities(void)
entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED);
e->player_max_speed = 4.f;
e->player_acceleration = 20.0f;
e->focus = V2(0, -1);
e->control.focus = V2(0, -1);
entity_enable_prop(e, ENTITY_PROP_ANIMATING);
entity_enable_prop(e, ENTITY_PROP_EQUIPPER);
player_ent = e;
@ -132,41 +133,21 @@ INTERNAL void spawn_test_entities(void)
/* Child 1 */
{
struct v2 pos = V2(0.15, -0.05);
struct v2 pos = V2(1, -1);
struct v2 size = V2(1, 1);
f32 r = 0;
struct xform xf = XFORM_TRS(.t = pos, .r = r, .s = size);
struct entity *e = entity_alloc_child(player_ent);
struct entity *e = entity_alloc_top(G.world.entity_store);
entity_set_local_xform(e, xf);
e->sprite = sprite_tag_from_path(STR("res/graphics/gun.ase"));
//e->sprite_tint = RGBA_32_F(0, 0, 0, 1);
entity_enable_prop(e, ENTITY_PROP_ANIMATING);
entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED);
}
/* Child 2 */
{
struct v2 pos = V2(-0.15, -0.05);
struct v2 size = V2(-0.25, 0.5);
f32 r = 0;
struct xform xf = XFORM_TRS(.t = pos, .r = r, .s = size);
struct entity *e = entity_alloc_child(player_ent);
entity_set_local_xform(e, xf);
e->sprite = sprite_tag_from_path(STR("res/graphics/tim.ase"));
e->sprite_span_name = STR("idle.two_handed");
e->sprite_tint = RGBA_32_F(0, 0, 0, 1);
entity_enable_prop(e, ENTITY_PROP_ANIMATING);
entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED);
entity_enable_prop(e, ENTITY_PROP_EQUIPABLE);
player_ent->equipped = e->handle;
}
/* Camera ent */
@ -203,10 +184,8 @@ INTERNAL void game_update(void)
struct temp_arena scratch = scratch_begin_no_conflict();
struct entity_store *store = G.world.entity_store;
/* ========================== *
* Begin frame cache scopes
* Begin frame
* ========================== */
struct sprite_scope *sprite_frame_scope = sprite_scope_begin();
@ -225,23 +204,20 @@ INTERNAL void game_update(void)
G.world.time += G.world.dt;
/* ========================== *
* Process game cmds
* Pull 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) {
struct game_cmd cmd = game_cmds.cmds[cmd_index];
switch (cmd.kind) {
/* Movement */
case GAME_CMD_KIND_PLAYER_MOVE: {
G.world.player_move_dir = cmd.move_dir;
} break;
case GAME_CMD_KIND_PLAYER_AIM: {
G.world.camera_focus = cmd.aim;
} break;
/* Clear level */
case GAME_CMD_KIND_CLEAR_ALL: {
logf_info("Clearing level");
@ -263,11 +239,6 @@ INTERNAL void game_update(void)
};
}
if (v2_len(G.world.player_move_dir) > 1.f) {
/* Clamp movement magnitude */
G.world.player_move_dir = v2_norm(G.world.player_move_dir);
}
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */
@ -284,7 +255,7 @@ INTERNAL void game_update(void)
if (sprite_tag_is_nil(ent->sprite)) continue;
/* ========================== *
* Update animation
* Update sprite animation
* ========================== */
if (entity_has_prop(ent, ENTITY_PROP_ANIMATING)) {
@ -332,31 +303,15 @@ INTERNAL void game_update(void)
/* ========================== *
* Update entities pre-physics
* Update entities pre-simulation
* ========================== */
/* Find active camera */
struct entity *active_camera;
{
enum entity_prop props[] = { ENTITY_PROP_CAMERA, ENTITY_PROP_CAMERA_ACTIVE };
active_camera = entity_find_first_match_all(store, (struct entity_prop_array) { .count = ARRAY_COUNT(props), .props = props });
}
/* Update camera focus */
if (active_camera->valid) {
active_camera->focus = G.world.camera_focus;
}
/* Calculate player aim pos from camera focus */
struct v2 player_aim_pos = v2_add(entity_get_xform(active_camera).og, G.world.camera_focus);
(UNUSED)player_aim_pos;
for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) {
struct entity *ent = &entities_array.entities[entity_index];
if (!ent->valid) continue;
/* ========================== *
* Initialize
* Initialize test
* ========================== */
/* ENTITY_PROP_TEST */
@ -367,15 +322,50 @@ INTERNAL void game_update(void)
}
/* ========================== *
* Calculate player aim angle
* 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)) {
struct xform xf = entity_get_xform(ent);
/* Update focus */
ent->focus = v2_sub(player_aim_pos, xf.og);
/* Solve for final angle using law of sines */
f32 final_xf_angle;
{
@ -390,7 +380,7 @@ INTERNAL void game_update(void)
}
struct v2 ent_pos = xf.og;
struct v2 focus_pos = v2_add(ent_pos, ent->focus);
struct v2 focus_pos = v2_add(ent_pos, ent->control.focus);
struct v2 hold_pos;
{
@ -451,10 +441,22 @@ INTERNAL void game_update(void)
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);
}
}
}
}
/* ========================== *
* Update entity physics
* Simulate entities
* ========================== */
for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) {
@ -462,14 +464,14 @@ INTERNAL void game_update(void)
if (!ent->valid) continue;
/* ========================== *
* Player movement
* Player control
* ========================== */
if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) {
f32 max_speed = ent->player_max_speed;
f32 acceleration_rate = ent->player_acceleration;
acceleration_rate = clamp_f32(acceleration_rate, 0, GAME_FPS); /* Can't integrate acceleration rate higher than FPS */
struct v2 target_velocity = v2_mul(G.world.player_move_dir, max_speed);
struct v2 target_velocity = v2_mul(ent->control.move, max_speed);
struct v2 target_acceleration = v2_sub(target_velocity, ent->velocity);
ent->acceleration = v2_mul(target_acceleration, acceleration_rate);
}
@ -493,7 +495,7 @@ INTERNAL void game_update(void)
}
/* ========================== *
* Update entities post-physics
* Update entities post-simulation
* ========================== */
for (u64 entity_index = 0; entity_index < entities_array.count; ++entity_index) {
@ -521,7 +523,7 @@ INTERNAL void game_update(void)
}
f32 ratio_y = 0.33f;
f32 ratio_x = ratio_y / aspect_ratio;
struct v2 camera_focus_dir = v2_mul_v2(follow->focus, V2(ratio_x, ratio_y));
struct v2 camera_focus_dir = v2_mul_v2(follow->control.focus, V2(ratio_x, ratio_y));
struct v2 camera_focus_pos = v2_add(entity_get_xform(follow).og, camera_focus_dir);
ent->camera_xform_target = xf;
ent->camera_xform_target.og = camera_focus_pos;

View File

@ -10,7 +10,7 @@ enum game_cmd_kind {
GAME_CMD_KIND_NONE,
GAME_CMD_KIND_PLAYER_MOVE,
GAME_CMD_KIND_PLAYER_AIM,
GAME_CMD_KIND_PLAYER_FIRE,
/* Testing */
GAME_CMD_KIND_CLEAR_ALL,
@ -22,11 +22,12 @@ enum game_cmd_kind {
struct game_cmd {
enum game_cmd_kind kind;
/* 1 = start, -1 = stop */
i32 state;
/* GAME_CMD_KIND_PLAYER_MOVE */
struct v2 move_dir;
/* GAME_CMD_KIND_PLAYER_AIM */
struct v2 aim;
struct v2 aim_pos;
};
struct game_cmd_array {

View File

@ -80,6 +80,7 @@ GLOBAL READONLY enum user_bind_kind g_binds[SYS_BTN_COUNT] = {
[SYS_BTN_S] = USER_BIND_KIND_MOVE_DOWN,
[SYS_BTN_A] = USER_BIND_KIND_MOVE_LEFT,
[SYS_BTN_D] = USER_BIND_KIND_MOVE_RIGHT,
[SYS_BTN_M1] = USER_BIND_KIND_FIRE,
/* Testing */
@ -440,11 +441,13 @@ INTERNAL void user_update(void)
tick_blend = clamp_f32(tick_blend, 0.0f, 1.0f);
}
/* TODO: Should we actually be basing interpolated tick on t1 rather
* than t0? This means un-interpolated values will use the newer frame's
* value, while still interpolating from the older frame. */
world_copy_replace(&G.world, t1);
/* Blend world globals */
G.world.time = math_lerp64(t0->time, t1->time, (f64)tick_blend);
G.world.camera_focus = v2_lerp(t0->camera_focus, t1->camera_focus, tick_blend);
/* Blend entities */
struct entity_array t0_entities = entity_store_as_array(t0->entity_store);
@ -466,7 +469,9 @@ INTERNAL void user_update(void)
e->acceleration = v2_lerp(e0->acceleration, e1->acceleration, tick_blend);
e->velocity = v2_lerp(e0->velocity, e1->velocity, tick_blend);
e->player_acceleration = math_lerp(e0->player_acceleration, e1->player_acceleration, tick_blend);
e->focus = v2_lerp(e0->focus, e1->focus, tick_blend);
e->control.move = v2_lerp(e0->control.move, e1->control.move, tick_blend);
e->control.focus = v2_lerp(e0->control.focus, e1->control.focus, tick_blend);
e->sprite_local_xform = xform_lerp(e0->sprite_local_xform, e1->sprite_local_xform, tick_blend);
e->animation_time_in_frame = math_lerp64(e0->animation_time_in_frame, e1->animation_time_in_frame, (f64)tick_blend);
@ -848,13 +853,13 @@ INTERNAL void user_update(void)
debug_draw_xform(xf);
}
/* Draw aim arrow */
/* Draw focus arrow */
if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) {
struct sprite_sheet *sheet = sprite_sheet_from_tag_async(sprite_frame_scope, ent->sprite);
struct sprite_sheet_slice slice = sprite_sheet_get_slice(sheet, STR("hold"), ent->animation_frame);
struct v2 start = xform_mul_v2(sprite_xform, slice.center);
start = xform_mul_v2(G.world_view, start);
struct v2 end = v2_add(xf.og, ent->focus);
struct v2 end = v2_add(xf.og, ent->control.focus);
end = xform_mul_v2(G.world_view, end);
draw_solid_arrow_line(G.viewport_canvas, start, end, 3, 10, RGBA_32_F(1, 1, 1, 0.5));
}
@ -946,10 +951,11 @@ INTERNAL void user_update(void)
}
/* ========================== *
* Construct movement input
* Construct player control cmd
* ========================== */
/* Movement */
{
struct v2 input_move_dir = { 0 };
{
for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(G.bind_states); ++bind) {
@ -985,20 +991,27 @@ INTERNAL void user_update(void)
input_move_dir = v2_norm(input_move_dir);
}
/* Queue move cmd */
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) {
/* Queue player move cmd */
queue_game_cmd(&cmd_list, (struct game_cmd) {
.kind = GAME_CMD_KIND_PLAYER_MOVE,
.move_dir = input_move_dir,
.aim_pos = input_aim_pos
});
/* Queue aim cmd */
if (!G.debug_camera) {
struct v2 input_aim = v2_sub(G.world_cursor, entity_get_xform(active_camera).og);
/* Queue player fire cmd */
if (cmd_fire) {
queue_game_cmd(&cmd_list, (struct game_cmd) {
.kind = GAME_CMD_KIND_PLAYER_AIM,
.aim = input_aim,
.kind = GAME_CMD_KIND_PLAYER_FIRE,
.state = cmd_fire
});
}
}
}
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */

View File

@ -18,6 +18,7 @@ enum user_bind_kind {
USER_BIND_KIND_MOVE_DOWN,
USER_BIND_KIND_MOVE_LEFT,
USER_BIND_KIND_MOVE_RIGHT,
USER_BIND_KIND_FIRE,
/* Testing */

View File

@ -12,9 +12,6 @@ struct world {
f64 dt;
f64 time;
struct v2 player_move_dir;
struct v2 camera_focus;
struct entity_store *entity_store;
};