From c5d0f8b6eac3b78cae875cd89516ee40dfcc7acc Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 6 Mar 2024 12:53:47 -0600 Subject: [PATCH] player acceleration, debug draw & camera mode --- res/graphics/tim.ase | 4 +- src/app.c | 4 +- src/entity.h | 5 +- src/game.c | 28 +++-- src/user.c | 259 ++++++++++++++++++++++++++----------------- src/user.h | 7 ++ 6 files changed, 187 insertions(+), 120 deletions(-) diff --git a/res/graphics/tim.ase b/res/graphics/tim.ase index 0afb493e..1835dcfb 100644 --- a/res/graphics/tim.ase +++ b/res/graphics/tim.ase @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2cc35b342e0b9c29d64b53620ad18c019ddd01f0c97bbbb87fe0e0cacf8cb5cf -size 5313 +oid sha256:3df0d8172cdcef00d61763c19c6bc3bfe5d9cb2ef6d0e9b9324d9908a3af57df +size 985 diff --git a/src/app.c b/src/app.c index 3477f27e..2a076884 100644 --- a/src/app.c +++ b/src/app.c @@ -124,9 +124,9 @@ void app_entry_point(void) draw_startup(); /* Startup threaded systems */ - user_startup(&window); work_startup(worker_count); game_startup(); + user_startup(&window); playback_startup(); sys_window_show(&window); @@ -139,9 +139,9 @@ void app_entry_point(void) * forcing process exit (to prevent process hanging in the background * when a thread gets stuck) */ playback_shutdown(); + user_shutdown(); game_shutdown(); work_shutdown(); - user_shutdown(); /* Write window settings to file */ struct sys_window_settings settings = sys_window_get_settings(&window); diff --git a/src/entity.h b/src/entity.h index e26400e2..4ec14a86 100644 --- a/src/entity.h +++ b/src/entity.h @@ -54,11 +54,12 @@ struct entity { /* ====================================================================== */ /* Physics */ + struct v2 acceleration; struct v2 velocity; - f32 drag; /* ENTITY_PROP_PLAYER_CONTROLLED */ - f32 player_acceleration_magnitude; + f32 player_max_speed; + f32 player_acceleration; /* ====================================================================== */ /* Sprite */ diff --git a/src/game.c b/src/game.c index 3f816d98..69ca31b1 100644 --- a/src/game.c +++ b/src/game.c @@ -10,7 +10,7 @@ #include "math.h" #include "scratch.h" -#define GAME_FPS 50 +#define GAME_FPS 30 GLOBAL struct { b32 shutdown; @@ -187,8 +187,8 @@ INTERNAL void game_update(void) e->sprite_tint = COLOR_WHITE; entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED); - e->player_acceleration_magnitude = 4.f; - e->drag = 5; + e->player_max_speed = 5.f; + e->player_acceleration = 20.0f; player_ent = e; @@ -326,19 +326,27 @@ break_animation: * ========================== */ if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { - f32 acc_magnitude = ent->player_acceleration_magnitude * (f32)L.tick.dt; - struct v2 acc = v2_mul(L.tick.player_move, acc_magnitude); - ent->velocity = v2_add(ent->velocity, acc); + 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(L.tick.player_move, max_speed); + struct v2 target_acceleration = v2_sub(target_velocity, ent->velocity); + ent->acceleration = v2_mul(target_acceleration, acceleration_rate); } /* ========================== * - * Apply velocity + * Integrate acceleration & velocity * ========================== */ - ent->rel_trs.t = v2_add(ent->rel_trs.t, ent->velocity); + { + f32 dt = (f32)L.tick.dt; - if (ent->drag > 0) { - ent->velocity = v2_div(ent->velocity, ent->drag); + /* Apply acceleration to velocity */ + struct v2 a = v2_mul(ent->acceleration, dt); + ent->velocity = v2_add(ent->velocity, a); + + /* Apply velocity to position */ + ent->rel_trs.t = v2_add(ent->rel_trs.t, v2_mul(ent->velocity, dt)); } /* ========================== * diff --git a/src/user.c b/src/user.c index 246a2cb2..6924c8d1 100644 --- a/src/user.c +++ b/src/user.c @@ -42,6 +42,9 @@ GLOBAL struct { b32 panning; struct v2 panning_from; + b32 debug_draw; + b32 debug_camera; + /* User thread input */ struct sys_mutex sys_events_mutex; struct arena sys_events_arena; @@ -59,16 +62,28 @@ GLOBAL struct { * Bind state * ========================== */ +struct bind_state { + b32 pressed; /* Is this bind held down this frame */ + u32 num_presses; /* How many times was this bind pressed since last frame */ +}; + +GLOBAL struct bind_state g_bind_states[USER_BIND_KIND_COUNT] = { 0 }; + /* TODO: Remove this */ GLOBAL enum user_bind_kind g_binds[SYS_BTN_COUNT] = { [SYS_BTN_W] = USER_BIND_KIND_MOVE_UP, [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_D] = USER_BIND_KIND_MOVE_RIGHT, -GLOBAL b32 g_bind_state[USER_BIND_KIND_COUNT] = { 0 }; + /* Testing */ + + [SYS_BTN_F6] = USER_BIND_KIND_DEBUG_DRAW, + [SYS_BTN_F7] = USER_BIND_KIND_DEBUG_CAMERA, + [SYS_BTN_MWHEELUP] = USER_BIND_KIND_ZOOM_IN, + [SYS_BTN_MWHEELDOWN] = USER_BIND_KIND_ZOOM_OUT +}; /* ========================== * * Window -> user communication @@ -230,11 +245,11 @@ INTERNAL struct v2 view_mouse_pos(struct view view) INTERNAL void debug_draw_xform(struct mat3x3 mtx) { f32 thickness = 2.f / PIXELS_PER_UNIT / L.world_view.zoom; - u32 color = RGBA_F(0, 1, 1, 0.3); - u32 color_x = RGBA_F(1, 0, 0, 0.3); - u32 color_y = RGBA_F(0, 1, 0, 0.3); + u32 color = RGBA_F(0, 1, 1, 0.3); + u32 color_x = RGBA_F(1, 0, 0, 0.3); + u32 color_y = RGBA_F(0, 1, 0, 0.3); - struct v2 pos = mat3x3_get_pos(mtx); + struct v2 pos = mat3x3_get_pos(mtx); struct v2 x_ray = mat3x3_get_right(mtx); struct v2 y_ray = mat3x3_get_up(mtx); @@ -246,6 +261,22 @@ INTERNAL void debug_draw_xform(struct mat3x3 mtx) draw_solid_quad(L.world_canvas, quad, color); } +/* TODO: remove this (testing) */ +INTERNAL void debug_draw_movement(struct entity *ent) +{ + f32 thickness = 2.f / PIXELS_PER_UNIT / L.world_view.zoom; + + u32 color_vel = RGBA_F(1, 0.5, 0, 1); + u32 color_acc = RGBA_F(1, 1, 0.5, 1); + + struct v2 pos = mat3x3_get_pos(ent->world_xform); + struct v2 vel_ray = ent->velocity; + struct v2 acc_ray = ent->acceleration; + + draw_solid_ray(L.world_canvas, pos, vel_ray, thickness, color_vel); + draw_solid_ray(L.world_canvas, pos, acc_ray, thickness, color_acc); +} + INTERNAL void user_update(void) { struct temp_arena scratch = scratch_begin_no_conflict(); @@ -276,7 +307,13 @@ INTERNAL void user_update(void) * Read sys events * ========================== */ - i32 input_zooms = 0; + /* Reset bind states "was_pressed" */ + for (u32 i = 0; i < ARRAY_COUNT(g_bind_states); ++i) { + g_bind_states[i] = (struct bind_state) { + .pressed = g_bind_states[i].pressed + }; + } + for (u64 entity_index = 0; entity_index < events.count; ++entity_index) { struct sys_event *event = &events.events[entity_index]; @@ -299,15 +336,6 @@ INTERNAL void user_update(void) } } - /* Zoom camera/view */ - if (event->kind == SYS_EVENT_KIND_BUTTON_DOWN) { - if (event->button == SYS_BTN_MWHEELUP) { - ++input_zooms; - } else if (event->button == SYS_BTN_MWHEELDOWN) { - --input_zooms; - } - } - if (event->kind == SYS_EVENT_KIND_BUTTON_UP) { #if DEVELOPER /* Escape quit */ @@ -331,12 +359,16 @@ INTERNAL void user_update(void) } /* Bind */ - if (event->kind == SYS_EVENT_KIND_BUTTON_DOWN || event->kind == SYS_EVENT_KIND_BUTTON_UP) { + if ((event->kind == SYS_EVENT_KIND_BUTTON_DOWN || event->kind == SYS_EVENT_KIND_BUTTON_UP) && !event->is_repeat) { enum sys_btn button = event->button; button = button >= SYS_BTN_COUNT ? SYS_BTN_NONE : button; enum user_bind_kind bind = g_binds[button]; if (bind) { - g_bind_state[bind] = event->kind == SYS_EVENT_KIND_BUTTON_DOWN; + b32 pressed = event->kind == SYS_EVENT_KIND_BUTTON_DOWN; + g_bind_states[bind].pressed = pressed; + if (pressed) { + ++g_bind_states[bind].num_presses; + } } } } @@ -353,6 +385,7 @@ INTERNAL void user_update(void) } /* Zoom view */ + i32 input_zooms = g_bind_states[USER_BIND_KIND_ZOOM_IN].num_presses - g_bind_states[USER_BIND_KIND_ZOOM_OUT].num_presses; if (input_zooms != 0) { i32 dir = input_zooms >= 0 ? 1 : -1; u32 zooms_abs = input_zooms >= 0 ? input_zooms : -input_zooms; @@ -377,41 +410,50 @@ INTERNAL void user_update(void) L.world_view.center = v2_add(L.world_view.center, offset); } - /* Process binds */ - struct v2 input_move_dir = { 0 }; - for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(g_bind_state); ++bind) { - b32 state = g_bind_state[bind]; - if (!state) { - continue; - } - - switch (bind) { - case USER_BIND_KIND_MOVE_UP: { - input_move_dir.y -= 1; - } break; - - case USER_BIND_KIND_MOVE_DOWN: { - input_move_dir.y += 1; - } break; - - case USER_BIND_KIND_MOVE_LEFT: { - input_move_dir.x -= 1; - } break; - - case USER_BIND_KIND_MOVE_RIGHT: { - input_move_dir.x += 1; - } break; - - default: break; + /* Movement */ + { + struct v2 input_move_dir = { 0 }; + for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(g_bind_states); ++bind) { + struct bind_state state = g_bind_states[bind]; + + if (!state.pressed && state.num_presses <= 0) { + continue; + } + + switch (bind) { + /* Movement */ + case USER_BIND_KIND_MOVE_UP: { + input_move_dir.y -= 1; + } break; + + case USER_BIND_KIND_MOVE_DOWN: { + input_move_dir.y += 1; + } break; + + case USER_BIND_KIND_MOVE_LEFT: { + input_move_dir.x -= 1; + } break; + + case USER_BIND_KIND_MOVE_RIGHT: { + input_move_dir.x += 1; + } break; + + default: break; + } } + queue_game_cmd(&cmd_list, (struct game_cmd) { + .kind = GAME_CMD_KIND_PLAYER_MOVE, + .dir = v2_norm(input_move_dir) + }); } - input_move_dir = v2_norm(input_move_dir); - /* Queue move cmd */ - queue_game_cmd(&cmd_list, (struct game_cmd) { - .kind = GAME_CMD_KIND_PLAYER_MOVE, - .dir = input_move_dir - }); + /* Debug */ + if (g_bind_states[USER_BIND_KIND_DEBUG_DRAW].num_presses > 0) { + L.debug_draw = !L.debug_draw; + } + if (g_bind_states[USER_BIND_KIND_DEBUG_CAMERA].num_presses > 0) { + L.debug_camera = !L.debug_camera; + } /* ========================== * * Produce interpolated tick @@ -446,12 +488,15 @@ INTERNAL void user_update(void) e->rel_trs = trs_lerp(e0->rel_trs, e1->rel_trs, tick_blend); e->world_xform = mat3x3_lerp(e0->world_xform, e1->world_xform, tick_blend); + 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_f32(e0->player_acceleration, e1->player_acceleration, tick_blend); + e->sprite_trs = trs_lerp(e0->sprite_trs, e1->sprite_trs, tick_blend); e->sprite_pivot_norm = v2_lerp(e0->sprite_pivot_norm, e1->sprite_pivot_norm, tick_blend); e->camera_rot = math_lerp_angle(e0->camera_rot, e1->camera_rot, tick_blend); - e->camera_zoom = math_lerp_f32(e0->camera_rot, e1->camera_rot, tick_blend); - + e->camera_zoom = math_lerp_f32(e0->camera_zoom, e1->camera_zoom, tick_blend); } } #else @@ -509,17 +554,17 @@ INTERNAL void user_update(void) struct entity *ent = &tick->entities[entity_index]; if (!ent->valid) continue; + b32 is_camera = entity_has_prop(ent, ENTITY_PROP_CAMERA); + /* Update view */ - if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) { - if (ent->camera_active) { - struct v2 center = mat3x3_get_pos(ent->world_xform); - f32 rot = ent->camera_rot; - f32 zoom = ent->camera_zoom; - zoom = zoom > 0 ? zoom : 1.0; - L.world_view.center = center; - L.world_view.rot = rot; - L.world_view.zoom = 1; - } + if (is_camera && ent->camera_active && !L.debug_camera) { + struct v2 center = mat3x3_get_pos(ent->world_xform); + f32 rot = ent->camera_rot; + f32 zoom = ent->camera_zoom; + zoom = zoom > 0 ? zoom : 1; + L.world_view.center = center; + L.world_view.rot = rot; + L.world_view.zoom = zoom; } /* Draw sprite */ @@ -550,37 +595,41 @@ INTERNAL void user_update(void) draw_texture_quad(L.world_canvas, params, quad); } + if (L.debug_draw && !is_camera) { #if 0 - /* Debug draw sprite quad */ - { - f32 thickness = 2.f; - u32 color = RGBA_F(1, 1, 0, 0.25); - draw_solid_quad_line(L.world_canvas, quad, (thickness / PIXELS_PER_UNIT / L.world_view.zoom), color); - } + /* Debug draw sprite quad */ + { + f32 thickness = 2.f; + u32 color = RGBA_F(1, 1, 0, 0.25); + draw_solid_quad_line(L.world_canvas, quad, (thickness / PIXELS_PER_UNIT / L.world_view.zoom), color); + } #endif #if 0 - /* Debug draw sprite transform */ - { - debug_draw_xform(mtx); - } + /* Debug draw sprite transform */ + { + debug_draw_xform(mtx); + } #endif - /* Debug draw sprite pivot */ - { - u32 color = RGBA_F(1, 0, 0, 1); + /* Debug draw sprite pivot */ + { + u32 color = RGBA_F(1, 0, 0, 1); - struct mat3x3 mtx_pre_pivot = ent->world_xform; - mtx_pre_pivot = mat3x3_trs(mtx_pre_pivot, ent->sprite_trs); + struct mat3x3 mtx_pre_pivot = ent->world_xform; + mtx_pre_pivot = mat3x3_trs(mtx_pre_pivot, ent->sprite_trs); - draw_solid_circle(L.world_canvas, mat3x3_get_pos(mtx_pre_pivot), 0.02, color, 20); + draw_solid_circle(L.world_canvas, mat3x3_get_pos(mtx_pre_pivot), 0.02, color, 20); + } } } /* Debug draw info */ - { + if (L.debug_draw && !is_camera) { struct mat3x3 mtx = ent->world_xform; - struct v2 bx = mat3x3_get_right(mtx); + struct trs trs = trs_from_mat3x3(mtx); + struct v2 velocity = ent->velocity; + struct v2 acceleration = ent->acceleration; struct font *disp_font = font_load_async(STR("res/fonts/fixedsys.ttf"), 12.0f); @@ -594,31 +643,30 @@ INTERNAL void user_update(void) if (disp_font) { struct string disp_name = ent->sprite_name; - if (disp_name.len > 13) { - disp_name = (struct string) { .len = disp_name.len - 13, .text = disp_name.text + 13 }; - } struct string fmt = STR( - "sprite name: %F,\n" - "rel rot: %F,\n" - "mtx rot: %F,\n" - "mtx bx: %F, %F\n" + "sprite name: \"%F\"\n" + "pos: (%F, %F)\n" + "scale: (%F, %F)\n" + "rot: %F\n" + "velocity: (%F, %F)\n" + "acceleration: (%F, %F)\n" ); struct string text = string_format(scratch.arena, fmt, - FMT_STR(disp_name), - FMT_FLOAT((f64)ent->rel_trs.r), - FMT_FLOAT((f64)trs_from_mat3x3(mtx).r), - FMT_FLOAT((f64)bx.x), FMT_FLOAT((f64)bx.y) - ); + FMT_STR(disp_name), + FMT_FLOAT((f64)trs.t.x), FMT_FLOAT((f64)trs.t.y), + FMT_FLOAT((f64)trs.s.x), FMT_FLOAT((f64)trs.s.y), + FMT_FLOAT((f64)trs.r), + FMT_FLOAT((f64)velocity.x), FMT_FLOAT((f64)velocity.y), + FMT_FLOAT((f64)acceleration.x), FMT_FLOAT((f64)acceleration.y) + ); draw_text_ex(L.world_canvas, disp_font, pos, 1.0f / PIXELS_PER_UNIT, text); } - } - /* Debug draw transform */ - { debug_draw_xform(ent->world_xform); + debug_draw_movement(ent); } } @@ -636,34 +684,37 @@ INTERNAL void user_update(void) }); /* Debug draw info */ - { + if (L.debug_draw) { f32 spacing = 20; struct v2 pos = V2(10, 8); struct font *font = font_load(STR("res/fonts/fixedsys.ttf"), 12.0f); - draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("L.time: %F"), FMT_FLOAT((f64)L.time))); + draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("time: %F"), FMT_FLOAT((f64)L.time))); pos.y += spacing; - draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("L.screen_size: (%F, %F)"), FMT_FLOAT((f64)L.screen_size.x), FMT_FLOAT((f64)L.screen_size.y))); + draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("screen_size: (%F, %F)"), FMT_FLOAT((f64)L.screen_size.x), FMT_FLOAT((f64)L.screen_size.y))); pos.y += spacing; - draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("L.screen_center: (%F, %F)"), FMT_FLOAT((f64)L.screen_center.x), FMT_FLOAT((f64)L.screen_center.y))); + draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("screen_center: (%F, %F)"), FMT_FLOAT((f64)L.screen_center.x), FMT_FLOAT((f64)L.screen_center.y))); pos.y += spacing; - draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("L.screen_mouse: (%F, %F)"), FMT_FLOAT((f64)L.screen_mouse.x), FMT_FLOAT((f64)L.screen_mouse.y))); + draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("screen_mouse: (%F, %F)"), FMT_FLOAT((f64)L.screen_mouse.x), FMT_FLOAT((f64)L.screen_mouse.y))); pos.y += spacing; - draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("L.world_view.center: (%F, %F)"), FMT_FLOAT((f64)L.world_view.center.x), FMT_FLOAT((f64)L.world_view.center.y))); + draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("world_view.center: (%F, %F)"), FMT_FLOAT((f64)L.world_view.center.x), FMT_FLOAT((f64)L.world_view.center.y))); pos.y += spacing; - draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("L.world_view.rot: %F"), FMT_FLOAT((f64)L.world_view.rot))); + draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("world_view.rot: %F"), FMT_FLOAT((f64)L.world_view.rot))); pos.y += spacing; - draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("L.world_view.zoom: %F"), FMT_FLOAT((f64)L.world_view.zoom))); + draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("world_view.zoom: %F"), FMT_FLOAT((f64)L.world_view.zoom))); pos.y += spacing; draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("world_mouse: (%F, %F)"), FMT_FLOAT((f64)world_mouse.x), FMT_FLOAT((f64)world_mouse.y))); pos.y += spacing; + + draw_text(L.ui_canvas, font, pos, string_format(scratch.arena, STR("debug_camera: %F"), FMT_STR(L.debug_camera ? STR("true") : STR("false")))); + pos.y += spacing; } /* Push game cmds */ diff --git a/src/user.h b/src/user.h index 5e4d394a..9a524a99 100644 --- a/src/user.h +++ b/src/user.h @@ -11,6 +11,13 @@ enum user_bind_kind { USER_BIND_KIND_MOVE_LEFT, USER_BIND_KIND_MOVE_RIGHT, + /* Testing */ + + USER_BIND_KIND_DEBUG_DRAW, + USER_BIND_KIND_DEBUG_CAMERA, + USER_BIND_KIND_ZOOM_IN, + USER_BIND_KIND_ZOOM_OUT, + USER_BIND_KIND_COUNT };