jef-sure/dgx-ge

0.5.0

Latest
uploaded 10 hours ago
A small engine of 2D and 2.5D games on top of DGX: an indexed frame sent by its changes and scaled, pictures from a pack in flash, actors living by animation scripts, tiles, masks, a world that lives on, dialogs, a sound mixer

Readme

# DGX-GE

DGX-GE is a small engine for 2D and 2.5D games on ESP32 microcontrollers,
built on top of my display library [DGX](https://github.com/jef-sure/esp32-component-dgx).
I had wanted a game engine of my own for a long time. My thoughts about it
were scattered: a game is pictures, animations and rules rather than code,
and time in it is real time rather than "so much per frame". To bring them
together I needed a real game that would not let me skip anything, and I took
the smallest one that was enough: Prince of Persia of 1989. It builds on the
engine, with its traps, doors, guards and fights described as data, and far
less C of its own than the original had. Neither that game nor anything of
it comes with the engine; it is only what the engine was checked on.

It is young: what each version brought is in [CHANGES.md](CHANGES.md).
Lately it has learned to read whole games from the files of my sprite editor
([dgx-ge-editor](https://github.com/jef-sure/dgx-ge-editor))
(scenes, the graph of scenes, links, the game's own data), to let DGX make
its fonts for each board, and to start on three boards I keep on my desk.
Its interfaces will still change.

## What is in it

- **A frame of palette indices** that finds what changed and sends only
  that to the display, as it is or scaled up by an integer factor, in
  blocks or smoothed (Scale2x, Scale3x or their SFX variants), through DGX;
  text and dialogs over it at the display's own resolution.
- **A pack in flash**: pictures of 1, 4 and 8 bits, read where they lie;
  scripts, dialogs, tiles, fonts, sounds and the game's own tables beside
  them.
- **A scene of layers** and **actors that live by animation scripts**
  written as text: frames, moves, events, a small language of expressions,
  a matrix of transitions between scripts.
- **Tiles with rules**, **collisions by masks**, contacts found at the
  exact moment they happen.
- **A world that lives on without a viewer**: things lying about, pockets,
  shots, timers, chases, crowds, cells with lives of their own.
- **Dialogs, labels, widgets and text** laid out in fonts of DGX, a dialog
  able to pause the game; cut-scenes; saved games.
- **Fonts as requests**: the game names a font by its size in points of the
  frame, the packer writes DGX's font requests beside the pack together with
  the letters the pack prints, and DGX makes each font for the board.
- **Touch** through [DGX-DI](https://github.com/jef-sure/esp32-component-dgx-di)
  when the game uses it: a pointer, gestures and an on-screen pad become the
  engine's input.
- **A sound mixer** of 8-bit and IMA ADPCM samples, mono or stereo; the
  platform sends what it mixes, from a task of its own if it likes, with
  its lock given to the mixer, so the game never takes one. Beside the
  samples, music by notes (MML) on the synthesizer the game chooses: a
  chip's square, triangle and noise, the NES's, or FM after the OPL2, with
  sets of instruments as data; the same C builds as WebAssembly for the
  editor, sample for sample.
- **Python tools** (`tools/`) that turn pictures, scripts, dialogs and
  sounds into one pack and a C header for it, also straight from a file of
  my sprite editor ([dgx-ge-editor](https://github.com/jef-sure/dgx-ge-editor)): its sets, animations, tiles,
  scenes, graph and game.

The engine itself knows no board: the game sets up its display, touch and
sound and gives the engine a DGX screen. The boards are
[jef-sure/boards](https://github.com/jef-sure/esp32-component-boards)'s:
a board is described there once, as data, and a game asks for its parts by
kind (`board_display_init`, `board_dgx_screen`, `board_touch_start`,
`board_sound_start`), never by the name of a board. Its
`tools/board_select.py` chooses the boards of a project once and writes
their ESP-IDF settings (`sdkconfig.defaults.<board>`) and a preset of each
(`CMakePresets.json`). What a game does over the board is the engine's:
the touch as its pointer, the pad and the keyboard on the display
(`dgx_ge_input_di.h`), the sound and the pace of its loop
(`dgx_ge_freertos.h`).

One of the boards is the computer itself, the board `host` of jef-sure/boards:
the linux target of ESP-IDF, the game's own `main.c` in a window of DGX as
large as the display of the board it shows, the mouse a finger, the keys
buttons, the computer's sound, NVS in a file kept beside the program. The example `examples/hello`, a game made in my editor
(its `game_data/`), builds for the computer and for four boards. How the
computer stands in for a board, how a game runs on it with nobody at it (a
script of keys and the mouse), and what it cannot show (speed, memory), is
in
[docs/host-en.md](docs/host-en.md) ([по-русски](docs/host-ru.md)). The
engine itself is plain C and is tested on a host too.

## On a board

A frame here is not tied to a rate. Actors live by the game's clock, and the
loop draws as soon as the frame before is out, sending only what changed;
a game says how often it steps (Prince of Persia steps 12 times a second, as
the original did). So what I give is what a frame costs: the time of one
frame that sent anything, and how many pixels of the display it sent.
Measured on 2026-10-23, from the engine of this version, ESP-IDF 6.0.1.
Prince of Persia runs its demo, three rooms in 40 seconds; each of its
numbers is of the log line the firmware writes once a second (the frames
that second sent: their time on average, the slowest, their pixels).

| | CYD (ESP32, 240 MHz; 320×240 over SPI) | Guition JC8012P4A1C (ESP32-P4, 400 MHz; 1280×800 over MIPI-DSI) |
| --- | --- | --- |
| A frame of 320×200 | at 1:1 | at ×4 |
| Prince of Persia in a room: a frame, on average | 5.9–13.0 ms; half of the seconds under 7.4 | 3.0–9.0 ms; half of the seconds under 4.7 |
| …the slowest frame of a second | 21 ms | 16 ms |
| …its pixels | 580–2 800 | 14 000–57 000 |
| …the room drawn anew, entering it | 39–44 ms | 37–41 ms |
| `examples/hello`, the whole frame | 29.3 ms, 64 000 pixels | 21.3 ms, 1 024 000 pixels |
| RAM free once the game runs (PoP / hello) | 190 012 / 226 488 bytes | 29.7 / 27.6 MB, most of it PSRAM |
| RAM the firmware's data take (`.data` + `.bss`; PoP / hello) | 24 844 / 14 396 bytes | 31 244 / 20 796 bytes |
| code kept in internal RAM (PoP / hello) | 52 515 / 52 135 bytes (IRAM, apart from the data's) | 68 392 / 68 392 bytes (DIRAM, shared with the data) |
| flash, code and constants (PoP / hello) | 1.34 MB (the pack 0.97 MB) / 0.24 MB | 1.74 / 0.49 MB |

A game where little moves at once has a lot of room: a frame of a room in
play takes 3 to 13 ms of the 83 ms a step of the game has. A frame where
everything changes costs about 40 ms on both boards. The free RAM is
what the game finds once it runs, after everything static and the frame;
the RAM of the data and the code in internal RAM are what the firmware
takes before it starts, the first of them the part a game's own globals
grow. Both firmwares log their numbers once a second (`room …: N pictures
a second, … us each on average, … at worst, … pixels each`; hello's the
same), so a game of yours can be measured the same way.

## Getting started

You need ESP-IDF 5.5 or newer (jef-sure/boards does); I build the engine,
the boards and the game it was checked on with 6.0. The engine is in the
ESP Component Registry; add it and the boards to the project's `main`, and
DGX comes along by itself, DGX-DI with a board that has touch:

```sh
idf.py add-dependency "jef-sure/dgx-ge"
idf.py add-dependency "jef-sure/boards>=0.0.5"
```

With no version it takes the newest, and `dependencies.lock` beside the
project keeps the versions taken until `idf.py update-dependencies`.

Or a whole new game at once, in a folder of its own: `tools/dgx_ge_new.py`
makes one from `examples/hello` or from a game of my editor, renamed for it
(its project, pack, prefix in C, NVS), with the example's boards, building
for them and for the computer (`idf.py`) as it is; other boards are
`board_select.py`'s. Run it from a checkout of the engine or from
`managed_components/jef-sure__dgx-ge/tools/`:

```sh
python3 tools/dgx_ge_new.py ../mygame                      # hello, renamed mygame
python3 tools/dgx_ge_new.py ../worm --from snake           # the editor's snake, once it is on jef-sure/boards
```

The engine of such a game is the registry's, as git keeps it; a checkout
instead is a link to it, `components/jef-sure__dgx-ge`, which ESP-IDF takes
over the registry's and git ignores. Run from a checkout of the engine, the
script links that checkout (`--engine registry`: no link); from the
registry's download, it links none. Its README says the rest.

DGX builds only what the project's `sdkconfig` turns on: the engine turns
on its virtual screens (`CONFIG_DGX_ENABLE_VSCREEN`, by its `Kconfig`), and
jef-sure/boards the display's bus and driver and the PSRAM a board with a
large display needs for its frame buffers.

The component manager puts it into `managed_components/jef-sure__dgx-ge`
and makes `main` require it, so `main/CMakeLists.txt` needs no `REQUIRES`
for it (if it names it, the name is `jef-sure__dgx-ge`). There, after
`idf_component_register`, the build is told how many params an actor
keeps, as many as the game's pack needs, how many `now` events an
actor's own step may send, waiting in turn (the engine's default, 4,
written out), and which synthesizers of music the firmware carries (the
engine's default, FM, written out; `chip`, `nes`, `fm`):

```cmake
idf_component_register(SRCS "main.c" INCLUDE_DIRS "."
                       EMBED_FILES "assets/game.pack")
dgx_ge_params_of_pack(assets/game_pack.h)
dgx_ge_now_events(4)
dgx_ge_synths(fm)
```

A Python script of the game builds its pack with `tools/dgx_ge_pack.py`
(`Pack`: `add_set`, `add_png`, `add_anim`, `add_dialog` and the rest, then
`write`), which gives the pack and its header. The game embeds the pack
(`EMBED_FILES`), opens it with `dgx_ge_pack_open()` and builds its scene
from it. A game with fonts of its own names them in its script
(`Pack.add_font`) and hands their requests to DGX in `main/CMakeLists.txt`:

```cmake
dgx_ge_fonts(assets/game.fonts)
dgx_fonts_board(PIXELS ${BOARD_PIXELS} MM ${BOARD_MM})   # the board's display, as jef-sure/boards gives it
dgx_fonts_scale(4)
dgx_fonts_rules(../fonts.rules)
```

The tools lie in `managed_components/jef-sure__dgx-ge/tools/`. They need
Python 3 and Pillow; fonts made for a board need a host C
compiler, FreeType's headers and fontconfig as well.

## Tests

The tests are in the repository, not in the registry's package:

```sh
git clone https://github.com/jef-sure/esp32-component-dgx-ge
make -C esp32-component-dgx-ge/test/host
```

They take DGX from a checkout next to the engine's
(`../esp32-component-dgx`), or from the managed components of a project the
engine is cloned into; `DGX=<path>` takes any other.

## Documentation

In English and in Russian:

- [docs/overview-en.md](docs/overview-en.md) ([по-русски](docs/overview-ru.md)):
  how the engine is built and how a game is made on it, for someone who has
  never made one;
- [docs/animation-en.md](docs/animation-en.md) ([по-русски](docs/animation-ru.md)):
  the language of the animation scripts;
- [docs/workflow-en.md](docs/workflow-en.md) ([по-русски](docs/workflow-ru.md)):
  the way of a game from its data, whatever made them, to the board;
- [docs/host-en.md](docs/host-en.md) ([по-русски](docs/host-ru.md)):
  a game built and run on the computer, the board `host` of jef-sure/boards.

The API itself is described in the headers, `include/*.h`.

## License

MIT, see [LICENSE](LICENSE).

Links

Supports all targets

Maintainer

  • Anton Petrusevich <anton.petrusevich.mobile@gmail.com>
To add this component to your project, run:

idf.py add-dependency "jef-sure/dgx-ge^0.5.0"

download archive

Stats

  • Archive size
    Archive size ~ 595.62 KB
  • Downloaded in total
    Downloaded in total 16 times
  • Weekly Downloads Weekly Downloads (All Versions)
  • Downloaded this version
    This version: 0 times

Badge

jef-sure/dgx-ge version: 0.5.0
|