From 9da26638c86250e01ae04fc90d74109981c9a291 Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Tue, 22 Oct 2024 20:32:17 +0300 Subject: [PATCH] rework input to be in line with rendering semantics --- apps/demos/bunnymark/game.c | 9 +-- apps/demos/platformer/game.c | 33 +------- apps/demos/platformer/scenes/ingame.c | 7 ++ apps/demos/platformer/scenes/title.c | 2 + apps/demos/scenery/game.c | 33 +------- apps/demos/scenery/scenes/ingame.c | 18 ++++- apps/demos/scenery/scenes/ingame.h | 4 + apps/demos/scenery/scenes/title.c | 2 + include/twn_input.h | 4 - src/twn_engine_context_c.h | 8 +- src/twn_input.c | 105 +++++++++++++------------- src/twn_input_c.h | 1 + src/twn_loop.c | 2 +- 13 files changed, 98 insertions(+), 130 deletions(-) diff --git a/apps/demos/bunnymark/game.c b/apps/demos/bunnymark/game.c index b9de4fb..9a6e335 100644 --- a/apps/demos/bunnymark/game.c +++ b/apps/demos/bunnymark/game.c @@ -67,14 +67,11 @@ void game_tick(void) // Allocating State struct to store data there if (!ctx.udata) ctx.udata = ccalloc(1, sizeof(State)); - - input_add_action("add_a_bit"); - input_bind_action_control("add_a_bit", CONTROL_LEFT_MOUSE); - - input_add_action("add_a_lot"); - input_bind_action_control("add_a_lot", CONTROL_RIGHT_MOUSE); } + input_bind_action_control("add_a_bit", CONTROL_LEFT_MOUSE); + input_bind_action_control("add_a_lot", CONTROL_RIGHT_MOUSE); + State *state = ctx.udata; for (int i = 0; i < state->bunniesCount; i++) diff --git a/apps/demos/platformer/game.c b/apps/demos/platformer/game.c index 027e092..644db16 100644 --- a/apps/demos/platformer/game.c +++ b/apps/demos/platformer/game.c @@ -18,40 +18,13 @@ void game_tick(void) { state->ctx = &ctx; state->scene = title_scene(state); } - - input_add_action("debug_toggle"); - input_bind_action_control("debug_toggle", CONTROL_BACKSPACE); - - input_add_action("debug_dump_atlases"); - input_bind_action_control("debug_dump_atlases", CONTROL_HOME); - - input_add_action("player_left"); - input_bind_action_control("player_left", CONTROL_A); - - input_add_action("player_right"); - input_bind_action_control("player_right", CONTROL_D); - - input_add_action("player_forward"); - input_bind_action_control("player_forward", CONTROL_W); - - input_add_action("player_backward"); - input_bind_action_control("player_backward", CONTROL_S); - - input_add_action("player_jump"); - input_bind_action_control("player_jump", CONTROL_SPACE); - - input_add_action("player_run"); - input_bind_action_control("player_run", CONTROL_LSHIFT); - - input_add_action("ui_accept"); - input_bind_action_control("ui_accept", CONTROL_RETURN); - - input_add_action("mouse_capture_toggle"); - input_bind_action_control("mouse_capture_toggle", CONTROL_ESCAPE); } State *state = ctx.udata; + input_bind_action_control("debug_toggle", CONTROL_BACKSPACE); + input_bind_action_control("debug_dump_atlases", CONTROL_HOME); + if (input_is_action_just_pressed("debug_toggle")) { ctx.debug = !ctx.debug; } diff --git a/apps/demos/platformer/scenes/ingame.c b/apps/demos/platformer/scenes/ingame.c index de4eb8a..c601eb7 100644 --- a/apps/demos/platformer/scenes/ingame.c +++ b/apps/demos/platformer/scenes/ingame.c @@ -11,6 +11,13 @@ static void ingame_tick(State *state) { SceneIngame *scn = (SceneIngame *)state->scene; + input_bind_action_control("player_left", CONTROL_A); + input_bind_action_control("player_right", CONTROL_D); + input_bind_action_control("player_forward", CONTROL_W); + input_bind_action_control("player_backward", CONTROL_S); + input_bind_action_control("player_jump", CONTROL_SPACE); + input_bind_action_control("player_run", CONTROL_LSHIFT); + world_drawdef(scn->world); player_calc(scn->player); diff --git a/apps/demos/platformer/scenes/title.c b/apps/demos/platformer/scenes/title.c index 2acf6ae..9820171 100644 --- a/apps/demos/platformer/scenes/title.c +++ b/apps/demos/platformer/scenes/title.c @@ -14,6 +14,8 @@ static void title_tick(State *state) { SceneTitle *scn = (SceneTitle *)state->scene; (void)scn; + input_bind_action_control("ui_accept", CONTROL_RETURN); + if (input_is_action_just_pressed("ui_accept")) { switch_to(state, ingame_scene); return; diff --git a/apps/demos/scenery/game.c b/apps/demos/scenery/game.c index df95747..928502c 100644 --- a/apps/demos/scenery/game.c +++ b/apps/demos/scenery/game.c @@ -19,40 +19,13 @@ void game_tick(void) { state->ctx = &ctx; state->scene = title_scene(state); } - - input_add_action("debug_toggle"); - input_bind_action_control("debug_toggle", CONTROL_BACKSPACE); - - input_add_action("debug_dump_atlases"); - input_bind_action_control("debug_dump_atlases", CONTROL_HOME); - - input_add_action("player_left"); - input_bind_action_control("player_left", CONTROL_A); - - input_add_action("player_right"); - input_bind_action_control("player_right", CONTROL_D); - - input_add_action("player_forward"); - input_bind_action_control("player_forward", CONTROL_W); - - input_add_action("player_backward"); - input_bind_action_control("player_backward", CONTROL_S); - - input_add_action("player_jump"); - input_bind_action_control("player_jump", CONTROL_SPACE); - - input_add_action("player_run"); - input_bind_action_control("player_run", CONTROL_LSHIFT); - - input_add_action("ui_accept"); - input_bind_action_control("ui_accept", CONTROL_RETURN); - - input_add_action("mouse_capture_toggle"); - input_bind_action_control("mouse_capture_toggle", CONTROL_ESCAPE); } State *state = ctx.udata; + input_bind_action_control("debug_toggle", CONTROL_BACKSPACE); + input_bind_action_control("debug_dump_atlases", CONTROL_HOME); + if (input_is_action_just_pressed("debug_toggle")) { ctx.debug = !ctx.debug; } diff --git a/apps/demos/scenery/scenes/ingame.c b/apps/demos/scenery/scenes/ingame.c index c71a560..464dbfc 100644 --- a/apps/demos/scenery/scenes/ingame.c +++ b/apps/demos/scenery/scenes/ingame.c @@ -15,7 +15,15 @@ static void ingame_tick(State *state) { SceneIngame *scn = (SceneIngame *)state->scene; - if (input_is_mouse_captured()) { + input_bind_action_control("player_left", CONTROL_A); + input_bind_action_control("player_right", CONTROL_D); + input_bind_action_control("player_forward", CONTROL_W); + input_bind_action_control("player_backward", CONTROL_S); + input_bind_action_control("player_jump", CONTROL_SPACE); + input_bind_action_control("player_run", CONTROL_LSHIFT); + input_bind_action_control("mouse_capture_toggle", CONTROL_ESCAPE); + + if (scn->mouse_captured) { const float sensitivity = 0.6f; /* TODO: put this in a better place */ scn->yaw += (float)ctx.mouse_movement.x * sensitivity; scn->pitch -= (float)ctx.mouse_movement.y * sensitivity; @@ -52,9 +60,10 @@ static void ingame_tick(State *state) { scn->cam.pos.y -= speed; /* toggle mouse capture with end key */ - if (input_is_action_just_pressed("mouse_capture_toggle")) { - input_set_mouse_captured(!input_is_mouse_captured()); - } + if (input_is_action_just_pressed("mouse_capture_toggle")) + scn->mouse_captured = !scn->mouse_captured; + + input_set_mouse_captured(scn->mouse_captured); draw_camera(&scn->cam); @@ -106,6 +115,7 @@ Scene *ingame_scene(State *state) { new_scene->base.end = ingame_end; new_scene->cam = (Camera){ .pos = { 32, 0, 1 }, .up = { 0, 1, 0 }, .fov = (float)M_PI_2 }; + new_scene->mouse_captured = true; m_audio(m_set(path, "music/mod65.xm"), m_opt(channel, "soundtrack"), diff --git a/apps/demos/scenery/scenes/ingame.h b/apps/demos/scenery/scenes/ingame.h index f3f9012..8eece6e 100644 --- a/apps/demos/scenery/scenes/ingame.h +++ b/apps/demos/scenery/scenes/ingame.h @@ -6,6 +6,8 @@ #include "../state.h" #include "scene.h" +#include + typedef struct SceneIngame { Scene base; @@ -16,6 +18,8 @@ typedef struct SceneIngame { float yaw; float pitch; float roll; + + bool mouse_captured; } SceneIngame; diff --git a/apps/demos/scenery/scenes/title.c b/apps/demos/scenery/scenes/title.c index a642427..de51f55 100644 --- a/apps/demos/scenery/scenes/title.c +++ b/apps/demos/scenery/scenes/title.c @@ -11,6 +11,8 @@ static void title_tick(State *state) { SceneTitle *scn = (SceneTitle *)state->scene; (void)scn; + input_bind_action_control("ui_accept", CONTROL_RETURN); + if (input_is_action_just_pressed("ui_accept")) { switch_to(state, ingame_scene); return; diff --git a/include/twn_input.h b/include/twn_input.h index 87b8952..50200e7 100644 --- a/include/twn_input.h +++ b/include/twn_input.h @@ -13,9 +13,6 @@ TWN_API void input_bind_action_control(const char *action_name, Control control); TWN_API void input_unbind_action_control(const char *action_name, Control control); -TWN_API void input_add_action(const char *action_name); -TWN_API void input_delete_action(const char *action_name); - TWN_API bool input_is_action_pressed(const char *action_name); TWN_API bool input_is_action_just_pressed(const char *action_name); TWN_API bool input_is_action_just_released(const char *action_name); @@ -23,6 +20,5 @@ TWN_API bool input_is_action_just_released(const char *action_name); TWN_API Vec2 input_get_action_position(const char *action_name); TWN_API void input_set_mouse_captured(bool enabled); -TWN_API bool input_is_mouse_captured(void); #endif diff --git a/src/twn_engine_context_c.h b/src/twn_engine_context_c.h index e11ee97..add9f9d 100644 --- a/src/twn_engine_context_c.h +++ b/src/twn_engine_context_c.h @@ -64,15 +64,15 @@ typedef struct EngineContext { int64_t delta_averager_residual; int64_t time_averager[4]; + SDL_GLContext *gl_context; + SDL_Window *window; + uint32_t window_id; + /* this should be a multiple of the current ticks per second */ /* use it to simulate low framerate (e.g. at 60 tps, set to 2 for 30 fps) */ /* it can be changed at runtime; any resulting logic anomalies are bugs */ uint32_t update_multiplicity; - SDL_GLContext *gl_context; - SDL_Window *window; - uint32_t window_id; - bool is_running; bool window_size_has_changed; bool resync_flag; diff --git a/src/twn_input.c b/src/twn_input.c index f8920cb..dafc365 100644 --- a/src/twn_input.c +++ b/src/twn_input.c @@ -9,7 +9,6 @@ #include #include -#include static void update_action_pressed_state(InputState *input, Action *action) { @@ -71,20 +70,40 @@ static void update_action_pressed_state(InputState *input, Action *action) { } +static ActionHashItem *input_add_action(char const *action_name) { + SDL_assert(action_name); + + Action new_action = { 0 }; + new_action.bindings = SDL_calloc(ctx.keybind_slots, sizeof *new_action.bindings); + shput(ctx.input.action_hash, action_name, new_action); + return shgetp(ctx.input.action_hash, action_name); +} + + +static void input_delete_action(char const *action_name) { + SDL_assert(action_name); + + ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name); + SDL_assert(action); + + SDL_free(action->value.bindings); + shdel(ctx.input.action_hash, action_name); +} + + static void input_bind_code_to_action(InputState *input, char const *action_name, ButtonSource source, union ButtonCode code) { ActionHashItem *action_item = shgetp_null(input->action_hash, action_name); - if (action_item == NULL) { - log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); - return; - } + if (!action_item) + action_item = input_add_action(action_name); + Action *action = &action_item->value; /* check every binding to make sure this code isn't already bound */ - for (size_t i = 0; i < (uint64_t)ctx.keybind_slots; ++i) { + for (size_t i = 0; i < action->num_bindings; ++i) { Button *binding = &action->bindings[i]; if (binding->source != source) @@ -111,7 +130,8 @@ static void input_bind_code_to_action(InputState *input, } if (is_already_bound) { - log_warn("(%s) Code already bound to action \"%s\".", __func__, action_name); + /* keep it alive */ + binding->in_use = true; return; } } @@ -119,13 +139,14 @@ static void input_bind_code_to_action(InputState *input, /* if we're at max bindings, forget the first element and shift the rest */ if (action->num_bindings == (uint64_t)ctx.keybind_slots) { --action->num_bindings; - size_t shifted_size = (sizeof action->bindings) - (sizeof action->bindings[0]); + size_t shifted_size = sizeof action->bindings[0] * (ctx.keybind_slots - 1); SDL_memmove(action->bindings, action->bindings + 1, shifted_size); } action->bindings[action->num_bindings++] = (Button) { .source = source, .code = code, + .in_use = true, }; } @@ -136,16 +157,15 @@ static void input_unbind_code_from_action(InputState *input, union ButtonCode code) { ActionHashItem *action_item = shgetp_null(input->action_hash, action_name); - if (action_item == NULL) { - log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); - return; - } + if (!action_item) + action_item = input_add_action(action_name); + Action *action = &action_item->value; /* check every binding to make sure this code is bound */ size_t index = 0; bool is_bound = false; - for (index = 0; index < (uint64_t)ctx.keybind_slots; ++index) { + for (index = 0; index < action->num_bindings; ++index) { Button *binding = &action->bindings[index]; if (binding->source != source) @@ -175,10 +195,8 @@ static void input_unbind_code_from_action(InputState *input, break; } - if (!is_bound) { - log_warn("(%s) Code is not bound to action \"%s\".", __func__, action_name); + if (!is_bound) return; - } /* remove the element to unbind and shift the rest so there isn't a gap */ size_t elements_after_index = action->num_bindings - index; @@ -199,6 +217,10 @@ void input_state_deinit(InputState *input) { void input_state_update(InputState *input) { + /* TODO: don't spam it if it happens */ + if (SDL_SetRelativeMouseMode(input->mouse_captured) != 0) + log_warn("(%s) Mouse capture isn't supported.", __func__); + input->keyboard_state = SDL_GetKeyboardState(NULL); input->mouse_state = SDL_GetMouseState(&input->mouse_window_position.x, &input->mouse_window_position.y); @@ -211,8 +233,23 @@ void input_state_update(InputState *input) { for (size_t i = 0; i < shlenu(input->action_hash); ++i) { Action *action = &input->action_hash[i].value; + /* collect unused */ + for (size_t u = 0; u < action->num_bindings; ++u) { + Button *button = &action->bindings[u]; + if (!button->in_use) + input_unbind_code_from_action(input, input->action_hash[i].key, button->source, button->code); + else + button->in_use = false; + } + update_action_pressed_state(input, action); } + + size_t removed = 0; + for (size_t i = 0; i < shlenu(input->action_hash); ++i) { + if (input->action_hash[i - removed].value.num_bindings == 0) + input_delete_action(input->action_hash[i - removed].key); + } } @@ -260,34 +297,6 @@ void input_unbind_action_control(char const *action_name, } -void input_add_action(char const *action_name) { - SDL_assert_always(action_name); - - if (shgeti(ctx.input.action_hash, action_name) >= 0) { - log_warn("(%s) Action \"%s\" is already registered.", __func__, action_name); - return; - } - - Action new_action = { 0 }; - new_action.bindings = ccalloc(ctx.keybind_slots, sizeof *new_action.bindings); - shput(ctx.input.action_hash, action_name, new_action); -} - - -void input_delete_action(char const *action_name) { - SDL_assert_always(action_name); - - ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name); - if (action == NULL) { - log_warn("(%s) Action \"%s\" is not registered.", __func__, action_name); - return; - } - - SDL_free(action->value.bindings); - shdel(ctx.input.action_hash, action_name); -} - - bool input_is_action_pressed(char const *action_name) { SDL_assert_always(action_name); @@ -338,13 +347,7 @@ Vec2 input_get_action_position(char const *action_name) { void input_set_mouse_captured(bool enabled) { - if (SDL_SetRelativeMouseMode(enabled) != 0) - log_warn("(%s) Mouse capture isn't supported.", __func__); -} - - -bool input_is_mouse_captured(void) { - return SDL_GetRelativeMouseMode(); + ctx.input.mouse_captured = enabled; } diff --git a/src/twn_input_c.h b/src/twn_input_c.h index df7ebb3..7e134bf 100644 --- a/src/twn_input_c.h +++ b/src/twn_input_c.h @@ -33,6 +33,7 @@ typedef enum ButtonSource { typedef struct Button { enum ButtonSource source; union ButtonCode code; + bool in_use; } Button; diff --git a/src/twn_loop.c b/src/twn_loop.c index c2c4fbb..9ac88a6 100644 --- a/src/twn_loop.c +++ b/src/twn_loop.c @@ -204,8 +204,8 @@ static void main_loop(void) { /* TODO: disable rendering pushes on not-last ? */ render_queue_clear(); poll_events(); - input_state_update(&ctx.input); game_object_tick(); + input_state_update(&ctx.input); preserve_persistent_ctx_fields(); ctx.frame_accumulator -= ctx.desired_frametime;