This guide outlines the steps to integrate a new game into the existing game selection and display framework on the ESP32.
The ESP32 game system is organized as follows:
main.c
Initializes core tasks (buttons, LEDs, display) and orchestrates the game selection menu and game execution loop.
game_menu.h / game_menu.c
Manages the game selection menu displayed on the OLED.
display_task.h / display_task.c
Handles the OLED display: drawing the menu, game graphics, game over screens, and transitions.
game_framework.h
Defines the game_t
interface (a struct of function pointers) that all games must implement, enabling standardized interaction.
game_drawing.h / game_drawing.c
Provides common drawing utilities (e.g., draw_filled_rect
, draw_rectangle_outline
) for use across games.
button_task.h / button_task.c
Handles button inputs and pushes them into a shared input queue.
You will mainly work with:
main.c
game_framework.h
(typically not required unless extending the interface)Create two new files in tasks/game/
:
my_new_game.h
my_new_game.c
my_new_game.h
Declare your game's external game_t
instance:
#ifndef MY_NEW_GAME_H #define MY_NEW_GAME_H #include "game_framework.h" // Required for game_t extern const game_t MyNewGame; #endif // MY_NEW_GAME_H
my_new_game.c
Implement your game by adhering to the game_t
interface.
#include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "esp_random.h" #include "ssd1306.h" #include "game_framework.h" #include "game_drawing.h"
Declare your game state:
static bool s_game_is_active = false; static bool s_game_over = false; static int s_score = 0; static QueueHandle_t s_game_input_queue_global = NULL;
game_t
Functionsinit_game
static void my_new_game_init_game_impl() { // Reset state s_game_is_active = true; s_game_over = false; s_score = 0; if (s_game_input_queue_global) { xQueueReset(s_game_input_queue_global); } }
handle_input
static void my_new_game_handle_input_impl() { int input_value; if (xQueueReceive(s_game_input_queue_global, &input_value, 0)) { // Handle input_value (e.g., move player) } }
update_game_state
static bool my_new_game_update_game_state_impl() { // Game logic here if (/* game over condition */) { s_game_over = true; return true; } return false; }
draw_game
static void my_new_game_draw_game_impl(SSD1306_t *dev) { draw_filled_rect(dev, 0, GAME_PLAY_AREA_START_Y, OLED_WIDTH - 1, OLED_HEIGHT - 1, false); draw_rectangle_outline(dev, 0, GAME_PLAY_AREA_START_Y - 1, OLED_WIDTH - 1, OLED_HEIGHT - 1, true); // Draw player, enemies, etc. using game_drawing.h functions }
⚠️ Note: Do not call
ssd1306_show_buffer()
or draw the score—these are handled bydisplay_task.c
.
get_score
static int my_new_game_get_score_impl() { return s_score; }
is_active
static bool my_new_game_is_active_impl() { return s_game_is_active && !s_game_over; }
set_active
static void my_new_game_set_active_impl(bool active) { s_game_is_active = active; }
get_input_queue
/ set_input_queue
static QueueHandle_t my_new_game_get_input_queue_impl(void) { return s_game_input_queue_global; } static void my_new_game_set_input_queue_impl(QueueHandle_t queue) { s_game_input_queue_global = queue; }
game_t
Instanceconst game_t MyNewGame = { .init_game = my_new_game_init_game_impl, .handle_input = my_new_game_handle_input_impl, .update_game_state = my_new_game_update_game_state_impl, .draw_game = my_new_game_draw_game_impl, .get_score = my_new_game_get_score_impl, .is_active = my_new_game_is_active_impl, .set_active = my_new_game_set_active_impl, .get_input_queue = my_new_game_get_input_queue_impl, .set_input_queue = my_new_game_set_input_queue_impl, };
main.c
Add at the top of main.c
:
#include "tasks/game/my_new_game.h"
Find the game_menu_items[]
array and append your game:
const menu_item_t game_menu_items[] = { {.name = "JUMP BIRD", .game_instance = &JumpBirdGame}, {.name = "ARKANOID", .game_instance = &ArkanoidGame}, {.name = "MY NEW GAME", .game_instance = &MyNewGame}, // Your game };
idf.py clean idf.py build
idf.py flash
idf.py monitor
You should now see "MY NEW GAME" on the OLED menu. Selecting it will run your new game.
By following this guide, you will:
game_t
interface