Quickstart: Turn Game Ideas into Reality
🚀 Quickstart: Turn Game Ideas into Reality
This guide shows how to quickly implement a new game idea on the T-Display-S3.
Prerequisites
- PlatformIO installed (VS Code extension or CLI)
- LilyGo T-Display-S3 connected
- Project cloned:
git clone https://github.com/lxndrJ/TDisplayS3_Adventure.git
Quickstart in 3 Steps
Step 1: Create a new game
# Create the folder
mkdir -p src/games/mygame
src/games/mygame/mygame.h – minimal template:
#ifndef MYGAME_H
#define MYGAME_H
#include "core/game.h"
class MyGameGame : public Game {
public:
const char* name() const override { return "My Game"; }
void init() override;
void update(const InputState& in, uint32_t dtMs) override;
void draw() override;
private:
// Your game state here
int score = 0;
};
#endif
src/games/mygame/mygame.cpp – implementation:
#include "mygame.h"
#include "core/display.h"
void MyGameGame::init() {
gfx.tft().fillScreen(TFT_BLACK);
score = 0;
}
void MyGameGame::update(const InputState& in, uint32_t dtMs) {
// Controls: in.pressed[0], in.pressed[1], in.longPressed[0], in.longPressed[1]
if (in.pressed[(uint8_t)Key::A]) score += 10;
if (in.longPressed[(uint8_t)Key::B]) wantsMenu = true; // back to the menu
}
void MyGameGame::draw() {
auto& tft = gfx.tft();
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE);
tft.drawString("Score: " + String(score), 10, 10, 4);
}
Step 2: Register in the game registry
Edit src/games/games.h:
#include "mygame/mygame.h" // add
inline Game* makeGame(uint8_t index) {
static EldoriaGame eldoria;
static NeonRunGame neonRun;
static MyGameGame myGame; // add
switch (index) {
case 0: return &eldoria;
case 1: return &neonRun;
case 2: return &myGame; // add
default: return nullptr;
}
}
inline constexpr uint8_t GAME_COUNT = 3; // 2 -> 3
Step 3: Build & flash
pio run -t upload # builds and flashes
Controls (T-Display-S3)
| Action | Button |
|---|---|
| Next option / right | B (GPIO14) short |
| Previous option / left | A (GPIO0) short |
| Select / confirm | B (GPIO14) long (~0.7 s) |
| Inventory / special | A (GPIO0) long (~1.2 s) |
⚠️ For flashing: hold BOOT → press RST → release → start upload
Engine Features (ready to use)
| Feature | Access |
|---|---|
| Button input | InputState with pressed[], longPressed[], down[], holdMs[] |
| Draw to display | gfx.tft() – direct TFT_eSPI access |
| Print text | tft.drawString(text, x, y, font) |
| Draw shapes | tft.fillRect(), tft.drawCircle(), etc. |
| Colors | TFT_BLACK, TFT_WHITE, TFT_RED, TFT_GREEN, TFT_BLUE, TFT_YELLOW (RGB565) |
| Back to menu | set wantsMenu = true |
| Game name | override name() |
Templates to Copy From
| Genre | Template | File |
|---|---|---|
| Text adventure | Scenes, inventory, combat dice | src/games/eldoria/eldoria.cpp |
| Action/runner | Game loop, collision, sprites | src/games/neonrun/neonrun.cpp |
| Sprite graphics | Swappable themes | src/games/neonrun/sprites.h |
Fast Test Workflow
To iterate quickly without going through the menu every time, boot directly into your game via the engine:
// src/core/engine.h (or main.cpp): start a specific game instead of the menu
engine.begin(makeGame(2)); // your game's index
pio run -t upload- Test → change code → repeat
Display Specifications
| Property | Value |
|---|---|
| Resolution | 320 × 170 pixels |
| Color depth | 16-bit RGB565 |
| Rotation | 1 (landscape) |
| Display driver | ST7789 (8-bit parallel) |
Useful TFT_eSPI Commands
auto& tft = gfx.tft();
// Clear the screen
tft.fillScreen(TFT_BLACK);
// Text
tft.setTextColor(TFT_WHITE, TFT_BLACK); // foreground, background
tft.drawString("Hello", 10, 10, 2); // font 1-8
// Shapes
tft.fillRect(x, y, w, h, TFT_RED);
tft.drawRect(x, y, w, h, TFT_WHITE);
tft.fillCircle(cx, cy, r, TFT_GREEN);
tft.drawLine(x1, y1, x2, y2, TFT_BLUE);
// Pixel
tft.drawPixel(x, y, color);
// Rotation
tft.setRotation(0); // 0=portrait, 1=landscape, 2=portrait-180, 3=landscape-180
Further Links
Help
- Questions? → GitHub Discussions
- Report a bug → GitHub Issues
- Project repository → lxndrJ/TDisplayS3_Adventure