Add better input parsing with key repeat tracking
This commit is contained in:
parent
22973ef51d
commit
80b35d404d
Binary file not shown.
@ -352,6 +352,42 @@ INTERNAL inline v4 getEntityScreenRect(Entity entity)
|
||||
return result;
|
||||
}
|
||||
|
||||
enum ReadKeyType
|
||||
{
|
||||
readkeytype_oneShot,
|
||||
readkeytype_delayedRepeat,
|
||||
readkeytype_repeat,
|
||||
readkeytype_count,
|
||||
};
|
||||
|
||||
INTERNAL b32 getKeyStatus(KeyState key, enum ReadKeyType readType)
|
||||
{
|
||||
switch(readType)
|
||||
{
|
||||
case readkeytype_oneShot:
|
||||
{
|
||||
if (key.endedDown &&
|
||||
(key.newHalfTransitionCount > key.oldHalfTransitionCount))
|
||||
return TRUE;
|
||||
break;
|
||||
}
|
||||
case readkeytype_repeat:
|
||||
{
|
||||
if (key.endedDown) return TRUE;
|
||||
break;
|
||||
}
|
||||
case readkeytype_delayedRepeat:
|
||||
default:
|
||||
// TODO(doyle): Add delayed repeat of keys
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(INVALID_CODE_PATH);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
INTERNAL void parseInput(GameState *state, const f32 dt)
|
||||
{
|
||||
/*
|
||||
@ -370,31 +406,52 @@ INTERNAL void parseInput(GameState *state, const f32 dt)
|
||||
Entity *hero = &world->entities[entity_getIndex(world, world->heroId)];
|
||||
v2 ddPos = V2(0, 0);
|
||||
|
||||
KeyState *keys = state->input.keys;
|
||||
for (i32 i = 0; i < keycode_count; i++)
|
||||
{
|
||||
KeyState *currKey = &keys[i];
|
||||
if (currKey->newHalfTransitionCount > currKey->oldHalfTransitionCount)
|
||||
{
|
||||
i32 numTransitions = currKey->newHalfTransitionCount -
|
||||
currKey->oldHalfTransitionCount;
|
||||
|
||||
if ((numTransitions & 1) == 1)
|
||||
{
|
||||
currKey->endedDown = ~currKey->endedDown;
|
||||
}
|
||||
|
||||
// TODO(doyle): Multi press within frame override UI input parsing
|
||||
if ((i >= keycode_A && i <= keycode_Z) ||
|
||||
(i >= keycode_a && i <= keycode_z))
|
||||
{
|
||||
if (getKeyStatus(*currKey, readkeytype_oneShot))
|
||||
{
|
||||
state->uiState.keyChar = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hero->stats->busyDuration <= 0)
|
||||
{
|
||||
// TODO(doyle): As we need to handle more key spam input, we want to
|
||||
// track
|
||||
// if a button ended down
|
||||
LOCAL_PERSIST b32 spaceBarWasDown = FALSE;
|
||||
|
||||
if (state->input.keys[keycode_right])
|
||||
if (getKeyStatus(keys[keycode_right], readkeytype_repeat))
|
||||
{
|
||||
ddPos.x = 1.0f;
|
||||
hero->direction = direction_east;
|
||||
}
|
||||
|
||||
if (state->input.keys[keycode_left])
|
||||
if (getKeyStatus(keys[keycode_left], readkeytype_repeat))
|
||||
{
|
||||
ddPos.x = -1.0f;
|
||||
hero->direction = direction_west;
|
||||
}
|
||||
|
||||
if (state->input.keys[keycode_up])
|
||||
if (getKeyStatus(keys[keycode_up], readkeytype_repeat))
|
||||
{
|
||||
ddPos.y = 1.0f;
|
||||
}
|
||||
|
||||
if (state->input.keys[keycode_down])
|
||||
if (getKeyStatus(keys[keycode_down], readkeytype_repeat))
|
||||
{
|
||||
ddPos.y = -1.0f;
|
||||
}
|
||||
@ -408,18 +465,16 @@ INTERNAL void parseInput(GameState *state, const f32 dt)
|
||||
ddPos = v2_scale(ddPos, 0.70710678118f);
|
||||
}
|
||||
|
||||
if (state->input.keys[keycode_enter])
|
||||
if (getKeyStatus(keys[keycode_enter], readkeytype_oneShot))
|
||||
{
|
||||
state->uiState.keyEntered = keycode_enter;
|
||||
}
|
||||
|
||||
LOCAL_PERSIST b32 toggleFlag = TRUE;
|
||||
// TODO(doyle): Revisit key input with state checking for last ended down
|
||||
if (state->input.keys[keycode_space] && spaceBarWasDown == FALSE)
|
||||
if (getKeyStatus(keys[keycode_space], readkeytype_oneShot))
|
||||
{
|
||||
state->uiState.keyEntered = keycode_space;
|
||||
DEBUG_LOG("push space");
|
||||
|
||||
#if 0
|
||||
Renderer *renderer = &state->renderer;
|
||||
f32 yPos = CAST(f32)(rand() % CAST(i32)renderer->size.h);
|
||||
f32 xModifier = 5.0f - CAST(f32)(rand() % 3);
|
||||
@ -427,12 +482,6 @@ INTERNAL void parseInput(GameState *state, const f32 dt)
|
||||
v2 pos = V2(renderer->size.w - (renderer->size.w / xModifier), yPos);
|
||||
entity_addGenericMob(&state->arena, &state->assetManager, world,
|
||||
pos);
|
||||
#endif
|
||||
spaceBarWasDown = TRUE;
|
||||
}
|
||||
else if (!state->input.keys[keycode_space])
|
||||
{
|
||||
spaceBarWasDown = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -455,7 +504,7 @@ INTERNAL void parseInput(GameState *state, const f32 dt)
|
||||
}
|
||||
|
||||
f32 heroSpeed = 6.2f * METERS_TO_PIXEL;
|
||||
if (state->input.keys[keycode_leftShift])
|
||||
if (state->input.keys[keycode_leftShift].endedDown)
|
||||
{
|
||||
// TODO: Context sensitive command separation
|
||||
state->uiState.keyMod = keycode_leftShift;
|
||||
@ -898,7 +947,7 @@ INTERNAL i32 button(UiState *uiState, AssetManager *assetManager,
|
||||
if (math_pointInRect(rect, input.mouseP))
|
||||
{
|
||||
uiState->hotItem = id;
|
||||
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft])
|
||||
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown)
|
||||
uiState->activeItem = id;
|
||||
}
|
||||
|
||||
@ -963,7 +1012,7 @@ INTERNAL i32 button(UiState *uiState, AssetManager *assetManager,
|
||||
|
||||
uiState->lastWidget = id;
|
||||
|
||||
if (!input.keys[keycode_mouseLeft] &&
|
||||
if (!input.keys[keycode_mouseLeft].endedDown &&
|
||||
uiState->hotItem == id &&
|
||||
uiState->activeItem == id)
|
||||
{
|
||||
@ -984,7 +1033,7 @@ INTERNAL i32 scrollBar(UiState *uiState, AssetManager *assetManager,
|
||||
if (math_pointInRect(scrollBarRect, input.mouseP))
|
||||
{
|
||||
uiState->hotItem = id;
|
||||
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft])
|
||||
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown)
|
||||
uiState->activeItem = id;
|
||||
}
|
||||
|
||||
@ -1095,7 +1144,7 @@ INTERNAL i32 textField(UiState *const uiState, MemoryArena *arena,
|
||||
if (math_pointInRect(textRect, input.mouseP))
|
||||
{
|
||||
uiState->hotItem = id;
|
||||
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft])
|
||||
if (uiState->activeItem == 0 && input.keys[keycode_mouseLeft].endedDown)
|
||||
{
|
||||
uiState->activeItem = id;
|
||||
}
|
||||
@ -1158,15 +1207,15 @@ INTERNAL i32 textField(UiState *const uiState, MemoryArena *arena,
|
||||
}
|
||||
|
||||
if (uiState->keyChar >= keycode_space &&
|
||||
uiState->keyChar <= keycode_Z && strLen < 30)
|
||||
uiState->keyChar <= keycode_tilda && strLen < 30)
|
||||
{
|
||||
string[strLen++] = uiState->keyChar;
|
||||
string[strLen++] = uiState->keyChar + ' ';
|
||||
string[strLen] = 0;
|
||||
changed = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!input.keys[keycode_mouseLeft] &&
|
||||
if (!input.keys[keycode_mouseLeft].endedDown &&
|
||||
uiState->hotItem == id &&
|
||||
uiState->activeItem == id)
|
||||
{
|
||||
@ -1449,7 +1498,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
state->input, 5, V2(1000, 500), fieldString);
|
||||
|
||||
// RESET IMGUI
|
||||
if (!state->input.keys[keycode_mouseLeft])
|
||||
if (!state->input.keys[keycode_mouseLeft].endedDown)
|
||||
state->uiState.activeItem = 0;
|
||||
else if (state->uiState.activeItem == 0)
|
||||
state->uiState.activeItem = -1;
|
||||
@ -1500,6 +1549,12 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
}
|
||||
}
|
||||
|
||||
for (i32 i = 0; i < keycode_count; i++)
|
||||
{
|
||||
state->input.keys[i].oldHalfTransitionCount =
|
||||
state->input.keys[i].newHalfTransitionCount;
|
||||
}
|
||||
|
||||
sortWorldEntityList(world, numDeadEntities);
|
||||
|
||||
#ifdef DENGINE_DEBUG
|
||||
|
@ -5,10 +5,13 @@
|
||||
#include "Dengine/OpenGL.h"
|
||||
#include "Dengine/WorldTraveller.h"
|
||||
|
||||
INTERNAL inline void processKey(b32 *currState, int key, int action)
|
||||
INTERNAL inline void processKey(KeyState *state, int action)
|
||||
{
|
||||
if (action == GLFW_PRESS) *currState = TRUE;
|
||||
else if (action == GLFW_RELEASE) *currState = FALSE;
|
||||
// TODO(doyle): Handle GLFW_REPEAT, and probably remove this function
|
||||
if (action == GLFW_PRESS || action == GLFW_RELEASE)
|
||||
{
|
||||
state->newHalfTransitionCount++;
|
||||
}
|
||||
}
|
||||
|
||||
INTERNAL void keyCallback(GLFWwindow *window, int key, int scancode, int action,
|
||||
@ -24,32 +27,38 @@ INTERNAL void keyCallback(GLFWwindow *window, int key, int scancode, int action,
|
||||
switch (key)
|
||||
{
|
||||
case GLFW_KEY_UP:
|
||||
processKey(&game->input.keys[keycode_up], key, action);
|
||||
processKey(&game->input.keys[keycode_up], action);
|
||||
break;
|
||||
case GLFW_KEY_DOWN:
|
||||
processKey(&game->input.keys[keycode_down], key, action);
|
||||
processKey(&game->input.keys[keycode_down], action);
|
||||
break;
|
||||
case GLFW_KEY_LEFT:
|
||||
processKey(&game->input.keys[keycode_left], key, action);
|
||||
processKey(&game->input.keys[keycode_left], action);
|
||||
break;
|
||||
case GLFW_KEY_RIGHT:
|
||||
processKey(&game->input.keys[keycode_right], key, action);
|
||||
processKey(&game->input.keys[keycode_right], action);
|
||||
break;
|
||||
case GLFW_KEY_LEFT_SHIFT:
|
||||
processKey(&game->input.keys[keycode_leftShift], key, action);
|
||||
processKey(&game->input.keys[keycode_leftShift], action);
|
||||
break;
|
||||
case GLFW_KEY_ENTER:
|
||||
processKey(&game->input.keys[keycode_enter], key, action);
|
||||
processKey(&game->input.keys[keycode_enter], action);
|
||||
break;
|
||||
case GLFW_KEY_BACKSPACE:
|
||||
processKey(&game->input.keys[keycode_backspace], key, action);
|
||||
processKey(&game->input.keys[keycode_backspace], action);
|
||||
break;
|
||||
default:
|
||||
if (key >= ' ' && key <= '~')
|
||||
{
|
||||
processKey(&game->input.keys[key - ' '], key, action);
|
||||
// TODO(doyle): Temporary
|
||||
game->uiState.keyChar = key - ' ';
|
||||
i32 offset = 0;
|
||||
if (key >= 'A' && key <= 'Z')
|
||||
{
|
||||
if (!game->input.keys[keycode_leftShift].endedDown)
|
||||
offset = 'a' - 'A';
|
||||
}
|
||||
|
||||
i32 glfwCodeToPlatformCode = (key - ' ') + offset;
|
||||
processKey(&game->input.keys[glfwCodeToPlatformCode], action);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -73,7 +82,7 @@ INTERNAL void mouseButtonCallback(GLFWwindow *window, int button, int action,
|
||||
switch(button)
|
||||
{
|
||||
case GLFW_MOUSE_BUTTON_LEFT:
|
||||
processKey(&game->input.keys[keycode_mouseLeft], button, action);
|
||||
processKey(&game->input.keys[keycode_mouseLeft], action);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -11,7 +11,7 @@ typedef struct MemoryArena MemoryArena;
|
||||
// have a mapping function to map it back to ascii?
|
||||
enum KeyCode
|
||||
{
|
||||
keycode_space = 0,
|
||||
keycode_space,
|
||||
keycode_exclamation,
|
||||
keycode_dbl_quotes,
|
||||
keycode_hash,
|
||||
@ -24,7 +24,6 @@ enum KeyCode
|
||||
keycode_star,
|
||||
keycode_plus,
|
||||
keycode_comma,
|
||||
keycode_minus,
|
||||
keycode_hyphen,
|
||||
keycode_dot,
|
||||
keycode_forward_slash,
|
||||
@ -114,16 +113,22 @@ enum KeyCode
|
||||
keycode_leftShift,
|
||||
keycode_mouseLeft,
|
||||
keycode_enter,
|
||||
keycode_count,
|
||||
keycode_backspace,
|
||||
keycode_tab,
|
||||
keycode_count,
|
||||
keycode_null,
|
||||
};
|
||||
|
||||
typedef struct KeyState
|
||||
{
|
||||
u32 oldHalfTransitionCount;
|
||||
u32 newHalfTransitionCount;
|
||||
b32 endedDown;
|
||||
} KeyState;
|
||||
|
||||
typedef struct KeyInput
|
||||
{
|
||||
v2 mouseP;
|
||||
b32 keys[keycode_count - 1];
|
||||
KeyState keys[keycode_count];
|
||||
} KeyInput;
|
||||
|
||||
typedef struct PlatformFileRead
|
||||
|
Loading…
Reference in New Issue
Block a user