I thought it was pretty interesting how Casey tied the keyboard to mimic controller input, though I was disappointed when I was building a tetris game for my class, I decided to not do SMFL because, well that's too easy I have quite a bit of experience with high level engines... So I did the handmade hero way and followed Casey (though my assignment was 10 days late because of this type of input thing)
Casey, at least at around day 25, the input was passed through UpdateAndRender, it's very handy if you want to, every frame, check if a key is down. But I found it in my assignment when I did it that it is not good of course, if you just want to check if a key is pressed, and not trigger the same event until the key is released, then pressed again.
So a few months later, while going back to rebuilding everything from episode 1 to do a full 3D offshoot, I fixed this problem with how Löve does it's input (in a nutshell). Löve has a function you can call to see if a key is down, useful when you're in the update function and want to check every frame. It then has two separate functions KeyPressed and KeyReleased, that run every time a key is pressed or released (respectively). But instead of a function to see if a key is down, In the gameinput structure I put a 256 bool array, the position in the array being for the ascii code of the key. I then made defines so I don't have to look up every number to a corresponding key like key_f11 or pad_0, stuff like that.
Here's some code for you guys to get an idea of what I got. (Keep in mind this is choppy to cut out the stuff that's not related to the input)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | struct game_input { float dt; bool KeyDown[256]; }; #define KEY_PRESSED(name) void name(char Key, int Uni, game_memory *Memory) typedef KEY_PRESSED(key_pressed); KEY_PRESSED(KeyPressedStub) { } #define KEY_RELEASED(name) void name(char Key, int Uni, game_memory *Memory) typedef KEY_RELEASED(key_released); KEY_RELEASED(KeyReleasedStub) { } /* Removed a bunch of key defines from here */ #define MOUSE_PRESSED(name) void name(char Button, int X, int y, game_memory *Memory) typedef MOUSE_PRESSED(mouse_pressed); MOUSE_PRESSED(MousePressedStub) { } #define MOUSE_RELEASED(name) void name(char Button, int X, int y, game_memory *Memory) typedef MOUSE_RELEASED(mouse_released); MOUSE_RELEASED(MouseReleasedStub) { } local void ProcessInputMessages(game_input *Input, game_memory *GameMemory, win32_platform_code *Game, win32_state *Win32State) { MSG Message; while(PeekMessage(&Message, 0, 0, 0, PM_REMOVE)) { switch(Message.message) { case WM_QUIT: { GLOBAL_RUNNING = false; }break; case WM_SYSKEYUP: case WM_KEYUP: { // NOTE(Jesse): Keyreleased uint32 VKCode = (uint32)Message.wParam; bool32 AltKeyDown = (Message.lParam & (1 << 29)); char Key = (char)VKCode; Game->KeyReleased((char)tolower(Key), VKCode, GameMemory); Input->KeyDown[VKCode] = false; }break; case WM_SYSKEYDOWN: case WM_KEYDOWN: { bool32 WasDown = ((Message.lParam & (1 << 30)) != 0); bool32 AltKeyDown = (Message.lParam & (1 << 29)); uint32 VKCode = (uint32)Message.wParam; if(!WasDown) { char Key = (char)VKCode; Game->KeyPressed((char)tolower(Key), VKCode, GameMemory); if(VKCode == VK_ESCAPE) { GLOBAL_RUNNING = !GLOBAL_RUNNING; } Input->KeyDown[VKCode] = true; } if(VKCode == VK_F11) { if(Message.hwnd) { ToggleFullscreen(Message.hwnd); } } if(VKCode == VK_F1) { if(Win32State->InputRecordingIndex == 0) { Win32BeginRecordingInput(Win32State, 1); } else { Win32EndRecordingInput(Win32State); Win32BeginInputPlayback(Win32State, 1); } } }break; case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: case WM_XBUTTONUP: { int X = (int)(WORD)(Message.lParam); int Y = (int)(Message.lParam >> 16); char Button = 0; switch(Message.message) { case WM_LBUTTONUP: { Button = 'l'; }break; case WM_MBUTTONUP: { Button = 'm'; }break; case WM_RBUTTONUP: { Button = 'r'; }break; } Game->MouseReleased(Button, X, Y, GameMemory); }break; case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_XBUTTONDOWN: { int X = (int)(WORD)(Message.lParam); int Y = (int)(Message.lParam >> 16); char Button = 0; switch(Message.message) { case WM_LBUTTONDOWN: { Button = 'l'; }break; case WM_MBUTTONDOWN: { Button = 'm'; }break; case WM_RBUTTONDOWN: { Button = 'r'; }break; } Game->MousePressed(Button, X, Y, GameMemory); }break; default: { TranslateMessage(&Message); DispatchMessageA(&Message); }break; } } } |
It works. I succeeded in creating separate functions from which to only trigger once per key event. And also allows, every frame for the key to be checked if it's down. Though it also creates a problem...
Casey and his beautiful live looped input recording and playback only handle for his conjoined controller/keyboard input method. I however am stumped trying to find how to not only ready the one key/mouse pressed/released system for saving into file write, but also how to write it. The only thing I can think of to relieve the problem is to make 4 arrays of bools, and when a key is pressed it switches the pressed array ascii index to 1, but every frame 0 out all 4 arrays. Which sounds unfavorable.
If anyone has any ideas on how to alleviate this solution, or have a different solution entirely, I'm welcome to suggestions.