rgame 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +94 -0
- data/README.md +130 -233
- data/docs/api/README.md +116 -69
- data/docs/api/assets.md +11 -12
- data/docs/api/components.md +58 -34
- data/docs/api/drawing.md +77 -9
- data/docs/api/game.md +34 -13
- data/docs/api/input.md +232 -51
- data/docs/api/scene_graph.md +242 -15
- data/docs/api/systems.md +20 -0
- data/docs/api/toolbox.md +19 -15
- data/docs/api/ui.md +98 -0
- data/docs/api/values.md +32 -0
- data/ext/README.md +6 -5
- data/ext/rgame_core/app/app.c +182 -8
- data/ext/rgame_core/audio/audio.c +74 -0
- data/ext/rgame_core/example.rb +17 -6
- data/ext/rgame_core/extconf.rb +52 -24
- data/ext/rgame_core/graphics/canvas.c +45 -4
- data/ext/rgame_core/graphics/canvas.h +65 -10
- data/ext/rgame_core/graphics/clip.c +22 -13
- data/ext/rgame_core/include/rgame/core.h +113 -3
- data/ext/rgame_core/input/gamepad.c +57 -3
- data/ext/rgame_core/ruby/core_ext.c +16 -0
- data/ext/rgame_core/ruby/renderer_ext.c +23 -0
- data/ext/rgame_util/color_ext.c +12 -3
- data/lib/rgame/core/app.rb +2 -0
- data/lib/rgame/core/input.rb +35 -41
- data/lib/rgame/core/recording.rb +3 -1
- data/lib/rgame/core/renderer.rb +76 -28
- data/lib/rgame/core/tile_map_renderer.rb +84 -55
- data/lib/rgame/engine/camera.rb +55 -10
- data/lib/rgame/engine/component.rb +11 -1
- data/lib/rgame/engine/components/animated_sprite.rb +9 -3
- data/lib/rgame/engine/components/camera_follow.rb +44 -0
- data/lib/rgame/engine/components/character_body.rb +25 -4
- data/lib/rgame/engine/components/sprite.rb +11 -1
- data/lib/rgame/engine/components/tile_world.rb +31 -18
- data/lib/rgame/engine/culling.rb +47 -0
- data/lib/rgame/engine/debug_overlay.rb +20 -9
- data/lib/rgame/engine/input/action_mapper.rb +101 -21
- data/lib/rgame/engine/input/actions.rb +69 -12
- data/lib/rgame/engine/input/input_map.rb +178 -0
- data/lib/rgame/engine/layout.rb +82 -0
- data/lib/rgame/engine/node2d.rb +205 -36
- data/lib/rgame/engine/player.rb +69 -0
- data/lib/rgame/engine/player_layer.rb +70 -0
- data/lib/rgame/engine/players.rb +212 -0
- data/lib/rgame/engine/scene/scene_stack.rb +25 -3
- data/lib/rgame/engine/spatial_hash.rb +17 -4
- data/lib/rgame/engine/tile_map_layer.rb +84 -0
- data/lib/rgame/engine/ui/menu.rb +115 -0
- data/lib/rgame/engine/ui/menu_item.rb +84 -0
- data/lib/rgame/engine/view.rb +76 -0
- data/lib/rgame/engine/viewports.rb +174 -0
- data/lib/rgame/engine/world_view.rb +70 -0
- data/lib/rgame/engine.rb +13 -1
- data/lib/rgame/game.rb +81 -11
- data/lib/rgame/util/controls.rb +117 -41
- data/lib/rgame/util/z.rb +133 -0
- data/lib/rgame/util.rb +1 -0
- data/lib/rgame/version.rb +1 -1
- metadata +26 -11
- data/lib/rgame/engine/camera_view.rb +0 -28
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e7eb60de3d59e1a7c55ad4261e68be3a9cdb1d8f478e56354eb655c2452fbc4d
|
|
4
|
+
data.tar.gz: 73f0f53dec4c19b34bd32a80cfa86ac2c8068a80debbd32bc234c1dca4cbd0b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f71a457f48b0f45206b03c68e8dfc8f222c9c2a87957ac2c44edd0f2330dc3412d82874544176379143a5ccf7e24596665253e1a75983d475fd8273a500bcdbe
|
|
7
|
+
data.tar.gz: f3c7ac9d5663a9e0f0df3823be78c7cf7496176982e86933e5ad8c19293ade3c21320828127e4a31086f21b44a75096cde3b9b6a4695a51180517ba9f700313b
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
6
|
+
this project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html) —
|
|
7
|
+
which before 1.0 means the public API can still change in a minor release.
|
|
8
|
+
|
|
9
|
+
Entries describe what changed for someone *using* the engine. The reasoning
|
|
10
|
+
behind a change belongs in the documentation it lands with; this file is the
|
|
11
|
+
index, not the argument.
|
|
12
|
+
|
|
13
|
+
## [Unreleased]
|
|
14
|
+
|
|
15
|
+
## [0.2.0] - 2026-08-26
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **Split-screen.** A game has seats (`RGame::Game.new(players: 2)`), and a
|
|
20
|
+
`RGame::Engine::Player` owns a device, a binding table, a camera and a region
|
|
21
|
+
of the screen. The shared world is updated once and drawn once per viewport by
|
|
22
|
+
a `WorldView`. Which player a node answers to is inherited down the tree like
|
|
23
|
+
its transform, so `ship.input_owner = players[1]` moves a whole subtree.
|
|
24
|
+
New: `Player`, `Players`, `Viewports`, `View`, `WorldView`, `Layout`.
|
|
25
|
+
- **`RGame::Engine::InputMap`** — one binding table per player, written in terms
|
|
26
|
+
of physical ids from `RGame::Util::Controls`, so a game names keys and pad
|
|
27
|
+
buttons in one place and reads named actions everywhere else.
|
|
28
|
+
- **Draw bands** (`RGame::Util::Z`): `:world`, `:hud`, `:overlay` and `:debug`.
|
|
29
|
+
A band beats every `z` in the tree, so nothing in the world can draw over the
|
|
30
|
+
HUD. Inherited down the tree, and set by the nodes that exist to mark one.
|
|
31
|
+
- **Culling** (`RGame::Engine::Culling`, `view.visible?`), which stops being an
|
|
32
|
+
optimisation once the world is drawn once per player.
|
|
33
|
+
- **A bare-bones UI package**: `PlayerLayer` gives a player their own screen, and
|
|
34
|
+
`UI::Menu` / `UI::MenuItem` navigate it by focus and activation — enough for
|
|
35
|
+
keyboard and controller menus. Layout, nesting, scrolling and text entry are
|
|
36
|
+
not in it; see [docs/api/ui.md](docs/api/ui.md).
|
|
37
|
+
- **Pausing** (`Node2D#paused`), which stops `control` and `update` for a node
|
|
38
|
+
and its whole subtree while it keeps drawing — a frozen world under a cutscene
|
|
39
|
+
overlay that goes on animating.
|
|
40
|
+
- `Components::CameraFollow`, and `TileMapLayer` as a node of its own.
|
|
41
|
+
- **`tools/drive_example.rb`** — boots an example unmodified, feeds it a
|
|
42
|
+
scripted input backend and reports what the game actually asked for: draws,
|
|
43
|
+
clips, sounds, scenes, ticks against frames.
|
|
44
|
+
- **macOS and Windows support**, and CI that runs every verification tier on
|
|
45
|
+
all three platforms.
|
|
46
|
+
- This changelog, linked from the gem's RubyGems page through `changelog_uri`.
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
|
|
50
|
+
- `Node2D#draw` and `on_draw` take the viewport being drawn into:
|
|
51
|
+
`on_draw(renderer, view)`. Most nodes ignore it; laying out against the edges
|
|
52
|
+
of a player's region, and culling, need it.
|
|
53
|
+
- `z` orders a node among its **siblings** only. It is never added to anything
|
|
54
|
+
and never reaches the renderer, so a node's whole subtree draws before or
|
|
55
|
+
after a sibling's, never interleaved with it. This replaces the additive
|
|
56
|
+
`abs_z = parent.abs_z + z`, under which a node at z 2 with a child at z 5
|
|
57
|
+
resolved to 7 and overtook a sibling at 4.
|
|
58
|
+
- `F2` quits and `F1` toggles the debug overlay. `Esc` is deliberately left to
|
|
59
|
+
the game, because it is the button a player expects to back out of a menu.
|
|
60
|
+
- Reading an action no `InputMap` declares raises `KeyError` instead of reading
|
|
61
|
+
as "never pressed" forever.
|
|
62
|
+
|
|
63
|
+
### Removed
|
|
64
|
+
|
|
65
|
+
- `RGame::Engine::CameraView`. A camera belongs to a `Player` and is applied by
|
|
66
|
+
the `View` being drawn.
|
|
67
|
+
|
|
68
|
+
## [0.1.0] - 2026-08-20
|
|
69
|
+
|
|
70
|
+
First release, and the first version that runs a game end to end.
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
|
|
74
|
+
- **The C engine**, as two Ruby extensions built from one source tree: an SDL2
|
|
75
|
+
window and a fixed-timestep main loop, keyboard and gamepad input with
|
|
76
|
+
hot-plug, a z-sorted batching renderer with transforms, clipping and baked
|
|
77
|
+
recordings, text from a shipped TrueType font, and audio (samples and
|
|
78
|
+
streamed Ogg Vorbis or WAV).
|
|
79
|
+
- **`RGame::Core`** — the half that owns the window, the GPU and the sound
|
|
80
|
+
device: `App`, `Input`, `Gamepad`, `Image`, `Renderer`, `Recording`, `Font`,
|
|
81
|
+
`Audio`, plus the asset layer (`AssetManager`, `SpriteSheet`, `NineSlice`,
|
|
82
|
+
`UIAtlas`, `TileMapRenderer`).
|
|
83
|
+
- **`RGame::Util`** — the graphics-free half, so values can be required with no
|
|
84
|
+
SDL and no OpenGL in the process: `Tensor`, `Color`, `Controls`.
|
|
85
|
+
- **`RGame::Engine`** — the scene graph a game is written in: nodes,
|
|
86
|
+
components, signals, sprites, tile maps, collision and pathfinding. Pure
|
|
87
|
+
Ruby, and unable to name `RGame::Core` at all, which is what lets game logic
|
|
88
|
+
and its specs run with no display.
|
|
89
|
+
- **`RGame::Game`** — the entry point that wires the two halves together.
|
|
90
|
+
- Examples: `14_asteroids`, `15_tiled_world`, `16_hello_world`.
|
|
91
|
+
|
|
92
|
+
[Unreleased]: https://github.com/psuessenb/rgame/compare/v0.2.0...HEAD
|
|
93
|
+
[0.2.0]: https://github.com/psuessenb/rgame/compare/v0.1.0...v0.2.0
|
|
94
|
+
[0.1.0]: https://github.com/psuessenb/rgame/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# RGame
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
RGame is a small 2D game engine for Ruby, written in Ruby and C. It's build on top of SDL2, OpenGL and miniaudio. It's built with testability and performance in mind, and aims to be an engine where you can write your whole game code in Ruby, test it as usual with RSpec (or Minitest, or another test framework) and still have acceptable performance.
|
|
4
|
+
|
|
5
|
+
While still a work in progress, RGame aims to be more than a SDL/OpenGL
|
|
6
|
+
binding - it ships with high level features like a scene graph, sprites,
|
|
7
|
+
collision systems, debugging tools and an UI toolkit.
|
|
4
8
|
|
|
5
9
|
## Why does this exist and should you use it?
|
|
6
10
|
|
|
@@ -37,47 +41,10 @@ game.start
|
|
|
37
41
|
|
|
38
42
|
You can learn more about how it works in the [documentation](docs/api/README.md).
|
|
39
43
|
|
|
40
|
-
# Where does stuff live
|
|
41
|
-
|
|
42
|
-
Everything Ruby-visible lives under the `RGame` module, split in two by what it
|
|
43
|
-
depends on:
|
|
44
|
-
|
|
45
|
-
| | `RGame::Core` | `RGame::Util` |
|
|
46
|
-
|---|---|---|
|
|
47
|
-
| For | anything depending on SDL/OpenGL (or on something that does) | everything else |
|
|
48
|
-
| C source | `ext/rgame_core/` | `ext/rgame_util/` |
|
|
49
|
-
| Extension | `rgame/core_ext` | `rgame/util_ext` |
|
|
50
|
-
| Links | SDL2 + OpenGL + pthread | nothing but Ruby |
|
|
51
|
-
| Holds today | `App` — window, GL context, fixed-timestep main loop; `Input`, `Gamepad`, `Image`, `Renderer`, `Recording`, `Font`, `Audio`, `Sample`, `Song` | `Tensor`, `Controls`, `Color` |
|
|
52
|
-
|
|
53
|
-
That split is load-bearing, not cosmetic: `require "rgame"` gives you the value
|
|
54
|
-
types *and* the scene graph with **no graphics libraries loaded into the process
|
|
55
|
-
at all**, so game logic and its specs run with no display and no SDL present.
|
|
56
|
-
`RGame::Core` is an explicit opt-in:
|
|
57
|
-
|
|
58
|
-
```ruby
|
|
59
|
-
require "rgame" # RGame::Util + RGame::Engine, no graphics
|
|
60
|
-
require "rgame/core" # adds RGame::Core, pulls in SDL2 + OpenGL
|
|
61
|
-
require "rgame/game" # all of it, wired — what a game writes
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
The engine opens a window, runs a fixed-timestep loop, reads keyboard and
|
|
65
|
-
controllers, loads PNGs onto the GPU, draws shapes, sprites and text through
|
|
66
|
-
a z-sorted batching renderer, and plays Ogg Vorbis and WAV. `RGame::Game` puts
|
|
67
|
-
those together with a scene graph, and the two games under `examples/` run on
|
|
68
|
-
it. Its C sources build two ways from one copy: a standalone binary (`build/rgame`, via the root
|
|
69
|
-
`Makefile`) and the `core_ext` extension (via `extconf.rb`).
|
|
70
|
-
|
|
71
|
-
Both halves ship as one gem — `rgame.gemspec` builds both extensions — though
|
|
72
|
-
nothing is published yet, so it is installed from a checkout or a built `.gem`.
|
|
73
|
-
|
|
74
|
-
**[docs/api/](docs/api/README.md) is the reference documentation** for using the
|
|
75
|
-
engine from Ruby: the entry point, the frame loop, input, drawing, text, audio,
|
|
76
|
-
assets and the value types. Start there if you want to write a game rather than
|
|
77
|
-
work on the engine.
|
|
78
|
-
|
|
79
44
|
## Requirements
|
|
80
45
|
|
|
46
|
+
At the moment this gem ships only source-code and no precompiled binaries, which unfortunately means you need to compile a bunch of C code on your locale machine.
|
|
47
|
+
|
|
81
48
|
### C engine
|
|
82
49
|
|
|
83
50
|
- A C compiler — `gcc` or `clang`
|
|
@@ -112,19 +79,76 @@ fixture and needs `libvorbisenc` to run.
|
|
|
112
79
|
Nothing else — the engine has no runtime Ruby dependencies, and the `Gemfile`
|
|
113
80
|
holds only development gems.
|
|
114
81
|
|
|
82
|
+
All three platforms below are built and tested on every push by
|
|
83
|
+
[CI](.github/workflows/ci.yml).
|
|
84
|
+
|
|
115
85
|
### Debian / Ubuntu
|
|
116
86
|
|
|
117
87
|
```
|
|
118
88
|
sudo apt install build-essential pkg-config libsdl2-dev libgl1-mesa-dev check
|
|
119
89
|
```
|
|
120
90
|
|
|
91
|
+
`rake spec:core` opens real windows and starts its own Xvfb, which needs a few
|
|
92
|
+
more packages — the display itself, the `xwininfo` it polls to know the display
|
|
93
|
+
is up, a software rasteriser (Xvfb has no GPU, and `SDL_GL_CreateContext` fails
|
|
94
|
+
without one) and the XTEST runtime for synthetic keystrokes:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
sudo apt install xvfb x11-utils libgl1-mesa-dri libxtst6
|
|
98
|
+
```
|
|
99
|
+
|
|
121
100
|
### macOS (Homebrew)
|
|
122
101
|
|
|
123
102
|
```
|
|
124
103
|
brew install sdl2 pkg-config check
|
|
125
104
|
```
|
|
126
105
|
|
|
127
|
-
OpenGL
|
|
106
|
+
OpenGL ships with the Xcode Command Line Tools (`xcode-select --install`) — the
|
|
107
|
+
full Xcode is not needed, and neither is anything else: `rake spec:core` uses
|
|
108
|
+
the native window server, so there is no Xvfb equivalent to set up. Note
|
|
109
|
+
Homebrew's `sdl2` formula now installs **sdl2-compat**, which is SDL2's API
|
|
110
|
+
implemented on top of SDL3; the engine works through it unchanged.
|
|
111
|
+
|
|
112
|
+
### Windows
|
|
113
|
+
|
|
114
|
+
Use a **RubyInstaller-built** Ruby (mise, vfox and rbenv-style managers all
|
|
115
|
+
fetch those), which is what supplies `ridk`. The combined DevKit installer is
|
|
116
|
+
not required — a standalone MSYS2 that `ridk` can find works just as well, and
|
|
117
|
+
`C:\msys64` is one of the places it looks:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
winget install --id MSYS2.MSYS2 -e
|
|
121
|
+
ridk exec pacman -Syu --noconfirm # core update; may need a second pass
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Then the libraries. Note `make` is an **msys** package with no prefix while
|
|
125
|
+
everything else is **ucrt64**-prefixed — that split is the whole Windows story:
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
ridk exec pacman -S --needed \
|
|
129
|
+
mingw-w64-ucrt-x86_64-SDL2 \
|
|
130
|
+
mingw-w64-ucrt-x86_64-check \
|
|
131
|
+
mingw-w64-ucrt-x86_64-pkgconf \
|
|
132
|
+
mingw-w64-ucrt-x86_64-gcc \
|
|
133
|
+
make
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**MSYS2 is several environments in one install, and picking the wrong one fails
|
|
137
|
+
in a way that looks like missing packages.** UCRT64 builds native Windows
|
|
138
|
+
binaries, which is what RubyInstaller's Ruby can load; the plain `msys`
|
|
139
|
+
environment builds against a Cygwin-like runtime, which it cannot. Work from
|
|
140
|
+
the "MSYS2 UCRT64" shell or run `ridk enable ucrt64` first, and verify before
|
|
141
|
+
trusting anything:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
which gcc # must be /ucrt64/bin/gcc, NOT /usr/bin/gcc
|
|
145
|
+
pkg-config --cflags sdl2 # must print a ucrt64 include path
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
One more Windows fact worth knowing up front: **Check has no usable fork
|
|
149
|
+
there**, so the first segfault kills the whole test binary and the output stops
|
|
150
|
+
mid-suite. Use a debugger rather than reading the log — `CK_FORK=no gdb --args
|
|
151
|
+
./build/test_rgame`, and `CK_RUN_SUITE=<name>` to run one suite.
|
|
128
152
|
|
|
129
153
|
## Build & run
|
|
130
154
|
|
|
@@ -184,11 +208,18 @@ ruby ext/rgame_core/example.rb
|
|
|
184
208
|
|
|
185
209
|
## Packaging
|
|
186
210
|
|
|
187
|
-
Both extensions and the Ruby layer ship as one gem, built from `rgame.gemspec
|
|
211
|
+
Both extensions and the Ruby layer ship as one gem, built from `rgame.gemspec`
|
|
212
|
+
and published at [rubygems.org/gems/rgame](https://rubygems.org/gems/rgame):
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
gem install rgame # from RubyGems; compiles both extensions here
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Or from a checkout, which is the same gem built locally:
|
|
188
219
|
|
|
189
220
|
```
|
|
190
221
|
rake build # package into pkg/rgame-<version>.gem
|
|
191
|
-
gem install pkg/rgame
|
|
222
|
+
gem install pkg/rgame-*.gem # compiles both extensions on this machine
|
|
192
223
|
```
|
|
193
224
|
|
|
194
225
|
`gem install` runs each `extconf.rb` and installs the resulting `.so` into the
|
|
@@ -207,200 +238,66 @@ that no build artifact, spec directory or plan is, so the parts of this that
|
|
|
207
238
|
would otherwise be a checklist fail the suite instead.
|
|
208
239
|
|
|
209
240
|
The version is `RGame::VERSION` in [lib/rgame/version.rb](lib/rgame/version.rb).
|
|
210
|
-
Nothing is published to RubyGems.
|
|
211
241
|
|
|
212
242
|
## Project structure
|
|
213
243
|
|
|
214
|
-
The
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
```
|
|
221
|
-
ext/rgame_core/ RGame::Core — the SDL/GL half. The sources are
|
|
222
|
-
grouped by subsystem, one folder each, and a file
|
|
223
|
-
names the folder it includes from: graphics/canvas.c
|
|
224
|
-
says #include "graphics/clip.h".
|
|
225
|
-
include/rgame/core.h Public C API (opaque handle, no SDL/GL types
|
|
226
|
-
leaked) — what both src/main.c and the extension
|
|
227
|
-
bind against.
|
|
228
|
-
app/ The window, the context and the loop.
|
|
229
|
-
app.c Engine implementation: SDL window + OpenGL context
|
|
230
|
-
setup; owns the main loop and calls back to the
|
|
231
|
-
caller's update/draw callbacks.
|
|
232
|
-
app_gl.h Private: the GL context behind the opaque handle.
|
|
233
|
-
frame_loop.h/.c Pure fixed-timestep + FPS logic, no SDL/GL — unit-
|
|
234
|
-
tested without a window (see CLAUDE.md's layering).
|
|
235
|
-
graphics/ Everything on the drawing path.
|
|
236
|
-
transform.h/.c Pure 2D affine transform stack — rotate, scale,
|
|
237
|
-
translate, composed. No SDL.
|
|
238
|
-
clip.h/.c Pure rects and the intersecting clip stack, in
|
|
239
|
-
screen space. No SDL.
|
|
240
|
-
draw_queue.h/.c Pure z-sort and batching: collects draw commands,
|
|
241
|
-
orders them by z, merges what can share a GL call.
|
|
242
|
-
canvas.h/.c Pure composition of transform + clip + queue; the
|
|
243
|
-
seam the drawing API is written against.
|
|
244
|
-
backend.h/.c The layer-2 seam: a function-pointer table a real
|
|
245
|
-
GL backend or a recording fake plugs into, plus
|
|
246
|
-
the loop that drives it from a prepared frame.
|
|
247
|
-
texture.h/.c Pure: refcounted texture sheets, the sub-rects
|
|
248
|
-
sprites cut out of them, and pixels -> UVs.
|
|
249
|
-
primitives.h/.c Pure: rects, thick lines, circles and sprites, in
|
|
250
|
-
terms of the canvas's triangles and quads.
|
|
251
|
-
recording.h/.c Pure: a baked block of drawing, kept between
|
|
252
|
-
frames and replayed as one call per texture.
|
|
253
|
-
gl_backend.h/.c The real GL calls — the only file that issues
|
|
254
|
-
them on the drawing path.
|
|
255
|
-
image.c Decode a PNG and upload it — the thin GL shim
|
|
256
|
-
over texture.h. Views share one upload.
|
|
257
|
-
image_internal.h What the draw path needs from inside an image.
|
|
258
|
-
text/ Glyphs, from a .ttf to a texture page.
|
|
259
|
-
atlas.h/.c Pure: shelf packing for the glyph atlas — where
|
|
260
|
-
the next glyph goes on a texture page.
|
|
261
|
-
glyph_cache.h/.c Pure: codepoint -> rasterised glyph, open
|
|
262
|
-
addressed, never evicted.
|
|
263
|
-
font.h/.c Pure: a typeface at one size — glyph metrics,
|
|
264
|
-
kerning, rasterisation and UTF-8, over
|
|
265
|
-
stb_truetype. No atlas, no GL.
|
|
266
|
-
font_atlas.c Composes font + atlas + glyph cache and owns the
|
|
267
|
-
GL pages — the only text file that calls gl*.
|
|
268
|
-
font_internal.h What the draw path needs from inside a font.
|
|
269
|
-
input/ Keyboard and controllers.
|
|
270
|
-
input.h/.c Pure input snapshot + the flat button-id space
|
|
271
|
-
(keyboard and gamepad ranges). No SDL.
|
|
272
|
-
device_slots.h/.c Pure player-slot table for controllers: keeps a
|
|
273
|
-
player on the same slot across a disconnect. No SDL.
|
|
274
|
-
gamepad.h/.c Thin SDL_GameController shim: opens/closes pads on
|
|
275
|
-
hot-plug and copies their state into the snapshot.
|
|
276
|
-
audio/ Sound, which touches neither SDL nor GL.
|
|
277
|
-
audio.c The sound device, samples and songs — miniaudio
|
|
278
|
-
talks to the platform directly.
|
|
279
|
-
audio_internal.h The live-sound counter, for tests.
|
|
280
|
-
vorbis_decoder.h/.c Ogg Vorbis for miniaudio, over stb_vorbis —
|
|
281
|
-
miniaudio cannot read ogg on its own.
|
|
282
|
-
ruby/ The Ruby-facing glue, and the only C here that
|
|
283
|
-
includes ruby.h.
|
|
284
|
-
core_ext.c VALUE wrappers + callback trampolines, and the
|
|
285
|
-
extension's entry point.
|
|
286
|
-
core_ext.h One init function per Ruby-visible class here.
|
|
287
|
-
image_ext.c RGame::Core::Image — the Ruby binding.
|
|
288
|
-
audio_ext.c RGame::Core::Audio, Sample and Song — the
|
|
289
|
-
bindings; three classes in one file because they
|
|
290
|
-
share a wrapping shape.
|
|
291
|
-
renderer_ext.c RGame::Core::Renderer — the drawing primitives.
|
|
292
|
-
font_ext.c RGame::Core::Font — the Ruby binding.
|
|
293
|
-
recording_ext.c RGame::Core::Recording — baked, replayable draws.
|
|
294
|
-
vendor/ Third-party sources + their licences.
|
|
295
|
-
<name>_impl.c One per vendored library (stb_image, stb_truetype,
|
|
296
|
-
stb_vorbis, miniaudio): instantiates it and picks
|
|
297
|
-
its features. The only files built without
|
|
298
|
-
-Wall -Wextra; the suffix is what selects that.
|
|
299
|
-
extconf.rb mkmf script; pkg_config("sdl2"), -lGL. It lists
|
|
300
|
-
the subsystem folders, because mkmf's own default
|
|
301
|
-
only finds sources one level up from here.
|
|
302
|
-
example.rb Manual smoke test driven from Ruby.
|
|
303
|
-
|
|
304
|
-
ext/rgame_util/ RGame::Util — the graphics-free half, so pure-data
|
|
305
|
-
helpers can be required without pulling in SDL/GL.
|
|
306
|
-
util_ext.c Entry point; hands RGame::Util to each class init.
|
|
307
|
-
tensor.c RGame::Util::Tensor — flat-array 3D grid.
|
|
308
|
-
color.c/.h Pure RGBA packing, no Ruby — Check-tested.
|
|
309
|
-
color_ext.c RGame::Util::Color — the Ruby binding over it.
|
|
310
|
-
extconf.rb mkmf script; no pkg_config, no -lGL.
|
|
311
|
-
|
|
312
|
-
lib/rgame.rb `require "rgame"` — loads RGame::Util only.
|
|
313
|
-
lib/rgame/version.rb RGame::VERSION, and nothing else — the gemspec
|
|
314
|
-
loads this file before anything is compiled.
|
|
315
|
-
lib/rgame/util.rb Namespace loader.
|
|
316
|
-
lib/rgame/util/tensor.rb Requires the compiled rgame/util_ext.
|
|
317
|
-
lib/rgame/util/controls.rb Input id vocabulary (keys, pad buttons, axes,
|
|
318
|
-
device slots) + default bindings. Pure Ruby
|
|
319
|
-
values, so a game may name them without Core.
|
|
320
|
-
lib/rgame/util/color.rb Requires the compiled rgame/util_ext for Color.
|
|
321
|
-
lib/rgame/core.rb `require "rgame/core"` — opt-in, loads SDL/GL.
|
|
322
|
-
lib/rgame/core/app.rb Requires the compiled rgame/core_ext.
|
|
323
|
-
lib/rgame/core/input.rb Symbolic action -> button, over the C queries.
|
|
324
|
-
lib/rgame/core/gamepad.rb Which controllers are plugged in, and their names.
|
|
325
|
-
lib/rgame/core/image.rb Sprite-sheet slicing over the C-backed Image.
|
|
326
|
-
lib/rgame/core/renderer.rb Keyword args, colours and transform blocks over
|
|
327
|
-
the C-backed Renderer.
|
|
328
|
-
lib/rgame/core/recording.rb #draw over the C-backed Recording.
|
|
329
|
-
lib/rgame/core/font.rb The default font path, over the C-backed Font.
|
|
330
|
-
lib/rgame/fonts/ The default font shipped with the engine:
|
|
331
|
-
Liberation Sans 2.1.5 (SIL OFL 1.1). Data read at
|
|
332
|
-
runtime, so it lives here rather than in ext/.
|
|
333
|
-
lib/rgame/*.so Build artifacts, copied here by `make ext`.
|
|
334
|
-
|
|
335
|
-
src/main.c Standalone executable entry point — the C
|
|
336
|
-
equivalent of example.rb. Only talks to
|
|
337
|
-
rgame/core.h, never touches SDL/GL directly. Kept
|
|
338
|
-
outside ext/ so mkmf doesn't compile its main()
|
|
339
|
-
into the extension.
|
|
340
|
-
|
|
341
|
-
test/ Check unit tests for the pure C logic (`make test`).
|
|
342
|
-
test_main.c Runs every suite; one binary, build/test_rgame.
|
|
343
|
-
suites.h Each test_<x>.c exposes a Suite, declared here.
|
|
344
|
-
support/ Test-only helpers, e.g. the recording draw backend
|
|
345
|
-
that stands in for OpenGL.
|
|
346
|
-
spec/ Headless RSpec specs: RGame::Util and
|
|
347
|
-
RGame::Engine (`rake spec`). Never loads SDL.
|
|
348
|
-
packaging_spec.rb What the gem ships, asserted against the tree so a
|
|
349
|
-
new source or data file cannot be left out of it.
|
|
350
|
-
spec_core/ RSpec specs for RGame::Core (`rake spec:core`).
|
|
351
|
-
Opens real windows; boots its own Xvfb.
|
|
352
|
-
docs/ The feature spec the engine is being built out to.
|
|
353
|
-
api/ Reference documentation for using it from Ruby.
|
|
354
|
-
|
|
355
|
-
rgame.gemspec Packages both halves as one gem: both extconf.rb
|
|
356
|
-
files, and a globbed file list so a new source or
|
|
357
|
-
asset ships without being listed anywhere.
|
|
358
|
-
```
|
|
244
|
+
The C lives under `ext/rgame_core/` rather than a top-level `src/`, because
|
|
245
|
+
`gem install` runs each `extconf.rb` and an extension can only build sources
|
|
246
|
+
inside its own directory — so one copy of the code serves both the standalone
|
|
247
|
+
binary and the gem. The Ruby half is split the same way it is namespaced:
|
|
248
|
+
`lib/rgame/util/`, `lib/rgame/core/` and `lib/rgame/engine/`.
|
|
359
249
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
of its own — `spec:core` boots Xvfb itself. `rake` runs all three.
|
|
250
|
+
A file-by-file map of the whole repository is in
|
|
251
|
+
[docs/project_structure.md](docs/project_structure.md).
|
|
363
252
|
|
|
364
253
|
## Roadmap
|
|
365
254
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
255
|
+
All three layers exist and the engine is usable end to end: the games under
|
|
256
|
+
[examples/](examples/) are written against exactly what is documented.
|
|
257
|
+
|
|
258
|
+
**Done**
|
|
259
|
+
|
|
260
|
+
1. **C engine** — an SDL2 window and a fixed-timestep loop, keyboard and
|
|
261
|
+
gamepad input with hot-plug, a z-sorted batching renderer with transforms,
|
|
262
|
+
clipping and baked recordings, text from a shipped TrueType font, and audio.
|
|
263
|
+
Linux, macOS and Windows are all supported and all gated by CI.
|
|
264
|
+
2. **Ruby C extensions** — both halves. `RGame::Core` binds
|
|
265
|
+
`include/rgame/core.h` (the app, the renderer, images, fonts, recordings,
|
|
266
|
+
sound); `RGame::Util` is the graphics-free one, so values can be required
|
|
267
|
+
without pulling SDL and OpenGL into the process.
|
|
268
|
+
3. **Pure-Ruby half** — `RGame::Engine`, the layer a game is actually written
|
|
269
|
+
in: the scene graph, components, signals, tile maps, collision,
|
|
270
|
+
pathfinding, and split-screen players with a camera and a binding table
|
|
271
|
+
each. `RGame::Game` wires it to `RGame::Core` and is the only class allowed
|
|
272
|
+
to name both.
|
|
273
|
+
4. **Gem** — `rgame.gemspec` packages both halves, compiling each extension
|
|
274
|
+
into `lib/rgame/` on install the way `make ext` does in a checkout.
|
|
275
|
+
[Published to RubyGems](https://rubygems.org/gems/rgame), so
|
|
276
|
+
`gem install rgame` works.
|
|
277
|
+
|
|
278
|
+
**Next**
|
|
279
|
+
|
|
280
|
+
- **A UI package worth the name.** What exists covers a region per player,
|
|
281
|
+
focus and activation — enough for keyboard-and-controller menus. Layout,
|
|
282
|
+
nesting, scrolling lists and text entry are all still open; see
|
|
283
|
+
["What this is not"](docs/api/ui.md#what-this-is-not).
|
|
284
|
+
- **Precompiled binary gems**, so installing needs no compiler. The compiling
|
|
285
|
+
is the easy part — CI already does it on three platforms — and the real
|
|
286
|
+
blocker is that the binary still needs SDL2 at runtime. The options are
|
|
287
|
+
written up in
|
|
288
|
+
[docs/plans/precompiled-binary-gems.md](docs/plans/precompiled-binary-gems.md).
|
|
289
|
+
- **Hot paths into C, where profiling says so** — the nine-slice tiling loops
|
|
290
|
+
and the animated-tile draw loop are the candidates. Deliberately last: each
|
|
291
|
+
is a straightforward move once the geometry is separable, and doing it early
|
|
292
|
+
would trade readability for a speedup nobody has measured.
|
|
293
|
+
|
|
294
|
+
Also known and deliberately deferred: the drawing path uses legacy
|
|
295
|
+
compatibility-profile OpenGL (`glBegin`/`glEnd`), which needs no loader library.
|
|
296
|
+
Moving to core-profile GL is a decision to take on purpose, not a drive-by
|
|
297
|
+
change.
|
|
298
|
+
|
|
299
|
+
## AI clause
|
|
300
|
+
|
|
301
|
+
This project is not vibe-coded, but AI tools were used heavily while
|
|
302
|
+
writing code. If you dislike AI generated code, this project is not for
|
|
303
|
+
you.
|