diff --git a/src/Asteroid.c b/src/Asteroid.c index 2757d0e..eb53c61 100644 --- a/src/Asteroid.c +++ b/src/Asteroid.c @@ -147,68 +147,6 @@ void initRenderer(GameState *state, v2 windowSize) } } -enum ReadKeyType -{ - readkeytype_oneShot, - readkeytype_delayedRepeat, - readkeytype_repeat, - readkeytype_count, -}; - -#define KEY_DELAY_NONE 0.0f -INTERNAL b32 getKeyStatus(KeyState *key, enum ReadKeyType readType, - f32 delayInterval, f32 dt) -{ - - // TODO(doyle): Don't let get key status modify keyinput state - if (!key->endedDown) return FALSE; - - switch(readType) - { - case readkeytype_oneShot: - { - if (key->newHalfTransitionCount > key->oldHalfTransitionCount) - return TRUE; - break; - } - case readkeytype_repeat: - case readkeytype_delayedRepeat: - { - if (key->newHalfTransitionCount > key->oldHalfTransitionCount) - { - if (readType == readkeytype_delayedRepeat) - { - // TODO(doyle): Let user set arbitrary delay after initial input - key->delayInterval = 2 * delayInterval; - } - else - { - key->delayInterval = delayInterval; - } - return TRUE; - } - else if (key->delayInterval <= 0.0f) - { - key->delayInterval = delayInterval; - return TRUE; - } - else - { - key->delayInterval -= dt; - } - break; - } - default: -#ifdef DENGINE_DEBUG - DEBUG_LOG("getKeyStatus() error: Invalid ReadKeyType enum"); - ASSERT(INVALID_CODE_PATH); -#endif - break; - } - - return FALSE; -} - #include #include v2 *createAsteroidVertexList(MemoryArena_ *arena, i32 iterations, @@ -697,33 +635,10 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, for (u32 i = world->asteroidCounter; i < world->numAsteroids; i++) addAsteroid(world, (rand() % asteroidsize_count)); - { - KeyState *keys = state->input.keys; - for (enum KeyCode code = 0; code < keycode_count; code++) - { - KeyState *keyState = &keys[code]; + platform_processInputBuffer(&state->input, dt); - u32 halfTransitionCount = keyState->newHalfTransitionCount - - keyState->oldHalfTransitionCount; - - if (halfTransitionCount > 0) - { - b32 transitionCountIsOdd = ((halfTransitionCount & 1) == 1); - - if (transitionCountIsOdd) - { - if (keyState->endedDown) keyState->endedDown = FALSE; - else keyState->endedDown = TRUE; - } - - keyState->oldHalfTransitionCount = - keyState->newHalfTransitionCount; - } - } - } - - if (getKeyStatus(&state->input.keys[keycode_left_square_bracket], - readkeytype_repeat, 0.2f, dt)) + if (platform_queryKey(&state->input.keys[keycode_left_square_bracket], + readkeytype_repeat, 0.2f)) { addAsteroid(world, (rand() % asteroidsize_count)); } @@ -739,8 +654,8 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, v2 ddP = {0}; if (entity->type == entitytype_ship) { - if (getKeyStatus(&state->input.keys[keycode_up], readkeytype_repeat, - 0.0f, dt)) + if (platform_queryKey(&state->input.keys[keycode_up], + readkeytype_repeat, 0.0f)) { // TODO(doyle): Renderer creates upfacing triangles by default, // but we need to offset rotation so that our base "0 degrees" @@ -753,20 +668,20 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, } Degrees rotationsPerSecond = 180.0f; - if (getKeyStatus(&state->input.keys[keycode_left], - readkeytype_repeat, 0.0f, dt)) + if (platform_queryKey(&state->input.keys[keycode_left], + readkeytype_repeat, 0.0f)) { - entity->rotation += (rotationsPerSecond) * dt; + entity->rotation += (rotationsPerSecond)*dt; } - if (getKeyStatus(&state->input.keys[keycode_right], - readkeytype_repeat, 0.0f, dt)) + if (platform_queryKey(&state->input.keys[keycode_right], + readkeytype_repeat, 0.0f)) { - entity->rotation -= (rotationsPerSecond) * dt; + entity->rotation -= (rotationsPerSecond)*dt; } - if (getKeyStatus(&state->input.keys[keycode_space], - readkeytype_delayedRepeat, 0.05f, dt)) + if (platform_queryKey(&state->input.keys[keycode_space], + readkeytype_one_shot, KEY_DELAY_NONE)) { addBullet(world, entity); @@ -882,11 +797,9 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, continue; } - f32 dPMultiplier = 10; Radians rotation = DEGREES_TO_RADIANS((entity->rotation + 90.0f)); - ddP = V2(math_cosf(rotation), math_sinf(rotation)); - entity->dP = v2_scale(ddP, world->pixelsPerMeter * dPMultiplier); - + v2 localDp = V2(math_cosf(rotation), math_sinf(rotation)); + entity->dP = v2_scale(localDp, world->pixelsPerMeter * 10); } /* Loop entity around world */ diff --git a/src/Platform.c b/src/Platform.c index c874b78..ce85ba2 100644 --- a/src/Platform.c +++ b/src/Platform.c @@ -94,3 +94,88 @@ i32 platform_readFileToBuffer(MemoryArena_ *arena, const char *const filePath, return 0; } + +void platform_processInputBuffer(InputBuffer *inputBuffer, f32 dt) +{ + KeyState *keyBuffer = inputBuffer->keys; + for (enum KeyCode code = 0; code < keycode_count; code++) + { + KeyState *key = &keyBuffer[code]; + + u32 halfTransitionCount = + key->newHalfTransitionCount - key->oldHalfTransitionCount; + + if (halfTransitionCount > 0) + { + b32 transitionCountIsOdd = ((halfTransitionCount & 1) == 1); + if (transitionCountIsOdd) + { + /* If it was not last ended down, then update interval if + * necessary */ + if (!common_isSet(key->flags, keystateflag_ended_down)) + { + if (key->delayInterval > 0) key->delayInterval -= dt; + + key->flags |= keystateflag_pressed_on_curr_frame; + } + key->flags ^= keystateflag_ended_down; + } + } + else + { + key->flags &= (~keystateflag_pressed_on_curr_frame); + } + + key->newHalfTransitionCount = key->oldHalfTransitionCount; + } +} + +b32 platform_queryKey(KeyState *key, enum ReadKeyType readType, + f32 delayInterval) +{ + + if (!common_isSet(key->flags, keystateflag_ended_down)) return FALSE; + + switch (readType) + { + case readkeytype_one_shot: + { + if (common_isSet(key->flags, keystateflag_pressed_on_curr_frame)) + return TRUE; + } + break; + + case readkeytype_repeat: + case readkeytype_delay_repeat: + { + if (common_isSet(key->flags, keystateflag_pressed_on_curr_frame)) + { + if (readType == readkeytype_delay_repeat) + { + // TODO(doyle): Let user set arbitrary delay after initial input + key->delayInterval = 2 * delayInterval; + } + else + { + key->delayInterval = delayInterval; + } + return TRUE; + } + else if (key->delayInterval <= 0.0f) + { + key->delayInterval = delayInterval; + return TRUE; + } + } + break; + + default: + { + ASSERT(INVALID_CODE_PATH); + } + break; + } + + return FALSE; +} + diff --git a/src/UserInterface.c b/src/UserInterface.c index 9c00c1d..7487468 100644 --- a/src/UserInterface.c +++ b/src/UserInterface.c @@ -7,7 +7,7 @@ i32 userInterface_button(UiState *const uiState, MemoryArena_ *const arena, AssetManager *const assetManager, Renderer *const renderer, Font *const font, - const KeyInput input, const i32 id, const Rect rect, + const InputBuffer input, const i32 id, const Rect rect, const char *const label) { if (math_pointInRect(rect, input.mouseP)) @@ -23,7 +23,8 @@ i32 userInterface_button(UiState *const uiState, MemoryArena_ *const arena, uiState->activeItem == uiState->debugWindow.id || uiState->activeItem == 0) { - if (input.keys[keycode_mouseLeft].endedDown) + if (common_isSet(input.keys[keycode_mouseLeft].flags, + keystateflag_ended_down)) { uiState->activeItem = id; } @@ -124,9 +125,9 @@ i32 userInterface_button(UiState *const uiState, MemoryArena_ *const arena, // If button is hot and active, but mouse button is not // down, the user must have clicked the button. - if (!input.keys[keycode_mouseLeft].endedDown && - uiState->hotItem == id && - uiState->activeItem == id) + if (!common_isSet(input.keys[keycode_mouseLeft].flags, + keystateflag_ended_down) && + uiState->hotItem == id && uiState->activeItem == id) { return id; } @@ -136,7 +137,7 @@ i32 userInterface_button(UiState *const uiState, MemoryArena_ *const arena, i32 userInterface_scrollbar(UiState *const uiState, AssetManager *const assetManager, - Renderer *const renderer, const KeyInput input, + Renderer *const renderer, const InputBuffer input, const i32 id, const Rect scrollBarRect, i32 *const value, const i32 maxValue) { @@ -151,7 +152,8 @@ i32 userInterface_scrollbar(UiState *const uiState, uiState->activeItem == uiState->debugWindow.id || uiState->activeItem == 0) { - if (input.keys[keycode_mouseLeft].endedDown) + if (common_isSet(input.keys[keycode_mouseLeft].flags, + keystateflag_ended_down)) { uiState->activeItem = id; } @@ -253,7 +255,7 @@ i32 userInterface_scrollbar(UiState *const uiState, i32 userInterface_textField(UiState *const uiState, MemoryArena_ *const arena, AssetManager *const assetManager, Renderer *const renderer, Font *const font, - KeyInput input, const i32 id, const Rect rect, + InputBuffer input, const i32 id, const Rect rect, char *const string) { i32 strLen = common_strlen(string); @@ -266,7 +268,8 @@ i32 userInterface_textField(UiState *const uiState, MemoryArena_ *const arena, uiState->activeItem == uiState->debugWindow.id || uiState->activeItem == 0) { - if (input.keys[keycode_mouseLeft].endedDown) + if (common_isSet(input.keys[keycode_mouseLeft].flags, + keystateflag_ended_down)) { uiState->activeItem = id; } @@ -338,9 +341,9 @@ i32 userInterface_textField(UiState *const uiState, MemoryArena_ *const arena, } } - if (!input.keys[keycode_mouseLeft].endedDown && - uiState->hotItem == id && - uiState->activeItem == id) + if (!common_isSet(input.keys[keycode_mouseLeft].flags, + keystateflag_ended_down) && + uiState->hotItem == id && uiState->activeItem == id) { uiState->kbdItem = id; } @@ -354,12 +357,14 @@ i32 userInterface_textField(UiState *const uiState, MemoryArena_ *const arena, i32 userInterface_window(UiState *const uiState, MemoryArena_ *const arena, AssetManager *const assetManager, Renderer *const renderer, Font *const font, - const KeyInput input, WindowState *window) + const InputBuffer input, WindowState *window) { if (math_pointInRect(window->rect, input.mouseP)) { uiState->hotItem = window->id; - if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown) + if (uiState->activeItem == 0 && + common_isSet(input.keys[keycode_mouseLeft].flags, + keystateflag_ended_down)) uiState->activeItem = window->id; } @@ -425,7 +430,8 @@ i32 userInterface_window(UiState *const uiState, MemoryArena_ *const arena, if (window->windowHeld) { - if (!input.keys[keycode_mouseLeft].endedDown) + if (!common_isSet(input.keys[keycode_mouseLeft].flags, + keystateflag_ended_down)) { window->windowHeld = FALSE; window->prevFrameWindowHeld = FALSE; @@ -458,10 +464,9 @@ i32 userInterface_window(UiState *const uiState, MemoryArena_ *const arena, window->prevMouseP = input.mouseP; } - - if (!input.keys[keycode_mouseLeft].endedDown && - uiState->hotItem == window->id && - uiState->activeItem == window->id) + if (!common_isSet(input.keys[keycode_mouseLeft].flags, + keystateflag_ended_down) && + uiState->hotItem == window->id && uiState->activeItem == window->id) { return window->id; } diff --git a/src/dengine.c b/src/dengine.c index 5ade1fc..016183e 100644 --- a/src/dengine.c +++ b/src/dengine.c @@ -56,7 +56,8 @@ INTERNAL void keyCallback(GLFWwindow *window, int key, int scancode, int action, i32 offset = 0; if (key >= 'A' && key <= 'Z') { - if (!game->input.keys[keycode_leftShift].endedDown) + KeyState *leftShiftKey = &game->input.keys[keycode_leftShift]; + if (!common_isSet(leftShiftKey->flags, keystateflag_ended_down)) offset = 'a' - 'A'; } diff --git a/src/include/Dengine/Asteroid.h b/src/include/Dengine/Asteroid.h index e37b8db..2ec46b4 100644 --- a/src/include/Dengine/Asteroid.h +++ b/src/include/Dengine/Asteroid.h @@ -46,7 +46,7 @@ typedef struct GameState { AudioManager audioManager; AssetManager assetManager; - KeyInput input; + InputBuffer input; Renderer renderer; World world; diff --git a/src/include/Dengine/Common.h b/src/include/Dengine/Common.h index bd525f0..5b5e2bb 100644 --- a/src/include/Dengine/Common.h +++ b/src/include/Dengine/Common.h @@ -44,6 +44,13 @@ u8 *common_memset(u8 *const ptr, const i32 value, const i32 numBytes); void common_itoa(i32 value, char *buf, i32 bufSize); i32 common_atoi(const char *string, const i32 len); +inline b32 common_isSet(u32 bitfield, u32 flags) +{ + b32 result = FALSE; + if ((bitfield & flags) == flags) result = TRUE; + + return result; +} //----------------------------------------------------------------------------- // MurmurHash2, by Austin Appleby diff --git a/src/include/Dengine/Platform.h b/src/include/Dengine/Platform.h index 159cff2..d9c2077 100644 --- a/src/include/Dengine/Platform.h +++ b/src/include/Dengine/Platform.h @@ -119,19 +119,33 @@ enum KeyCode keycode_null, }; +/* + NOTE(doyle): EndedDown describes the last state the key was in and is + _NOT_ the same as pressed on current frame. They key may of have been + held down over multiple frames, so endedDown will remain true. + + Pressed on current frame captures if it was just pressed on this frame + only. +*/ +enum KeyStateFlag +{ + keystateflag_ended_down = (1 << 0), + keystateflag_pressed_on_curr_frame = (1 << 1), +}; + typedef struct KeyState { f32 delayInterval; u32 oldHalfTransitionCount; u32 newHalfTransitionCount; - b32 endedDown; + u32 flags; } KeyState; -typedef struct KeyInput +typedef struct InputBuffer { v2 mouseP; KeyState keys[keycode_count]; -} KeyInput; +} InputBuffer; typedef struct PlatformFileRead { @@ -155,4 +169,22 @@ void platform_closeFileRead(MemoryArena_ *arena, PlatformFileRead *file); i32 platform_readFileToBuffer(MemoryArena_ *arena, const char *const filePath, PlatformFileRead *file); +/* + NOTE(doyle): The keyinput functions are technically not for "communicating to + the platform layer", but I've decided to group it here alongside the input + data definitions. Since we require that the platform layer writes directly + into our input buffer located in the game state. + */ +#define KEY_DELAY_NONE 0.0f + +enum ReadKeyType +{ + readkeytype_one_shot, + readkeytype_delay_repeat, + readkeytype_repeat, + readkeytype_count, +}; +void platform_processInputBuffer(InputBuffer *inputBuffer, f32 dt); +b32 platform_queryKey(KeyState *key, enum ReadKeyType readType, + f32 delayInterval); #endif diff --git a/src/include/Dengine/UserInterface.h b/src/include/Dengine/UserInterface.h index e469514..6bf7d0e 100644 --- a/src/include/Dengine/UserInterface.h +++ b/src/include/Dengine/UserInterface.h @@ -79,23 +79,23 @@ inline i32 userInterface_generateId(UiState *const uiState) i32 userInterface_button(UiState *const uiState, MemoryArena_ *const arena, AssetManager *const assetManager, Renderer *const renderer, Font *const font, - const KeyInput input, const i32 id, const Rect rect, + const InputBuffer input, const i32 id, const Rect rect, const char *const label); i32 userInterface_textField(UiState *const uiState, MemoryArena_ *const arena, AssetManager *const assetManager, Renderer *const renderer, Font *const font, - KeyInput input, const i32 id, const Rect rect, + InputBuffer input, const i32 id, const Rect rect, char *const string); i32 userInterface_scrollbar(UiState *const uiState, AssetManager *const assetManager, - Renderer *const renderer, const KeyInput input, + Renderer *const renderer, const InputBuffer input, const i32 id, const Rect scrollBarRect, i32 *const value, const i32 maxValue); i32 userInterface_window(UiState *const uiState, MemoryArena_ *const arena, AssetManager *const assetManager, Renderer *const renderer, Font *const font, - const KeyInput input, WindowState *window); + const InputBuffer input, WindowState *window); #endif