player acceleration, debug draw & camera mode

This commit is contained in:
jacob 2024-03-06 12:53:47 -06:00
parent a4dc273480
commit c5d0f8b6ea
6 changed files with 187 additions and 120 deletions

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

Binary file not shown.

View File

@ -124,9 +124,9 @@ void app_entry_point(void)
draw_startup(); draw_startup();
/* Startup threaded systems */ /* Startup threaded systems */
user_startup(&window);
work_startup(worker_count); work_startup(worker_count);
game_startup(); game_startup();
user_startup(&window);
playback_startup(); playback_startup();
sys_window_show(&window); sys_window_show(&window);
@ -139,9 +139,9 @@ void app_entry_point(void)
* forcing process exit (to prevent process hanging in the background * forcing process exit (to prevent process hanging in the background
* when a thread gets stuck) */ * when a thread gets stuck) */
playback_shutdown(); playback_shutdown();
user_shutdown();
game_shutdown(); game_shutdown();
work_shutdown(); work_shutdown();
user_shutdown();
/* Write window settings to file */ /* Write window settings to file */
struct sys_window_settings settings = sys_window_get_settings(&window); struct sys_window_settings settings = sys_window_get_settings(&window);

View File

@ -54,11 +54,12 @@ struct entity {
/* ====================================================================== */ /* ====================================================================== */
/* Physics */ /* Physics */
struct v2 acceleration;
struct v2 velocity; struct v2 velocity;
f32 drag;
/* ENTITY_PROP_PLAYER_CONTROLLED */ /* ENTITY_PROP_PLAYER_CONTROLLED */
f32 player_acceleration_magnitude; f32 player_max_speed;
f32 player_acceleration;
/* ====================================================================== */ /* ====================================================================== */
/* Sprite */ /* Sprite */

View File

@ -10,7 +10,7 @@
#include "math.h" #include "math.h"
#include "scratch.h" #include "scratch.h"
#define GAME_FPS 50 #define GAME_FPS 30
GLOBAL struct { GLOBAL struct {
b32 shutdown; b32 shutdown;
@ -187,8 +187,8 @@ INTERNAL void game_update(void)
e->sprite_tint = COLOR_WHITE; e->sprite_tint = COLOR_WHITE;
entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED); entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED);
e->player_acceleration_magnitude = 4.f; e->player_max_speed = 5.f;
e->drag = 5; e->player_acceleration = 20.0f;
player_ent = e; player_ent = e;
@ -326,19 +326,27 @@ break_animation:
* ========================== */ * ========================== */
if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) {
f32 acc_magnitude = ent->player_acceleration_magnitude * (f32)L.tick.dt; f32 max_speed = ent->player_max_speed;
struct v2 acc = v2_mul(L.tick.player_move, acc_magnitude); f32 acceleration_rate = ent->player_acceleration;
ent->velocity = v2_add(ent->velocity, acc); 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) { /* Apply acceleration to velocity */
ent->velocity = v2_div(ent->velocity, ent->drag); 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));
} }
/* ========================== * /* ========================== *

View File

@ -42,6 +42,9 @@ GLOBAL struct {
b32 panning; b32 panning;
struct v2 panning_from; struct v2 panning_from;
b32 debug_draw;
b32 debug_camera;
/* User thread input */ /* User thread input */
struct sys_mutex sys_events_mutex; struct sys_mutex sys_events_mutex;
struct arena sys_events_arena; struct arena sys_events_arena;
@ -59,16 +62,28 @@ GLOBAL struct {
* Bind state * 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 */ /* TODO: Remove this */
GLOBAL enum user_bind_kind g_binds[SYS_BTN_COUNT] = { GLOBAL enum user_bind_kind g_binds[SYS_BTN_COUNT] = {
[SYS_BTN_W] = USER_BIND_KIND_MOVE_UP, [SYS_BTN_W] = USER_BIND_KIND_MOVE_UP,
[SYS_BTN_S] = USER_BIND_KIND_MOVE_DOWN, [SYS_BTN_S] = USER_BIND_KIND_MOVE_DOWN,
[SYS_BTN_A] = USER_BIND_KIND_MOVE_LEFT, [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 * Window -> user communication
@ -246,6 +261,22 @@ INTERNAL void debug_draw_xform(struct mat3x3 mtx)
draw_solid_quad(L.world_canvas, quad, color); 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) INTERNAL void user_update(void)
{ {
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
@ -276,7 +307,13 @@ INTERNAL void user_update(void)
* Read sys events * 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) { for (u64 entity_index = 0; entity_index < events.count; ++entity_index) {
struct sys_event *event = &events.events[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 (event->kind == SYS_EVENT_KIND_BUTTON_UP) {
#if DEVELOPER #if DEVELOPER
/* Escape quit */ /* Escape quit */
@ -331,12 +359,16 @@ INTERNAL void user_update(void)
} }
/* Bind */ /* 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; enum sys_btn button = event->button;
button = button >= SYS_BTN_COUNT ? SYS_BTN_NONE : button; button = button >= SYS_BTN_COUNT ? SYS_BTN_NONE : button;
enum user_bind_kind bind = g_binds[button]; enum user_bind_kind bind = g_binds[button];
if (bind) { 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 */ /* 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) { if (input_zooms != 0) {
i32 dir = input_zooms >= 0 ? 1 : -1; i32 dir = input_zooms >= 0 ? 1 : -1;
u32 zooms_abs = input_zooms >= 0 ? input_zooms : -input_zooms; u32 zooms_abs = input_zooms >= 0 ? input_zooms : -input_zooms;
@ -377,15 +410,18 @@ INTERNAL void user_update(void)
L.world_view.center = v2_add(L.world_view.center, offset); L.world_view.center = v2_add(L.world_view.center, offset);
} }
/* Process binds */ /* Movement */
{
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_state); ++bind) { for (enum user_bind_kind bind = 0; bind < (i32)ARRAY_COUNT(g_bind_states); ++bind) {
b32 state = g_bind_state[bind]; struct bind_state state = g_bind_states[bind];
if (!state) {
if (!state.pressed && state.num_presses <= 0) {
continue; continue;
} }
switch (bind) { switch (bind) {
/* Movement */
case USER_BIND_KIND_MOVE_UP: { case USER_BIND_KIND_MOVE_UP: {
input_move_dir.y -= 1; input_move_dir.y -= 1;
} break; } break;
@ -405,13 +441,19 @@ INTERNAL void user_update(void)
default: break; default: break;
} }
} }
input_move_dir = v2_norm(input_move_dir);
/* Queue 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,
.dir = input_move_dir .dir = v2_norm(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 * 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->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->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_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->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_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 #else
@ -509,17 +554,17 @@ INTERNAL void user_update(void)
struct entity *ent = &tick->entities[entity_index]; struct entity *ent = &tick->entities[entity_index];
if (!ent->valid) continue; if (!ent->valid) continue;
b32 is_camera = entity_has_prop(ent, ENTITY_PROP_CAMERA);
/* Update view */ /* Update view */
if (entity_has_prop(ent, ENTITY_PROP_CAMERA)) { if (is_camera && ent->camera_active && !L.debug_camera) {
if (ent->camera_active) {
struct v2 center = mat3x3_get_pos(ent->world_xform); struct v2 center = mat3x3_get_pos(ent->world_xform);
f32 rot = ent->camera_rot; f32 rot = ent->camera_rot;
f32 zoom = ent->camera_zoom; f32 zoom = ent->camera_zoom;
zoom = zoom > 0 ? zoom : 1.0; zoom = zoom > 0 ? zoom : 1;
L.world_view.center = center; L.world_view.center = center;
L.world_view.rot = rot; L.world_view.rot = rot;
L.world_view.zoom = 1; L.world_view.zoom = zoom;
}
} }
/* Draw sprite */ /* Draw sprite */
@ -550,6 +595,7 @@ INTERNAL void user_update(void)
draw_texture_quad(L.world_canvas, params, quad); draw_texture_quad(L.world_canvas, params, quad);
} }
if (L.debug_draw && !is_camera) {
#if 0 #if 0
/* Debug draw sprite quad */ /* Debug draw sprite quad */
{ {
@ -576,11 +622,14 @@ INTERNAL void user_update(void)
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 */ /* Debug draw info */
{ if (L.debug_draw && !is_camera) {
struct mat3x3 mtx = ent->world_xform; 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); 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) { if (disp_font) {
struct string disp_name = ent->sprite_name; 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( struct string fmt = STR(
"sprite name: %F,\n" "sprite name: \"%F\"\n"
"rel rot: %F,\n" "pos: (%F, %F)\n"
"mtx rot: %F,\n" "scale: (%F, %F)\n"
"mtx bx: %F, %F\n" "rot: %F\n"
"velocity: (%F, %F)\n"
"acceleration: (%F, %F)\n"
); );
struct string text = string_format(scratch.arena, fmt, struct string text = string_format(scratch.arena, fmt,
FMT_STR(disp_name), FMT_STR(disp_name),
FMT_FLOAT((f64)ent->rel_trs.r), FMT_FLOAT((f64)trs.t.x), FMT_FLOAT((f64)trs.t.y),
FMT_FLOAT((f64)trs_from_mat3x3(mtx).r), FMT_FLOAT((f64)trs.s.x), FMT_FLOAT((f64)trs.s.y),
FMT_FLOAT((f64)bx.x), FMT_FLOAT((f64)bx.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); 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_xform(ent->world_xform);
debug_draw_movement(ent);
} }
} }
@ -636,34 +684,37 @@ INTERNAL void user_update(void)
}); });
/* Debug draw info */ /* Debug draw info */
{ if (L.debug_draw) {
f32 spacing = 20; f32 spacing = 20;
struct v2 pos = V2(10, 8); struct v2 pos = V2(10, 8);
struct font *font = font_load(STR("res/fonts/fixedsys.ttf"), 12.0f); 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; 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; 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; 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; 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; 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; 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; 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))); 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; 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 */ /* Push game cmds */

View File

@ -11,6 +11,13 @@ enum user_bind_kind {
USER_BIND_KIND_MOVE_LEFT, USER_BIND_KIND_MOVE_LEFT,
USER_BIND_KIND_MOVE_RIGHT, 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 USER_BIND_KIND_COUNT
}; };