rgame 0.1.0 → 0.3.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 +228 -0
- data/README.md +142 -243
- data/docs/api/README.md +141 -90
- data/docs/api/app.md +125 -68
- data/docs/api/assets.md +203 -166
- data/docs/api/audio.md +130 -89
- data/docs/api/cli.md +259 -0
- data/docs/api/components.md +1045 -254
- data/docs/api/drawing.md +187 -119
- data/docs/api/examples.md +263 -0
- data/docs/api/game.md +165 -45
- data/docs/api/images.md +39 -39
- data/docs/api/input.md +344 -85
- data/docs/api/internals.md +241 -62
- data/docs/api/localization.md +285 -0
- data/docs/api/scene_graph.md +503 -123
- data/docs/api/signals.md +81 -76
- data/docs/api/systems.md +197 -64
- data/docs/api/text.md +58 -41
- data/docs/api/tile_maps.md +237 -0
- data/docs/api/toolbox.md +400 -147
- data/docs/api/ui.md +927 -0
- data/docs/api/values.md +234 -19
- data/examples/assets/README.md +322 -0
- data/examples/assets/blip.ogg +0 -0
- data/examples/assets/glyphs.json +5 -0
- data/examples/assets/glyphs.png +0 -0
- data/examples/assets/hero.json +12 -0
- data/examples/assets/hero.png +0 -0
- data/examples/assets/icons.json +13 -0
- data/examples/assets/icons.png +0 -0
- data/examples/assets/music.ogg +0 -0
- data/examples/assets/skills.json +10 -0
- data/examples/assets/skills.png +0 -0
- data/examples/assets/tileset.png +0 -0
- data/examples/assets/tileset.tsx +65 -0
- data/examples/assets/town.tmx +26 -0
- data/examples/assets/ui.json +11 -0
- data/examples/assets/ui.png +0 -0
- data/examples/collision/locales/en.yml +8 -0
- data/examples/collision/main.rb +316 -0
- data/examples/collision_tiles/locales/en.yml +9 -0
- data/examples/collision_tiles/main.rb +274 -0
- data/examples/fullscreen/locales/en.yml +10 -0
- data/examples/fullscreen/main.rb +216 -0
- data/examples/game_menu/locales/en.yml +8 -0
- data/examples/game_menu/main.rb +170 -0
- data/examples/input_glyphs/locales/en.yml +14 -0
- data/examples/input_glyphs/main.rb +213 -0
- data/examples/jump_topdown/locales/en.yml +9 -0
- data/examples/jump_topdown/main.rb +178 -0
- data/examples/localization/locales/de.yml +12 -0
- data/examples/localization/locales/en.yml +13 -0
- data/examples/localization/main.rb +158 -0
- data/examples/menu_navigation/locales/en.yml +23 -0
- data/examples/menu_navigation/main.rb +365 -0
- data/examples/music/locales/en.yml +7 -0
- data/examples/music/main.rb +134 -0
- data/examples/pathfinding/locales/en.yml +17 -0
- data/examples/pathfinding/main.rb +298 -0
- data/examples/pooling/locales/en.yml +7 -0
- data/examples/pooling/main.rb +259 -0
- data/examples/quick_wheel/locales/en.yml +16 -0
- data/examples/quick_wheel/main.rb +184 -0
- data/examples/radial_menu/locales/en.yml +16 -0
- data/examples/radial_menu/main.rb +184 -0
- data/examples/save_load/locales/en.yml +11 -0
- data/examples/save_load/main.rb +207 -0
- data/examples/save_load_ids/locales/en.yml +11 -0
- data/examples/save_load_ids/main.rb +322 -0
- data/examples/scroll_map/locales/en.yml +4 -0
- data/examples/scroll_map/main.rb +140 -0
- data/examples/signals/locales/en.yml +6 -0
- data/examples/signals/main.rb +278 -0
- data/examples/skill_bar/locales/en.yml +14 -0
- data/examples/skill_bar/main.rb +159 -0
- data/examples/sound/locales/en.yml +6 -0
- data/examples/sound/main.rb +122 -0
- data/examples/split_screen/locales/en.yml +9 -0
- data/examples/split_screen/main.rb +304 -0
- data/examples/sprite/locales/en.yml +8 -0
- data/examples/sprite/main.rb +180 -0
- data/examples/timer/locales/en.yml +12 -0
- data/examples/timer/main.rb +273 -0
- data/examples/velocity/locales/en.yml +6 -0
- data/examples/velocity/main.rb +196 -0
- data/examples/walk/locales/en.yml +4 -0
- data/examples/walk/main.rb +99 -0
- data/exe/rgame +9 -0
- data/ext/README.md +6 -5
- data/ext/rgame_core/app/app.c +215 -11
- data/ext/rgame_core/app/locale.c +67 -0
- data/ext/rgame_core/app/locale.h +28 -0
- data/ext/rgame_core/audio/audio.c +113 -2
- data/ext/rgame_core/example.rb +18 -56
- data/ext/rgame_core/extconf.rb +16 -113
- 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 +151 -5
- data/ext/rgame_core/input/gamepad.c +57 -3
- data/ext/rgame_core/ruby/audio_ext.c +10 -5
- data/ext/rgame_core/ruby/core_ext.c +46 -7
- data/ext/rgame_core/ruby/core_ext.h +3 -0
- data/ext/rgame_core/ruby/locale_ext.c +44 -0
- data/ext/rgame_core/ruby/recording_ext.c +1 -1
- data/ext/rgame_core/ruby/renderer_ext.c +42 -19
- data/ext/rgame_util/color_ext.c +12 -3
- data/ext/rgame_util/extconf.rb +2 -20
- data/ext/rgame_util/route_search.c +305 -0
- data/ext/rgame_util/route_search.h +86 -0
- data/ext/rgame_util/route_search_ext.c +150 -0
- data/ext/rgame_util/solid_grid.c +58 -0
- data/ext/rgame_util/solid_grid.h +49 -0
- data/ext/rgame_util/solid_grid_ext.c +161 -0
- data/ext/rgame_util/tile_sweep.c +164 -0
- data/ext/rgame_util/tile_sweep.h +62 -0
- data/ext/rgame_util/tile_sweep_ext.c +155 -0
- data/ext/rgame_util/util_ext.c +3 -0
- data/ext/rgame_util/util_ext.h +15 -0
- data/lib/rgame/boot.rb +0 -10
- data/lib/rgame/cli/new_project.rb +139 -0
- data/lib/rgame/cli/templates/Gemfile.tt +23 -0
- data/lib/rgame/cli/templates/README.md.tt +93 -0
- data/lib/rgame/cli/templates/Rakefile.tt +9 -0
- data/lib/rgame/cli/templates/assets/locales/en.yml.tt +10 -0
- data/lib/rgame/cli/templates/game.rb.tt +23 -0
- data/lib/rgame/cli/templates/gitignore.tt +12 -0
- data/lib/rgame/cli/templates/main.rb.tt +11 -0
- data/lib/rgame/cli/templates/nodes/root.rb.tt +24 -0
- data/lib/rgame/cli/templates/rspec.tt +2 -0
- data/lib/rgame/cli/templates/rubocop.yml.tt +75 -0
- data/lib/rgame/cli/templates/ruby-version.tt +1 -0
- data/lib/rgame/cli/templates/spec/locales_spec.rb.tt +18 -0
- data/lib/rgame/cli/templates/spec/nodes/root_spec.rb.tt +18 -0
- data/lib/rgame/cli/templates/spec/spec_helper.rb.tt +37 -0
- data/lib/rgame/cli.rb +66 -0
- data/lib/rgame/core/app.rb +6 -42
- data/lib/rgame/core/asset_manager.rb +13 -31
- data/lib/rgame/core/audio.rb +37 -16
- data/lib/rgame/core/font.rb +0 -3
- data/lib/rgame/core/input.rb +35 -41
- data/lib/rgame/core/locale.rb +22 -0
- data/lib/rgame/core/nine_slice.rb +0 -21
- data/lib/rgame/core/recording.rb +3 -1
- data/lib/rgame/core/renderer.rb +75 -84
- data/lib/rgame/core/sprite_sheet.rb +0 -3
- data/lib/rgame/core/tile_map_renderer.rb +77 -65
- data/lib/rgame/core/ui_atlas.rb +28 -13
- data/lib/rgame/core.rb +1 -8
- data/lib/rgame/engine/actor_blockers.rb +131 -0
- data/lib/rgame/engine/animation_set.rb +1 -0
- data/lib/rgame/engine/audio_director.rb +36 -6
- data/lib/rgame/engine/bounds_blockers.rb +74 -0
- data/lib/rgame/engine/camera.rb +55 -10
- data/lib/rgame/engine/circle_collider.rb +4 -2
- data/lib/rgame/engine/collision_box.rb +26 -1
- data/lib/rgame/engine/collision_system.rb +110 -22
- data/lib/rgame/engine/component.rb +35 -1
- data/lib/rgame/engine/components/action_trigger.rb +0 -1
- data/lib/rgame/engine/components/animated_sprite.rb +31 -23
- data/lib/rgame/engine/components/box_collider.rb +99 -0
- data/lib/rgame/engine/components/camera_follow.rb +45 -0
- data/lib/rgame/engine/components/character_body.rb +21 -41
- data/lib/rgame/engine/components/circle_collider.rb +47 -11
- data/lib/rgame/engine/components/collision_world.rb +159 -31
- data/lib/rgame/engine/components/despawn_offscreen.rb +24 -8
- data/lib/rgame/engine/components/feet_collider.rb +61 -0
- data/lib/rgame/engine/components/hop.rb +76 -0
- data/lib/rgame/engine/components/identity.rb +73 -0
- data/lib/rgame/engine/components/mover.rb +285 -0
- data/lib/rgame/engine/components/navigator.rb +145 -0
- data/lib/rgame/engine/components/path_follow.rb +123 -31
- data/lib/rgame/engine/components/player_controller.rb +5 -2
- data/lib/rgame/engine/components/pool.rb +1 -1
- data/lib/rgame/engine/components/screen_wrap.rb +33 -11
- data/lib/rgame/engine/components/sprite.rb +22 -6
- data/lib/rgame/engine/components/targeting.rb +9 -11
- data/lib/rgame/engine/components/thrust_controller.rb +1 -1
- data/lib/rgame/engine/components/tile_world.rb +70 -29
- data/lib/rgame/engine/components/timer.rb +1 -1
- data/lib/rgame/engine/components/velocity.rb +23 -7
- data/lib/rgame/engine/components/wander_controller.rb +6 -2
- data/lib/rgame/engine/components/world.rb +133 -0
- data/lib/rgame/engine/contact_set.rb +74 -0
- data/lib/rgame/engine/culling.rb +45 -0
- data/lib/rgame/engine/debug_overlay.rb +23 -18
- data/lib/rgame/engine/i18n/plural.rb +45 -0
- data/lib/rgame/engine/i18n/plural_rules.rb +82 -0
- data/lib/rgame/engine/i18n/template.rb +59 -0
- data/lib/rgame/engine/i18n.rb +276 -51
- data/lib/rgame/engine/input/action_mapper.rb +76 -22
- data/lib/rgame/engine/input/actions.rb +63 -12
- data/lib/rgame/engine/input/input_map.rb +196 -0
- data/lib/rgame/engine/layout.rb +82 -0
- data/lib/rgame/engine/nav_grid.rb +87 -0
- data/lib/rgame/engine/node2d.rb +364 -80
- data/lib/rgame/engine/path.rb +4 -6
- data/lib/rgame/engine/player.rb +69 -0
- data/lib/rgame/engine/player_layer.rb +70 -0
- data/lib/rgame/engine/players.rb +205 -0
- data/lib/rgame/engine/presentation.rb +171 -0
- data/lib/rgame/engine/scene/scene_stack.rb +29 -7
- data/lib/rgame/engine/sealed_privates.rb +54 -0
- data/lib/rgame/engine/spatial_hash.rb +53 -8
- data/lib/rgame/engine/text.rb +194 -0
- data/lib/rgame/engine/tile_blockers.rb +63 -0
- data/lib/rgame/engine/tile_map.rb +2 -3
- data/lib/rgame/engine/tile_map_layer.rb +82 -0
- data/lib/rgame/engine/tileset.rb +2 -4
- data/lib/rgame/engine/timer.rb +2 -2
- data/lib/rgame/engine/ui/button.rb +248 -0
- data/lib/rgame/engine/ui/column.rb +20 -0
- data/lib/rgame/engine/ui/icon_button.rb +93 -0
- data/lib/rgame/engine/ui/menu.rb +290 -0
- data/lib/rgame/engine/ui/navigation.rb +57 -0
- data/lib/rgame/engine/ui/nine_slice_style.rb +50 -0
- data/lib/rgame/engine/ui/option_button.rb +163 -0
- data/lib/rgame/engine/ui/panel_button.rb +32 -0
- data/lib/rgame/engine/ui/panel_menu.rb +36 -0
- data/lib/rgame/engine/ui/pointing.rb +146 -0
- data/lib/rgame/engine/ui/radial_menu.rb +85 -0
- data/lib/rgame/engine/ui/ring.rb +55 -0
- data/lib/rgame/engine/ui/row.rb +21 -0
- data/lib/rgame/engine/ui/shape_style.rb +102 -0
- data/lib/rgame/engine/ui/stack.rb +58 -0
- data/lib/rgame/engine/ui/stepping.rb +93 -0
- data/lib/rgame/engine/ui/text_button.rb +59 -0
- data/lib/rgame/engine/view.rb +76 -0
- data/lib/rgame/engine/viewports.rb +171 -0
- data/lib/rgame/engine/world_view.rb +71 -0
- data/lib/rgame/engine.rb +43 -25
- data/lib/rgame/game.rb +164 -22
- data/lib/rgame/rubocop/cop/game/draw_in_local_space.rb +103 -0
- data/lib/rgame/rubocop/cop/game/hot_path.rb +36 -0
- data/lib/rgame/rubocop/cop/game/layer_boundary.rb +43 -0
- data/lib/rgame/rubocop/cop/game/no_core_in_engine_layer.rb +100 -0
- data/lib/rgame/rubocop/cop/game/no_engine_in_core_layer.rb +84 -0
- data/lib/rgame/rubocop/cop/game/no_interpolation_in_hot_path.rb +50 -0
- data/lib/rgame/rubocop/cop/game/no_literal_text.rb +41 -0
- data/lib/rgame/rubocop/cop/game/no_needless_allocation.rb +112 -0
- data/lib/rgame/rubocop/default.yml +39 -0
- data/lib/rgame/rubocop/plugin.rb +45 -0
- data/lib/rgame/rubocop.rb +11 -0
- data/lib/rgame/util/color.rb +20 -24
- data/lib/rgame/util/controls.rb +106 -44
- data/lib/rgame/util/route_search.rb +27 -0
- data/lib/rgame/util/save_file.rb +107 -0
- data/lib/rgame/util/solid_grid.rb +37 -0
- data/lib/rgame/util/tensor.rb +0 -9
- data/lib/rgame/util/tile_sweep.rb +36 -0
- data/lib/rgame/util/z.rb +123 -0
- data/lib/rgame/util.rb +5 -3
- data/lib/rgame/version.rb +1 -1
- data/lib/rgame.rb +0 -15
- metadata +176 -20
- data/lib/rgame/engine/actor.rb +0 -53
- data/lib/rgame/engine/body.rb +0 -49
- data/lib/rgame/engine/cached_label.rb +0 -33
- data/lib/rgame/engine/camera_view.rb +0 -28
- data/lib/rgame/engine/input/player_controller.rb +0 -14
- data/lib/rgame/engine/matrix.rb +0 -32
- data/lib/rgame/engine/resettable.rb +0 -67
- data/lib/rgame/engine/tile_collision.rb +0 -78
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. You can check out the examples to get a feel for its capabilities.
|
|
4
8
|
|
|
5
9
|
## Why does this exist and should you use it?
|
|
6
10
|
|
|
@@ -10,19 +14,41 @@ RGame puts a lot of emphazis on testing and being testable: It separates the lay
|
|
|
10
14
|
|
|
11
15
|
It also tries to marry the beauty of Ruby with the hard performance requirements of games: Hot paths have no per-frame allocation, because garbadge collection is what really slows down Ruby interpreation, and math-heavy use-cases are backed by C code instead of Ruby classes.
|
|
12
16
|
|
|
13
|
-
Should you use it, though? If you're looking for something mature, free, and more battle-tested take a look at Godot instead. If you're looking for something mature and battle-tested
|
|
17
|
+
Should you use it, though? If you're looking for something mature, free, and more battle-tested take a look at Godot instead. If you're looking for something mature and battle-tested _in Ruby land_, take a look at dragonruby instead (it's not free, but it's probably worth the price).
|
|
14
18
|
|
|
15
19
|
If you're just starting with game development and planning on making the next big indie hit, might as well pick this one as the engine for the game you never finish!
|
|
16
20
|
|
|
17
|
-
In all seriousness, though: This is a hobby project of mine, and while it might develop into something actually useful, at the time of writing it's a playground. If you search for something I searched and found nothing
|
|
21
|
+
In all seriousness, though: This is a hobby project of mine, and while it might develop into something actually useful, at the time of writing it's a playground. If you search for something I searched and found nothing - try RGame! If you want to learn how to write games or just need a small prototype, and you really like Ruby - try RGame! If all you know is Rails, but you want to make a game that doesn't run in a browser and don't care about shipping it - try RGame!
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
I would be really happy if someone else actually uses it, but at this point I can't really recommend it for anything else than small projects and/or learning the ropes of game development.
|
|
24
|
+
|
|
25
|
+
## Getting started
|
|
26
|
+
|
|
27
|
+
Installing the gem puts an `rgame` command on your PATH, which you can use to setup a project:
|
|
20
28
|
|
|
21
29
|
```
|
|
30
|
+
gem install rgame
|
|
31
|
+
rgame new tictactoe
|
|
32
|
+
|
|
33
|
+
cd tictactoe
|
|
34
|
+
bundle install
|
|
35
|
+
bundle exec rspec # the game logic, headless — no window needed
|
|
36
|
+
ruby main.rb # the game itself
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
What it writes is small — a game class, a root node, a spec and the usual configuration — but it is laid out the way the engine wants to be used: one file loads SDL, everything else stays graphics-free and therefore testable with no display. [The `rgame` command](docs/api/cli.md) explains the layout and why it matters.
|
|
40
|
+
|
|
41
|
+
`gem install` compiles two C extensions, so the [requirements](#requirements) below have to be in place first. That's the one big hurdle at the moment: This gem ships source-only, so you have to compile a lot of C _on your machine_ to get this running.
|
|
42
|
+
|
|
43
|
+
## Hello world
|
|
44
|
+
|
|
45
|
+
The simplest "game" you can write, all in one file:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
22
48
|
require 'rgame/game'
|
|
23
49
|
|
|
24
50
|
class Scene < RGame::Engine::Node2D
|
|
25
|
-
def on_draw(renderer)
|
|
51
|
+
def on_draw(renderer, _view)
|
|
26
52
|
renderer.text('Hello world!', 250, 200)
|
|
27
53
|
end
|
|
28
54
|
end
|
|
@@ -37,47 +63,41 @@ game.start
|
|
|
37
63
|
|
|
38
64
|
You can learn more about how it works in the [documentation](docs/api/README.md).
|
|
39
65
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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.
|
|
66
|
+
## Examples
|
|
67
|
+
|
|
68
|
+
`examples/` holds one small program per concept, each a single file you can run with `ruby examples/<name>/main.rb`. [The examples page](docs/api/examples.md) describes them in more detail. They're also all full of code comments so you have an easier time learning the concepts.
|
|
69
|
+
|
|
70
|
+
| Example | Shows |
|
|
71
|
+
|---|---|
|
|
72
|
+
| [walk](docs/api/examples.md#walk) | A player-controlled sprite — a node, three components, input as actions |
|
|
73
|
+
| [sprite](docs/api/examples.md#sprite) | One frame drawn at a node, with no animation behind it |
|
|
74
|
+
| [velocity](docs/api/examples.md#velocity) | Movement with nobody driving: a velocity, integrated |
|
|
75
|
+
| [scroll_map](docs/api/examples.md#scroll_map) | A Tiled map larger than the window, scrolled by a camera |
|
|
76
|
+
| [collision](docs/api/examples.md#collision) | Two shapes touching, and who gets told about it |
|
|
77
|
+
| [collision_tiles](docs/api/examples.md#collision_tiles) | Walking into a wall of solid tiles, and sliding along it |
|
|
78
|
+
| [jump_topdown](docs/api/examples.md#jump_topdown) | A hop in a top-down view, where the sprite rises and the feet stay on the ground |
|
|
79
|
+
| [signals](docs/api/examples.md#signals) | A node announcing something happened, to nobody in particular |
|
|
80
|
+
| [timer](docs/api/examples.md#timer) | Things that happen on a clock, with nothing pressed |
|
|
81
|
+
| [pooling](docs/api/examples.md#pooling) | Spawning a lot of things without allocating them |
|
|
82
|
+
| [game_menu](docs/api/examples.md#game_menu) | A menu over a world that keeps running |
|
|
83
|
+
| [menu_navigation](docs/api/examples.md#menu_navigation) | Several screens, and settings that persist |
|
|
84
|
+
| [radial_menu](docs/api/examples.md#radial_menu) | A wheel of icons, chosen by the direction of the stick |
|
|
85
|
+
| [quick_wheel](docs/api/examples.md#quick_wheel) | A wheel held open by a button and chosen by letting go |
|
|
86
|
+
| [skill_bar](docs/api/examples.md#skill_bar) | A row of tools, stepped through or fired by hotkeys |
|
|
87
|
+
| [sound](docs/api/examples.md#sound) | A sound effect fired by a button, and the seam it travels |
|
|
88
|
+
| [music](docs/api/examples.md#music) | A looping track, started and stopped |
|
|
89
|
+
| [split_screen](docs/api/examples.md#split_screen) | Two players in one world, drawn once per viewport |
|
|
90
|
+
| [input_glyphs](docs/api/examples.md#input_glyphs) | Prompts that match the device in your hands |
|
|
91
|
+
| [fullscreen](docs/api/examples.md#fullscreen) | Fullscreen, switched at any time, and the scale modes |
|
|
92
|
+
| [save_load](docs/api/examples.md#save_load) | Writing game state to disk and putting it back |
|
|
93
|
+
| [save_load_ids](docs/api/examples.md#save_load_ids) | A save that has to name things, and why a reference forces ids |
|
|
94
|
+
| [localization](docs/api/examples.md#localization) | The same screen in two languages, switched and remembered |
|
|
95
|
+
| [pathfinding](docs/api/examples.md#pathfinding) | Setting a target for an actor and let if find its way there |
|
|
78
96
|
|
|
79
97
|
## Requirements
|
|
80
98
|
|
|
99
|
+
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. This _also_ means you need to install some system libaries and have header files present.
|
|
100
|
+
|
|
81
101
|
### C engine
|
|
82
102
|
|
|
83
103
|
- A C compiler — `gcc` or `clang`
|
|
@@ -100,17 +120,15 @@ fixture and needs `libvorbisenc` to run.
|
|
|
100
120
|
|
|
101
121
|
### Ruby side
|
|
102
122
|
|
|
103
|
-
- **Ruby 4.0.5**, pinned in `.ruby-version`.
|
|
104
|
-
|
|
105
|
-
`.ruby-version`); any version manager that reads `.ruby-version` works just
|
|
106
|
-
as well.
|
|
107
|
-
- **Ruby development headers.** Version-manager builds (mise, rbenv, rvm,
|
|
108
|
-
asdf) include them. On a distro-packaged Ruby, install `ruby-dev`
|
|
123
|
+
- **Ruby 4.0.5**, pinned in `.ruby-version`.
|
|
124
|
+
- **Ruby development headers.** Version-manager builds (mise, rbenv, rvm, asdf) include them. On a distro-packaged Ruby, install `ruby-dev`
|
|
109
125
|
(Debian/Ubuntu). These are what `extconf.rb` compiles against.
|
|
110
126
|
- **Bundler**, then `bundle install` for the dev/test gems (RSpec, RuboCop).
|
|
111
127
|
|
|
112
|
-
Nothing else — the engine has no runtime Ruby dependencies, and the `Gemfile`
|
|
113
|
-
|
|
128
|
+
Nothing else — the engine has no runtime Ruby dependencies, and the `Gemfile` holds only development gems.
|
|
129
|
+
|
|
130
|
+
All three platforms below are built and tested on every push by
|
|
131
|
+
[CI](.github/workflows/ci.yml) - with that people have usually at home, so Apple Silicon and not Apple Intel, etc.
|
|
114
132
|
|
|
115
133
|
### Debian / Ubuntu
|
|
116
134
|
|
|
@@ -118,13 +136,67 @@ holds only development gems.
|
|
|
118
136
|
sudo apt install build-essential pkg-config libsdl2-dev libgl1-mesa-dev check
|
|
119
137
|
```
|
|
120
138
|
|
|
139
|
+
`rake spec:core` opens real windows and starts its own Xvfb, which needs a few
|
|
140
|
+
more packages — the display itself, the `xwininfo` it polls to know the display
|
|
141
|
+
is up, a software rasteriser (Xvfb has no GPU, and `SDL_GL_CreateContext` fails
|
|
142
|
+
without one) and the XTEST runtime for synthetic keystrokes:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
sudo apt install xvfb x11-utils libgl1-mesa-dri libxtst6
|
|
146
|
+
```
|
|
147
|
+
|
|
121
148
|
### macOS (Homebrew)
|
|
122
149
|
|
|
123
150
|
```
|
|
124
151
|
brew install sdl2 pkg-config check
|
|
125
152
|
```
|
|
126
153
|
|
|
127
|
-
OpenGL
|
|
154
|
+
OpenGL ships with the Xcode Command Line Tools (`xcode-select --install`) — the
|
|
155
|
+
full Xcode is not needed, and neither is anything else: `rake spec:core` uses
|
|
156
|
+
the native window server, so there is no Xvfb equivalent to set up. Note
|
|
157
|
+
Homebrew's `sdl2` formula now installs **sdl2-compat**, which is SDL2's API
|
|
158
|
+
implemented on top of SDL3; the engine works through it unchanged.
|
|
159
|
+
|
|
160
|
+
### Windows
|
|
161
|
+
|
|
162
|
+
Use a **RubyInstaller-built** Ruby (mise, vfox and rbenv-style managers all
|
|
163
|
+
fetch those), which is what supplies `ridk`. The combined DevKit installer is
|
|
164
|
+
not required — a standalone MSYS2 that `ridk` can find works just as well, and
|
|
165
|
+
`C:\msys64` is one of the places it looks:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
winget install --id MSYS2.MSYS2 -e
|
|
169
|
+
ridk exec pacman -Syu --noconfirm # core update; may need a second pass
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Then the libraries. Note `make` is an **msys** package with no prefix while
|
|
173
|
+
everything else is **ucrt64**-prefixed — that split is the whole Windows story:
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
ridk exec pacman -S --needed \
|
|
177
|
+
mingw-w64-ucrt-x86_64-SDL2 \
|
|
178
|
+
mingw-w64-ucrt-x86_64-check \
|
|
179
|
+
mingw-w64-ucrt-x86_64-pkgconf \
|
|
180
|
+
mingw-w64-ucrt-x86_64-gcc \
|
|
181
|
+
make
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**MSYS2 is several environments in one install, and picking the wrong one fails
|
|
185
|
+
in a way that looks like missing packages.** UCRT64 builds native Windows
|
|
186
|
+
binaries, which is what RubyInstaller's Ruby can load; the plain `msys`
|
|
187
|
+
environment builds against a Cygwin-like runtime, which it cannot. Work from
|
|
188
|
+
the "MSYS2 UCRT64" shell or run `ridk enable ucrt64` first, and verify before
|
|
189
|
+
trusting anything:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
which gcc # must be /ucrt64/bin/gcc, NOT /usr/bin/gcc
|
|
193
|
+
pkg-config --cflags sdl2 # must print a ucrt64 include path
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
One more Windows fact worth knowing up front: **Check has no usable fork
|
|
197
|
+
there**, so the first segfault kills the whole test binary and the output stops
|
|
198
|
+
mid-suite. Use a debugger rather than reading the log — `CK_FORK=no gdb --args
|
|
199
|
+
./build/test_rgame`, and `CK_RUN_SUITE=<name>` to run one suite.
|
|
128
200
|
|
|
129
201
|
## Build & run
|
|
130
202
|
|
|
@@ -156,7 +228,7 @@ rake spec # headless specs: RGame::Util, the engine layer, packaging
|
|
|
156
228
|
rake spec:core # RGame::Core specs; opens real windows, boots its own Xvfb
|
|
157
229
|
rake # everything: make test, rake spec, rake spec:core
|
|
158
230
|
bundle exec rubocop # lint; configured in .rubocop.yml, which also loads the
|
|
159
|
-
# project's own cops from
|
|
231
|
+
# project's own cops from lib/rgame/rubocop/.
|
|
160
232
|
```
|
|
161
233
|
|
|
162
234
|
The two Ruby suites are two directories and two processes on purpose. `spec/`
|
|
@@ -184,11 +256,17 @@ ruby ext/rgame_core/example.rb
|
|
|
184
256
|
|
|
185
257
|
## Packaging
|
|
186
258
|
|
|
187
|
-
Both extensions and the Ruby layer ship as one gem, built from `rgame.gemspec
|
|
259
|
+
Both extensions and the Ruby layer ship as one gem, built from `rgame.gemspec` and published at [rubygems.org/gems/rgame](https://rubygems.org/gems/rgame):
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
gem install rgame # from RubyGems; compiles both extensions here
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Or from a checkout, which is the same gem built locally:
|
|
188
266
|
|
|
189
267
|
```
|
|
190
268
|
rake build # package into pkg/rgame-<version>.gem
|
|
191
|
-
gem install pkg/rgame
|
|
269
|
+
gem install pkg/rgame-*.gem # compiles both extensions on this machine
|
|
192
270
|
```
|
|
193
271
|
|
|
194
272
|
`gem install` runs each `extconf.rb` and installs the resulting `.so` into the
|
|
@@ -207,200 +285,21 @@ that no build artifact, spec directory or plan is, so the parts of this that
|
|
|
207
285
|
would otherwise be a checklist fail the suite instead.
|
|
208
286
|
|
|
209
287
|
The version is `RGame::VERSION` in [lib/rgame/version.rb](lib/rgame/version.rb).
|
|
210
|
-
Nothing is published to RubyGems.
|
|
211
288
|
|
|
212
289
|
## Project structure
|
|
213
290
|
|
|
214
|
-
The
|
|
215
|
-
|
|
216
|
-
and runs each `extconf.rb`, which can only build sources inside its own
|
|
217
|
-
directory, so keeping the C there means one copy of the code serves both the
|
|
218
|
-
standalone binary and the gem.
|
|
291
|
+
The C lives under `ext/rgame_core/` rather than a top-level `src/`, because`gem install` runs each `extconf.rb` and an extension can only build sources inside its own directory — so one copy of the code serves both the standalone binary and the gem. The Ruby half is split the same way it is namespaced:
|
|
292
|
+
`lib/rgame/util/`, `lib/rgame/core/` and `lib/rgame/engine/`.
|
|
219
293
|
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
```
|
|
359
|
-
|
|
360
|
-
Three test suites: `make test` covers the C (Check), `rake spec` the headless
|
|
361
|
-
Ruby half, `rake spec:core` the parts that open a window. None needs a display
|
|
362
|
-
of its own — `spec:core` boots Xvfb itself. `rake` runs all three.
|
|
294
|
+
A file-by-file map of the whole repository is in
|
|
295
|
+
[docs/project_structure.md](docs/project_structure.md).
|
|
363
296
|
|
|
364
297
|
## Roadmap
|
|
365
298
|
|
|
366
|
-
|
|
367
|
-
Drawing primitives are the current gap.
|
|
368
|
-
2. **Ruby C extensions** (done in first form) — `RGame::Core::App` wraps
|
|
369
|
-
`include/rgame/core.h`, so the engine can be driven from Ruby
|
|
370
|
-
(`ext/rgame_core/example.rb`); `RGame::Util::Tensor` covers the
|
|
371
|
-
graphics-free half.
|
|
372
|
-
3. **Pure-Ruby half** (started) — `lib/` holds the namespace loaders; so far
|
|
373
|
-
the classes underneath them are all C-backed.
|
|
374
|
-
4. **Gem** (done) — `rgame.gemspec` packages both halves, building each
|
|
375
|
-
extension into `lib/rgame/` on install the way `make ext` does in a
|
|
376
|
-
checkout. Not published to RubyGems.
|
|
377
|
-
|
|
378
|
-
## Ruby API
|
|
299
|
+
There is currently no roadmap for the next version.
|
|
379
300
|
|
|
380
|
-
|
|
301
|
+
## AI clause
|
|
381
302
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
class MyGame < RGame::Core::App
|
|
386
|
-
def initialize = super(width: 800, height: 600, caption: "title")
|
|
387
|
-
|
|
388
|
-
def update(dt); end # one fixed simulation tick
|
|
389
|
-
def draw; end # render one frame
|
|
390
|
-
def needs_redraw?; end # false skips the draw
|
|
391
|
-
def button_down(id); end # discrete key press
|
|
392
|
-
end
|
|
393
|
-
|
|
394
|
-
MyGame.new.run
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
The util half, with no graphics libraries loaded:
|
|
398
|
-
|
|
399
|
-
```ruby
|
|
400
|
-
require "rgame"
|
|
401
|
-
|
|
402
|
-
grid = RGame::Util::Tensor.new(width, height, depth, initial: nil)
|
|
403
|
-
grid[x, y, z] = value
|
|
404
|
-
grid[x, y, z]
|
|
405
|
-
grid.width # => Integer, also #height / #depth
|
|
406
|
-
```
|
|
303
|
+
This project is not vibe-coded, but AI tools were used heavily while
|
|
304
|
+
writing code. If you dislike AI generated code, this project is not for
|
|
305
|
+
you.
|