XMLGameEngine is a video game description language written in XML, plus a C++ engine that runs it. A whole game (window, sounds, objects, screens, rules) is one .xml file, checked against a schema, games/xgedef.xsd.
The file holds no code: no loops, no if, no function calls. What things do comes from a fixed vocabulary of verbs written as tags (<bounce />, <die />, <hop direction="up">), and the engine knows how to carry out each one. The aim is to describe the non-scrolling 2D games of the late 1970s and early 1980s, and the board and card games beside them, adding a word only when a real game cannot be written without it.
This is all of pong_min.xml, two paddles and a ball:
<game xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../xgedef.xsd">
<window name="Pong">
<width>1280</width> <height>720</height>
</window>
<variables>
<variable name="margin">30</variable>
<variable name="size">16</variable>
<variable name="width">20</variable>
<variable name="height">120</variable>
<variable name="step">5</variable>
</variables>
<states>
<state name="playing">
<objects>
<object name="ball">
<sprite><rectangle><width>size</width><height>size</height></rectangle></sprite>
<position><x><centered /></x><y><centered /></y></position>
<velocity>
<x>
<formula>
<multiply>
<multiplicand><sign><value>ball.side</value></sign></multiplicand>
<multiplier>ball.speed</multiplier>
<multiplier><cosine><value>ball.angle</value></cosine></multiplier>
</multiply>
</formula>
</x>
<y>
<formula>
<multiply>
<multiplicand>ball.speed</multiplicand>
<multiplier><sine><value>ball.angle</value></sine></multiplier>
</multiply>
</formula>
</y>
</velocity>
<collisions>
<collision edge="vertical"><bounce /></collision>
<collision edge="horizontal"><reset /></collision>
<collision><deflect>45</deflect></collision>
</collisions>
<variables>
<variable name="speed">6</variable>
<variable name="angle"><random><min>-30</min><max>30</max></random></variable>
<variable name="side"><random><min>-1</min><max>1</max></random></variable>
</variables>
</object>
<group name="paddles">
<sprite><rectangle><width>width</width><height>height</height></rectangle></sprite>
<collisions>
<collision edge="vertical"><stick /></collision>
</collisions>
<actions>
<action name="up"><move direction="up">step</move></action>
<action name="down"><move direction="down">step</move></action>
</actions>
<member name="paddle1"><position><x>margin</x><y><centered /></y></position></member>
<member name="paddle2"><position><x>window.right - margin - width</x><y><centered /></y></position></member>
</group>
</objects>
<inputs>
<input button="w"><trigger object="paddle1" action="up" /></input>
<input button="s"><trigger object="paddle1" action="down" /></input>
<input button="up"><trigger object="paddle2" action="up" /></input>
<input button="down"><trigger object="paddle2" action="down" /></input>
</inputs>
</state>
</states>
</game>A collision rule reads like a sentence: this ball, when it touches the top or bottom edge, bounces; when it leaves by a side, it starts again; when it touches anything else, it deflects by up to 45 degrees. The serve is worked out with <formula> tags, so no arithmetic beyond + - * / hides in a string.
- Attributes name or pick; everything else is content.
name,class,edge,buttonare attributes; a size, a colour or a position is element content. - Behaviour is a list of verbs, run in order, in a collision rule, an action, a key, a condition, a timer or a path step. The schema says which verbs each place takes.
- A value is a number: plain arithmetic as text (
window.right - margin), a<random>, or a<formula>of tags. - Names are dotted:
ball.radius,aliens.3.2,playing.room2.robots;self.andtouch.inside a rule. - Keys are bound in states, never in objects, so changing a key is one edit.
- Say a thing once: groups write what members share; a kit holds pieces taken by name; states and scenes copy others with
from=and say only what differs. - Mistakes stop the load with a message that says where, and "did you mean" for a misspelt name.
<game>
<window> size, background, frame rate, scale
<variables> numbers the whole game uses
<sounds> chip notes and music (optional)
<paths> routes that objects fly (optional)
<styles> named text and bitmap looks (optional)
<kit> pieces written once, taken later (optional)
<objects> what every state shares (optional)
<keeps> objects kept between games (optional)
<states> title, playing, paused, game over ...
<demo> a recorded game for attract mode (optional)
</game>
The documentation has a tour, every word, and an index linking each to its page.
55 games in games/descriptions/, each with a test that plays it frame by frame:
| Kind | Games |
|---|---|
| Paddle and ball | Pong, Breakout, Space Race, Tennis |
| Shooters | Space Invaders (two versions), Galaxian, Asteroids, Missile Command, Megamania, Yars' Revenge, Berserk, Demon Attack, Atlantis, Astrosmash, Air-Sea Battle, Combat, Depth Charge |
| Platforms and rooms | Donkey Kong, Mario Bros., Q*bert, Pitfall!, Frostbite, Adventure, Haunted House, Lunar Lander |
| Mazes, lanes, catching | Pac-Man, Frogger, Freeway, Kaboom!, Fishing Derby |
| Grid puzzles | Tetris, Columns, Snake, Centipede, Capsules |
| Tables and tracks | Putt Park, Cannon Hill, Pinball, Speedway, Trick Shot |
| Board games | Checkers, Reversi, Four in a Row, Tic-Tac-Toe, Dots and Boxes |
| Card games | Klondike, Crazy Eights, Blackjack, Texas Hold'em, Hearts, Spades, Gin Rummy, Pyramid |
The full list says what each game needed. They share one key convention: Space starts, pauses and restarts; player one is W A S D plus the arrows; where Space fires, P or Escape pauses.
cmake -B build
cmake --build build --parallel
output/Debug/xgecli frogger
The build makes xgelib (the engine), xgecli (command line) and xgegui (a Qt 6 app showing the game beside a tree of its data, built only when Qt is found). Everything needed to run lands in output/<Config>/, so the folder can be copied anywhere. -w, -x and -a pick the window, XML and sound libraries (xgecli pong -w raylib -a sdl2).
| Library | For |
|---|---|
| Xerces-C | parsing and full XSD validation |
| SFML 3 | window, keys and sound |
| lunasvg | drawing SVG sprites |
| Qt 6 Widgets | xgegui (found, never fetched) |
| libxslt | xgecli --generate (found, never fetched) |
| Catch2 | the tests |
Each is found through vcpkg or the system, or fetched and built. A plain build has one library of each kind; -DXGE_WITH_<NAME>=ON adds raylib, SDL2, OpenGL, TinyXML2, PugiXML or RapidXML, and -DXGE_ALL_BACKENDS=ON all of them.
- Tests:
cmake -B build -DBUILD_TESTING=ON, thenoutput/Debug/xgetest. - A program of its own:
xgecli pong --generate windows-cppwrites the game as a standalone SFML program. XSLT turns the game file into C++ data and the engine's own code plays it, so it plays exactly asxgeclidoes.
Version 1 is non-scrolling games; a camera for scrolling ones is planned for version 2. Movement is pixels per frame and timers count frames; sound is chip music made from notes. The limits are listed in known limitations.
MIT, in LICENSE; a generated program carries a copy. The fonts in games/assets/ keep their own: Tuffy is public domain, and Press Start 2P is under the SIL Open Font License (pressstart2p-ofl.txt beside it).
- docs/readme.md: the guide, a tour, and an index of every word.
- docs/reference/: how the engine works today, a page per topic.
- docs/designs/00-designs.md: why, options rejected, ideas not built.
- AGENTS.md and docs/agents/notes.md: notes for contributors and AI coding agents.