├── My_First_Cpp_Game ├── platform_common.cpp ├── utils.cpp ├── My_First_Cpp_Game.vcxproj.filters ├── win32_platform.cpp ├── game.cpp ├── My_First_Cpp_Game.vcxproj └── renderer.cpp ├── README.md ├── LICENSE └── My_First_Cpp_Game.sln /My_First_Cpp_Game/platform_common.cpp: -------------------------------------------------------------------------------- 1 | struct Button_State { 2 | bool is_down; 3 | bool changed; 4 | }; 5 | 6 | enum { 7 | BUTTON_UP, 8 | BUTTON_DOWN, 9 | BUTTON_W, 10 | BUTTON_S, 11 | BUTTON_LEFT, 12 | BUTTON_RIGHT, 13 | BUTTON_ENTER, 14 | 15 | BUTTON_COUNT, // Should be the last item 16 | }; 17 | 18 | struct Input { 19 | Button_State buttons[BUTTON_COUNT]; 20 | }; -------------------------------------------------------------------------------- /My_First_Cpp_Game/utils.cpp: -------------------------------------------------------------------------------- 1 | typedef char s8; 2 | typedef unsigned char u8; 3 | typedef short s16; 4 | typedef unsigned short u16; 5 | typedef int s32; 6 | typedef unsigned int u32; 7 | typedef long long s64; 8 | typedef unsigned long long u64; 9 | 10 | #define global_variable static 11 | #define internal static 12 | 13 | inline int 14 | clamp(int min, int val, int max) { 15 | if (val < min) return min; 16 | if (val > max) return max; 17 | return val; 18 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Tutorial] How to program a game in C++ (Pong) 2 | Ever wondered how are games programmed? In this series of tutorials, I will teach you how to make a game in C++. Hopefully, you will learn the fundamentals of game programming and be able to make your own games by the end! 3 | 4 | # Step-by-Step, Beginner-Friendly Tutorials 5 | This game was created accompanied by several tutorials that will guide you through the creation of a game an the engine! 6 | 7 | Check out the tutorial series: 8 | https://www.youtube.com/playlist?list=PL7Ej6SUky135IAAR3PFCFyiVwanauRqj3 9 | 10 | This is the first tutorial: 11 | https://www.youtube.com/watch?v=luuyjjOxnUI 12 | 13 | The final result on Itch.io: 14 | https://danzaidan.itch.io/pong-learn-programming 15 | 16 | 17 | I hope you like it! 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | LICENSE: 2 | 3 | This license is supposed to be as permissive as possible. So attribution is not required, but appreciated. A donation is also appreciated :) . 4 | 5 | This software is dual-licensed to the PUBLIC DOMAIN and under the following license: you are granted a perpetual, irrevocable license to copy, modify, publish, and distribute the software and its source code as you see fit. 6 | 7 | The software is provided "as is", without warranty of any kind, express of implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfrigement. In no event shall the author be liable for any claim, damages of other liability, whether in an action of contract, tort of otherwise, arising from, out of or in connection with the software or the use or other dealing in the software. 8 | -------------------------------------------------------------------------------- /My_First_Cpp_Game/My_First_Cpp_Game.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | -------------------------------------------------------------------------------- /My_First_Cpp_Game.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29123.88 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "My_First_Cpp_Game", "My_First_Cpp_Game\My_First_Cpp_Game.vcxproj", "{F53E6F7F-074B-4374-9FAB-E3D50AD64315}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {F53E6F7F-074B-4374-9FAB-E3D50AD64315}.Debug|x64.ActiveCfg = Debug|x64 17 | {F53E6F7F-074B-4374-9FAB-E3D50AD64315}.Debug|x64.Build.0 = Debug|x64 18 | {F53E6F7F-074B-4374-9FAB-E3D50AD64315}.Debug|x86.ActiveCfg = Debug|Win32 19 | {F53E6F7F-074B-4374-9FAB-E3D50AD64315}.Debug|x86.Build.0 = Debug|Win32 20 | {F53E6F7F-074B-4374-9FAB-E3D50AD64315}.Release|x64.ActiveCfg = Release|x64 21 | {F53E6F7F-074B-4374-9FAB-E3D50AD64315}.Release|x64.Build.0 = Release|x64 22 | {F53E6F7F-074B-4374-9FAB-E3D50AD64315}.Release|x86.ActiveCfg = Release|Win32 23 | {F53E6F7F-074B-4374-9FAB-E3D50AD64315}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {740DDA96-7DCA-4D76-B498-189514B26EEF} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /My_First_Cpp_Game/win32_platform.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.cpp" 2 | #include 3 | 4 | global_variable bool running = true; 5 | 6 | struct Render_State { 7 | int height, width; 8 | void* memory; 9 | 10 | BITMAPINFO bitmap_info; 11 | }; 12 | 13 | global_variable Render_State render_state; 14 | 15 | #include "platform_common.cpp" 16 | #include "renderer.cpp" 17 | #include "game.cpp" 18 | 19 | LRESULT CALLBACK window_callback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 20 | LRESULT result = 0; 21 | 22 | switch (uMsg) { 23 | case WM_CLOSE: 24 | case WM_DESTROY: { 25 | running = false; 26 | } break; 27 | 28 | case WM_SIZE: { 29 | RECT rect; 30 | GetClientRect(hwnd, &rect); 31 | render_state.width = rect.right - rect.left; 32 | render_state.height = rect.bottom - rect.top; 33 | 34 | int size = render_state.width * render_state.height * sizeof(unsigned int); 35 | 36 | if (render_state.memory) VirtualFree(render_state.memory, 0, MEM_RELEASE); 37 | render_state.memory = VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 38 | 39 | render_state.bitmap_info.bmiHeader.biSize = sizeof(render_state.bitmap_info.bmiHeader); 40 | render_state.bitmap_info.bmiHeader.biWidth = render_state.width; 41 | render_state.bitmap_info.bmiHeader.biHeight = render_state.height; 42 | render_state.bitmap_info.bmiHeader.biPlanes = 1; 43 | render_state.bitmap_info.bmiHeader.biBitCount = 32; 44 | render_state.bitmap_info.bmiHeader.biCompression = BI_RGB; 45 | 46 | } break; 47 | 48 | default: { 49 | result = DefWindowProc(hwnd, uMsg, wParam, lParam); 50 | } 51 | } 52 | return result; 53 | } 54 | 55 | int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { 56 | 57 | ShowCursor(FALSE); 58 | 59 | // Create Window Class 60 | WNDCLASS window_class = {}; 61 | window_class.style = CS_HREDRAW | CS_VREDRAW; 62 | window_class.lpszClassName = "Game Window Class"; 63 | window_class.lpfnWndProc = window_callback; 64 | 65 | // Register Class 66 | RegisterClass(&window_class); 67 | 68 | // Create Window 69 | HWND window = CreateWindow(window_class.lpszClassName, "Pong - Tutorial", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 1280, 720, 0, 0, hInstance, 0); 70 | { 71 | //Fullscreen 72 | SetWindowLong(window, GWL_STYLE, GetWindowLong(window, GWL_STYLE) & ~WS_OVERLAPPEDWINDOW); 73 | MONITORINFO mi = { sizeof(mi) }; 74 | GetMonitorInfo(MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY), &mi); 75 | SetWindowPos(window, HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top, mi.rcMonitor.right - mi.rcMonitor.left, mi.rcMonitor.bottom - mi.rcMonitor.top, SWP_NOOWNERZORDER | SWP_FRAMECHANGED); 76 | } 77 | 78 | HDC hdc = GetDC(window); 79 | 80 | Input input = {}; 81 | 82 | float delta_time = 0.016666f; 83 | LARGE_INTEGER frame_begin_time; 84 | QueryPerformanceCounter(&frame_begin_time); 85 | 86 | float performance_frequency; 87 | { 88 | LARGE_INTEGER perf; 89 | QueryPerformanceFrequency(&perf); 90 | performance_frequency = (float)perf.QuadPart; 91 | } 92 | 93 | while (running) { 94 | // Input 95 | MSG message; 96 | 97 | for (int i = 0; i < BUTTON_COUNT; i++) { 98 | input.buttons[i].changed = false; 99 | } 100 | 101 | while (PeekMessage(&message, window, 0, 0, PM_REMOVE)) { 102 | 103 | switch (message.message) { 104 | case WM_KEYUP: 105 | case WM_KEYDOWN: { 106 | u32 vk_code = (u32)message.wParam; 107 | bool is_down = ((message.lParam & (1 << 31)) == 0); 108 | 109 | #define process_button(b, vk)\ 110 | case vk: {\ 111 | input.buttons[b].changed = is_down != input.buttons[b].is_down;\ 112 | input.buttons[b].is_down = is_down;\ 113 | } break; 114 | 115 | switch (vk_code) { 116 | process_button(BUTTON_UP, VK_UP); 117 | process_button(BUTTON_DOWN, VK_DOWN); 118 | process_button(BUTTON_W, 'W'); 119 | process_button(BUTTON_S, 'S'); 120 | process_button(BUTTON_LEFT, VK_LEFT); 121 | process_button(BUTTON_RIGHT, VK_RIGHT); 122 | process_button(BUTTON_ENTER, VK_RETURN); 123 | } 124 | } break; 125 | 126 | default: { 127 | TranslateMessage(&message); 128 | DispatchMessage(&message); 129 | } 130 | } 131 | 132 | } 133 | 134 | // Simulate 135 | simulate_game(&input, delta_time); 136 | 137 | // Render 138 | StretchDIBits(hdc, 0, 0, render_state.width, render_state.height, 0, 0, render_state.width, render_state.height, render_state.memory, &render_state.bitmap_info, DIB_RGB_COLORS, SRCCOPY); 139 | 140 | LARGE_INTEGER frame_end_time; 141 | QueryPerformanceCounter(&frame_end_time); 142 | delta_time = (float)(frame_end_time.QuadPart - frame_begin_time.QuadPart) / performance_frequency; 143 | frame_begin_time = frame_end_time; 144 | } 145 | 146 | } -------------------------------------------------------------------------------- /My_First_Cpp_Game/game.cpp: -------------------------------------------------------------------------------- 1 | #define is_down(b) input->buttons[b].is_down 2 | #define pressed(b) (input->buttons[b].is_down && input->buttons[b].changed) 3 | #define released(b) (!input->buttons[b].is_down && input->buttons[b].changed) 4 | 5 | float player_1_p, player_1_dp, player_2_p, player_2_dp; 6 | float arena_half_size_x = 85, arena_half_size_y = 45; 7 | float player_half_size_x = 2.5, player_half_size_y = 12; 8 | float ball_p_x, ball_p_y, ball_dp_x = 130, ball_dp_y, ball_half_size = 1; 9 | 10 | int player_1_score, player_2_score; 11 | 12 | internal void 13 | simulate_player(float *p, float *dp, float ddp, float dt) { 14 | ddp -= *dp * 10.f; 15 | 16 | *p = *p + *dp * dt + ddp * dt * dt * .5f; 17 | *dp = *dp + ddp * dt; 18 | 19 | if (*p + player_half_size_y > arena_half_size_y) { 20 | *p = arena_half_size_y - player_half_size_y; 21 | *dp = 0; 22 | } 23 | else if (*p - player_half_size_y < -arena_half_size_y) { 24 | *p = -arena_half_size_y + player_half_size_y; 25 | *dp = 0; 26 | } 27 | } 28 | 29 | internal bool 30 | aabb_vs_aabb(float p1x, float p1y, float hs1x, float hs1y, 31 | float p2x, float p2y, float hs2x, float hs2y) { 32 | return (p1x + hs1x > p2x - hs2x && 33 | p1x - hs1x < p2x + hs2x && 34 | p1y + hs1y > p2y - hs2y && 35 | p1y + hs1y < p2y + hs2y); 36 | } 37 | 38 | enum Gamemode { 39 | GM_MENU, 40 | GM_GAMEPLAY, 41 | }; 42 | 43 | Gamemode current_gamemode; 44 | int hot_button; 45 | bool enemy_is_ai; 46 | 47 | internal void 48 | simulate_game(Input* input, float dt) { 49 | draw_rect(0, 0, arena_half_size_x, arena_half_size_y, 0xffaa33); 50 | draw_arena_borders(arena_half_size_x, arena_half_size_y, 0xff5500); 51 | 52 | 53 | if (current_gamemode == GM_GAMEPLAY) { 54 | float player_1_ddp = 0.f; 55 | if (!enemy_is_ai) { 56 | if (is_down(BUTTON_UP)) player_1_ddp += 2000; 57 | if (is_down(BUTTON_DOWN)) player_1_ddp -= 2000; 58 | } else { 59 | //if (ball_p_y > player_1_p+2.f) player_1_ddp += 1300; 60 | //if (ball_p_y < player_1_p-2.f) player_1_ddp -= 1300; 61 | player_1_ddp = (ball_p_y - player_1_p) * 100; 62 | if (player_1_ddp > 1300) player_1_ddp = 1300; 63 | if (player_1_ddp < -1300) player_1_ddp = -1300; 64 | } 65 | 66 | float player_2_ddp = 0.f; 67 | if (is_down(BUTTON_W)) player_2_ddp += 2000; 68 | if (is_down(BUTTON_S)) player_2_ddp -= 2000; 69 | 70 | simulate_player(&player_1_p, &player_1_dp, player_1_ddp, dt); 71 | simulate_player(&player_2_p, &player_2_dp, player_2_ddp, dt); 72 | 73 | 74 | // Simulate Ball 75 | { 76 | ball_p_x += ball_dp_x * dt; 77 | ball_p_y += ball_dp_y * dt; 78 | 79 | if (aabb_vs_aabb(ball_p_x, ball_p_y, ball_half_size, ball_half_size, 80, player_1_p, player_half_size_x, player_half_size_y)) { 80 | ball_p_x = 80 - player_half_size_x - ball_half_size; 81 | ball_dp_x *= -1; 82 | ball_dp_y = (ball_p_y - player_1_p) * 2 + player_1_dp * .75f; 83 | } else if (aabb_vs_aabb(ball_p_x, ball_p_y, ball_half_size, ball_half_size, -80, player_2_p, player_half_size_x, player_half_size_y)) { 84 | ball_p_x = -80 + player_half_size_x + ball_half_size; 85 | ball_dp_x *= -1; 86 | ball_dp_y = (ball_p_y - player_2_p) * 2 + player_2_dp * .75f; 87 | } 88 | 89 | if (ball_p_y + ball_half_size > arena_half_size_y) { 90 | ball_p_y = arena_half_size_y - ball_half_size; 91 | ball_dp_y *= -1; 92 | } else if (ball_p_y - ball_half_size < -arena_half_size_y) { 93 | ball_p_y = -arena_half_size_y + ball_half_size; 94 | ball_dp_y *= -1; 95 | } 96 | 97 | if (ball_p_x + ball_half_size > arena_half_size_x) { 98 | ball_dp_x *= -1; 99 | ball_dp_y = 0; 100 | ball_p_x = 0; 101 | ball_p_y = 0; 102 | player_1_score++; 103 | } else if (ball_p_x - ball_half_size < -arena_half_size_x) { 104 | ball_dp_x *= -1; 105 | ball_dp_y = 0; 106 | ball_p_x = 0; 107 | ball_p_y = 0; 108 | player_2_score++; 109 | } 110 | } 111 | 112 | draw_number(player_1_score, -10, 40, 1.f, 0xbbffbb); 113 | draw_number(player_2_score, 10, 40, 1.f, 0xbbffbb); 114 | 115 | // Rendering 116 | draw_rect(ball_p_x, ball_p_y, ball_half_size, ball_half_size, 0xffffff); 117 | 118 | draw_rect(80, player_1_p, player_half_size_x, player_half_size_y, 0xff0000); 119 | draw_rect(-80, player_2_p, player_half_size_x, player_half_size_y, 0xff0000); 120 | 121 | } else { 122 | 123 | if (pressed(BUTTON_LEFT) || pressed(BUTTON_RIGHT)) { 124 | hot_button = !hot_button; 125 | } 126 | 127 | 128 | 129 | if (pressed(BUTTON_ENTER)) { 130 | current_gamemode = GM_GAMEPLAY; 131 | enemy_is_ai = hot_button ? 0 : 1; 132 | } 133 | 134 | if (hot_button == 0) { 135 | draw_text("SINGLE PLAYER", -80, -10, 1, 0xff0000); 136 | draw_text("MULTIPLAYER", 20, -10, 1, 0xaaaaaa); 137 | } else { 138 | draw_text("SINGLE PLAYER", -80, -10, 1, 0xaaaaaa); 139 | draw_text("MULTIPLAYER", 20, -10, 1, 0xff0000); 140 | } 141 | 142 | draw_text("PONG TUTORIAL", -73, 40, 2, 0xffffff); 143 | draw_text("WATCH THE STEP BY STEP TUTORIAL ON", -73, 22, .75, 0xffffff); 144 | draw_text("YOUTUBE.COM/DANZAIDAN", -73, 15, 1.22, 0xffffff); 145 | 146 | } 147 | } -------------------------------------------------------------------------------- /My_First_Cpp_Game/My_First_Cpp_Game.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {F53E6F7F-074B-4374-9FAB-E3D50AD64315} 24 | MyFirstCppGame 25 | 10.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v142 32 | MultiByte 33 | 34 | 35 | Application 36 | false 37 | v142 38 | true 39 | MultiByte 40 | 41 | 42 | Application 43 | true 44 | v142 45 | MultiByte 46 | 47 | 48 | Application 49 | false 50 | v142 51 | true 52 | MultiByte 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | Disabled 77 | true 78 | true 79 | 80 | 81 | Windows 82 | 83 | 84 | 85 | 86 | Level3 87 | Disabled 88 | true 89 | true 90 | 91 | 92 | Windows 93 | 94 | 95 | 96 | 97 | Level3 98 | MaxSpeed 99 | true 100 | true 101 | true 102 | true 103 | 104 | 105 | Windows 106 | true 107 | true 108 | 109 | 110 | 111 | 112 | Level3 113 | MaxSpeed 114 | true 115 | true 116 | true 117 | true 118 | 119 | 120 | Windows 121 | true 122 | true 123 | 124 | 125 | 126 | 127 | true 128 | true 129 | true 130 | true 131 | 132 | 133 | true 134 | true 135 | true 136 | true 137 | 138 | 139 | true 140 | true 141 | true 142 | true 143 | 144 | 145 | true 146 | true 147 | true 148 | true 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /My_First_Cpp_Game/renderer.cpp: -------------------------------------------------------------------------------- 1 | internal void 2 | clear_screen(u32 color) { 3 | unsigned int* pixel = (u32*)render_state.memory; 4 | for (int y = 0; y < render_state.height; y++) { 5 | for (int x = 0; x < render_state.width; x++) { 6 | *pixel++ = color; 7 | } 8 | } 9 | } 10 | 11 | internal void 12 | draw_rect_in_pixels(int x0, int y0, int x1, int y1, u32 color) { 13 | 14 | x0 = clamp(0, x0, render_state.width); 15 | x1 = clamp(0, x1, render_state.width); 16 | y0 = clamp(0, y0, render_state.height); 17 | y1 = clamp(0, y1, render_state.height); 18 | 19 | for (int y = y0; y < y1; y++) { 20 | u32* pixel = (u32*)render_state.memory + x0 + y*render_state.width; 21 | for (int x = x0; x < x1; x++) { 22 | *pixel++ = color; 23 | } 24 | } 25 | } 26 | 27 | global_variable float render_scale = 0.01f; 28 | 29 | internal void 30 | draw_arena_borders(float arena_x, float arena_y, u32 color) { 31 | arena_x *= render_state.height * render_scale; 32 | arena_y *= render_state.height * render_scale; 33 | 34 | int x0 = (int)((float)render_state.width * .5f - arena_x); 35 | int x1 = (int)((float)render_state.width * .5f + arena_x); 36 | int y0 = (int)((float)render_state.height * .5f - arena_y); 37 | int y1 = (int)((float)render_state.height * .5f + arena_y); 38 | 39 | draw_rect_in_pixels(0, 0, render_state.width, y0, color); 40 | draw_rect_in_pixels(0, y1, x1, render_state.height, color); 41 | draw_rect_in_pixels(0, y0, x0, y1, color); 42 | draw_rect_in_pixels(x1, y0, render_state.width, render_state.height, color); 43 | } 44 | 45 | internal void 46 | draw_rect(float x, float y, float half_size_x, float half_size_y, u32 color) { 47 | 48 | x *= render_state.height*render_scale; 49 | y *= render_state.height * render_scale; 50 | half_size_x *= render_state.height * render_scale; 51 | half_size_y *= render_state.height * render_scale; 52 | 53 | x += render_state.width / 2.f; 54 | y += render_state.height / 2.f; 55 | 56 | // Change to pixels 57 | int x0 = x - half_size_x; 58 | int x1 = x + half_size_x; 59 | int y0 = y - half_size_y; 60 | int y1 = y + half_size_y; 61 | 62 | draw_rect_in_pixels(x0, y0, x1, y1, color); 63 | } 64 | 65 | const char* letters[][7] = { 66 | " 00", 67 | "0 0", 68 | "0 0", 69 | "0000", 70 | "0 0", 71 | "0 0", 72 | "0 0", 73 | 74 | "000", 75 | "0 0", 76 | "0 0", 77 | "000", 78 | "0 0", 79 | "0 0", 80 | "000", 81 | 82 | " 000", 83 | "0", 84 | "0", 85 | "0", 86 | "0", 87 | "0", 88 | " 000", 89 | 90 | "000", 91 | "0 0", 92 | "0 0", 93 | "0 0", 94 | "0 0", 95 | "0 0", 96 | "000", 97 | 98 | "0000", 99 | "0", 100 | "0", 101 | "000", 102 | "0", 103 | "0", 104 | "0000", 105 | 106 | "0000", 107 | "0", 108 | "0", 109 | "000", 110 | "0", 111 | "0", 112 | "0", 113 | 114 | " 000", 115 | "0", 116 | "0", 117 | "0 00", 118 | "0 0", 119 | "0 0", 120 | " 000", 121 | 122 | "0 0", 123 | "0 0", 124 | "0 0", 125 | "0000", 126 | "0 0", 127 | "0 0", 128 | "0 0", 129 | 130 | "000", 131 | " 0", 132 | " 0", 133 | " 0", 134 | " 0", 135 | " 0", 136 | "000", 137 | 138 | " 000", 139 | " 0", 140 | " 0", 141 | " 0", 142 | "0 0", 143 | "0 0", 144 | " 000", 145 | 146 | "0 0", 147 | "0 0", 148 | "0 0", 149 | "00", 150 | "0 0", 151 | "0 0", 152 | "0 0", 153 | 154 | "0", 155 | "0", 156 | "0", 157 | "0", 158 | "0", 159 | "0", 160 | "0000", 161 | 162 | "00 00", 163 | "0 0 0", 164 | "0 0 0", 165 | "0 0", 166 | "0 0", 167 | "0 0", 168 | "0 0", 169 | 170 | "00 0", 171 | "0 0 0", 172 | "0 0 0", 173 | "0 0 0", 174 | "0 0 0", 175 | "0 0 0", 176 | "0 00", 177 | 178 | "0000", 179 | "0 0", 180 | "0 0", 181 | "0 0", 182 | "0 0", 183 | "0 0", 184 | "0000", 185 | 186 | " 000", 187 | "0 0", 188 | "0 0", 189 | "000", 190 | "0", 191 | "0", 192 | "0", 193 | 194 | " 000 ", 195 | "0 0", 196 | "0 0", 197 | "0 0", 198 | "0 0 0", 199 | "0 0 ", 200 | " 00 0", 201 | 202 | "000", 203 | "0 0", 204 | "0 0", 205 | "000", 206 | "0 0", 207 | "0 0", 208 | "0 0", 209 | 210 | " 000", 211 | "0", 212 | "0 ", 213 | " 00", 214 | " 0", 215 | " 0", 216 | "000 ", 217 | 218 | "000", 219 | " 0", 220 | " 0", 221 | " 0", 222 | " 0", 223 | " 0", 224 | " 0", 225 | 226 | "0 0", 227 | "0 0", 228 | "0 0", 229 | "0 0", 230 | "0 0", 231 | "0 0", 232 | " 00", 233 | 234 | "0 0", 235 | "0 0", 236 | "0 0", 237 | "0 0", 238 | "0 0", 239 | " 0 0", 240 | " 0", 241 | 242 | "0 0 ", 243 | "0 0", 244 | "0 0", 245 | "0 0 0", 246 | "0 0 0", 247 | "0 0 0", 248 | " 0 0 ", 249 | 250 | "0 0", 251 | "0 0", 252 | " 0 0", 253 | " 0", 254 | " 0 0", 255 | "0 0", 256 | "0 0", 257 | 258 | "0 0", 259 | "0 0", 260 | " 0 0", 261 | " 0", 262 | " 0", 263 | " 0", 264 | " 0", 265 | 266 | "0000", 267 | " 0", 268 | " 0", 269 | " 0", 270 | "0", 271 | "0", 272 | "0000", 273 | 274 | "", 275 | "", 276 | "", 277 | "", 278 | "", 279 | "", 280 | "0", 281 | 282 | " 0", 283 | " 0", 284 | " 0", 285 | " 0", 286 | " 0", 287 | "0", 288 | "0", 289 | }; 290 | 291 | internal void 292 | draw_text(const char *text, float x, float y, float size, u32 color) { 293 | float half_size = size * .5f; 294 | float original_y = y; 295 | 296 | while (*text) { 297 | if (*text != 32) { 298 | const char** letter; 299 | if (*text == 47) letter = letters[27]; 300 | else if (*text == 46) letter = letters[26]; 301 | else letter = letters[*text - 'A']; 302 | float original_x = x; 303 | 304 | for (int i = 0; i < 7; i++) { 305 | const char* row = letter[i]; 306 | while (*row) { 307 | if (*row == '0') { 308 | draw_rect(x, y, half_size, half_size, color); 309 | } 310 | x += size; 311 | row++; 312 | } 313 | y -= size; 314 | x = original_x; 315 | } 316 | } 317 | text++; 318 | x += size * 6.f; 319 | y = original_y; 320 | } 321 | } 322 | 323 | internal void 324 | draw_number(int number, float x, float y, float size, u32 color) { 325 | float half_size = size * .5f; 326 | 327 | bool drew_number = false; 328 | while (number || !drew_number) { 329 | drew_number = true; 330 | 331 | int digit = number % 10; 332 | number = number / 10; 333 | 334 | switch (digit) { 335 | case 0: { 336 | draw_rect(x - size, y, half_size, 2.5f * size, color); 337 | draw_rect(x + size, y, half_size, 2.5f * size, color); 338 | draw_rect(x, y + size * 2.f, half_size, half_size, color); 339 | draw_rect(x, y - size * 2.f, half_size, half_size, color); 340 | x -= size * 4.f; 341 | } break; 342 | 343 | case 1: { 344 | draw_rect(x + size, y, half_size, 2.5f * size, color); 345 | x -= size * 2.f; 346 | } break; 347 | 348 | case 2: { 349 | draw_rect(x, y + size * 2.f, 1.5f * size, half_size, color); 350 | draw_rect(x, y, 1.5f * size, half_size, color); 351 | draw_rect(x, y - size * 2.f, 1.5f * size, half_size, color); 352 | draw_rect(x + size, y + size, half_size, half_size, color); 353 | draw_rect(x - size, y - size, half_size, half_size, color); 354 | x -= size * 4.f; 355 | } break; 356 | 357 | case 3: { 358 | draw_rect(x - half_size, y + size * 2.f, size, half_size, color); 359 | draw_rect(x - half_size, y, size, half_size, color); 360 | draw_rect(x - half_size, y - size * 2.f, size, half_size, color); 361 | draw_rect(x + size, y, half_size, 2.5f * size, color); 362 | x -= size * 4.f; 363 | } break; 364 | 365 | case 4: { 366 | draw_rect(x + size, y, half_size, 2.5f * size, color); 367 | draw_rect(x - size, y + size, half_size, 1.5f * size, color); 368 | draw_rect(x, y, half_size, half_size, color); 369 | x -= size * 4.f; 370 | } break; 371 | 372 | case 5: { 373 | draw_rect(x, y + size * 2.f, 1.5f * size, half_size, color); 374 | draw_rect(x, y, 1.5f * size, half_size, color); 375 | draw_rect(x, y - size * 2.f, 1.5f * size, half_size, color); 376 | draw_rect(x - size, y + size, half_size, half_size, color); 377 | draw_rect(x + size, y - size, half_size, half_size, color); 378 | x -= size * 4.f; 379 | } break; 380 | 381 | case 6: { 382 | draw_rect(x + half_size, y + size * 2.f, size, half_size, color); 383 | draw_rect(x + half_size, y, size, half_size, color); 384 | draw_rect(x + half_size, y - size * 2.f, size, half_size, color); 385 | draw_rect(x - size, y, half_size, 2.5f * size, color); 386 | draw_rect(x + size, y - size, half_size, half_size, color); 387 | x -= size * 4.f; 388 | } break; 389 | 390 | case 7: { 391 | draw_rect(x + size, y, half_size, 2.5f * size, color); 392 | draw_rect(x - half_size, y + size * 2.f, size, half_size, color); 393 | x -= size * 4.f; 394 | } break; 395 | 396 | case 8: { 397 | draw_rect(x - size, y, half_size, 2.5f * size, color); 398 | draw_rect(x + size, y, half_size, 2.5f * size, color); 399 | draw_rect(x, y + size * 2.f, half_size, half_size, color); 400 | draw_rect(x, y - size * 2.f, half_size, half_size, color); 401 | draw_rect(x, y, half_size, half_size, color); 402 | x -= size * 4.f; 403 | } break; 404 | 405 | case 9: { 406 | draw_rect(x - half_size, y + size * 2.f, size, half_size, color); 407 | draw_rect(x - half_size, y, size, half_size, color); 408 | draw_rect(x - half_size, y - size * 2.f, size, half_size, color); 409 | draw_rect(x + size, y, half_size, 2.5f * size, color); 410 | draw_rect(x - size, y + size, half_size, half_size, color); 411 | x -= size * 4.f; 412 | } break; 413 | } 414 | 415 | } 416 | } --------------------------------------------------------------------------------