rgame 0.2.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 +135 -1
- data/README.md +67 -65
- data/docs/api/README.md +86 -82
- data/docs/api/app.md +125 -68
- data/docs/api/assets.md +203 -165
- data/docs/api/audio.md +130 -89
- data/docs/api/cli.md +259 -0
- data/docs/api/components.md +1032 -265
- data/docs/api/drawing.md +160 -160
- data/docs/api/examples.md +263 -0
- data/docs/api/game.md +149 -50
- data/docs/api/images.md +39 -39
- data/docs/api/input.md +226 -148
- data/docs/api/internals.md +241 -62
- data/docs/api/localization.md +285 -0
- data/docs/api/scene_graph.md +397 -244
- data/docs/api/signals.md +81 -76
- data/docs/api/systems.md +186 -73
- data/docs/api/text.md +58 -41
- data/docs/api/tile_maps.md +237 -0
- data/docs/api/toolbox.md +395 -146
- data/docs/api/ui.md +883 -54
- data/docs/api/values.md +216 -33
- 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/rgame_core/app/app.c +33 -3
- 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 +39 -2
- data/ext/rgame_core/example.rb +0 -49
- data/ext/rgame_core/extconf.rb +0 -125
- data/ext/rgame_core/include/rgame/core.h +38 -2
- data/ext/rgame_core/ruby/audio_ext.c +10 -5
- data/ext/rgame_core/ruby/core_ext.c +30 -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 +20 -20
- 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 -44
- 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/locale.rb +22 -0
- data/lib/rgame/core/nine_slice.rb +0 -21
- data/lib/rgame/core/renderer.rb +6 -63
- data/lib/rgame/core/sprite_sheet.rb +0 -3
- data/lib/rgame/core/tile_map_renderer.rb +2 -19
- 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 +3 -3
- 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 +34 -10
- data/lib/rgame/engine/components/action_trigger.rb +0 -1
- data/lib/rgame/engine/components/animated_sprite.rb +27 -25
- data/lib/rgame/engine/components/box_collider.rb +99 -0
- data/lib/rgame/engine/components/camera_follow.rb +6 -5
- data/lib/rgame/engine/components/character_body.rb +20 -61
- 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 +12 -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 +41 -13
- 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 +4 -6
- data/lib/rgame/engine/debug_overlay.rb +5 -11
- 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 +0 -26
- data/lib/rgame/engine/input/actions.rb +2 -8
- data/lib/rgame/engine/input/input_map.rb +39 -21
- data/lib/rgame/engine/nav_grid.rb +87 -0
- data/lib/rgame/engine/node2d.rb +248 -133
- data/lib/rgame/engine/path.rb +4 -6
- data/lib/rgame/engine/players.rb +6 -13
- data/lib/rgame/engine/presentation.rb +171 -0
- data/lib/rgame/engine/scene/scene_stack.rb +4 -4
- data/lib/rgame/engine/sealed_privates.rb +54 -0
- data/lib/rgame/engine/spatial_hash.rb +51 -19
- 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 +0 -2
- 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 +246 -71
- 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/viewports.rb +2 -5
- data/lib/rgame/engine/world_view.rb +5 -4
- data/lib/rgame/engine.rb +31 -25
- data/lib/rgame/game.rb +99 -27
- 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 +14 -28
- 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 +4 -14
- data/lib/rgame/util.rb +4 -3
- data/lib/rgame/version.rb +1 -1
- data/lib/rgame.rb +0 -15
- metadata +152 -11
- 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/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/lib/rgame/engine/ui/menu_item.rb +0 -84
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 54caa0772adc1eb3211edbd694540c196be0018d6482089447d727f5c871e19a
|
|
4
|
+
data.tar.gz: 16b3d1de8baefc2579bbf5a89fb7b42041d174567b6c7059d5c967f2598621f5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ab441100a4d86c34d93efed615b350a7d65ad819d3b7929e868e709c6a9790b9c78a956b9860632e62750f0880ca9656a1f7f6c98cc3df6c1b19f7d229e40919
|
|
7
|
+
data.tar.gz: e98b7a96d21700bf0533f0155be99ffc9f480e35fda2375aaee4b8b2a7bb65b871d0a70a1f2486873b32e28a83aaa4abfc91a14991226153f57959b0d62237b4
|
data/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,139 @@ index, not the argument.
|
|
|
12
12
|
|
|
13
13
|
## [Unreleased]
|
|
14
14
|
|
|
15
|
+
## [0.3.0] - 2026-09-15
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **`rgame new NAME`.** The gem installs an `rgame` command, and `rgame new`
|
|
20
|
+
generates a project: a game class, a root node, an English translation table,
|
|
21
|
+
and a spec suite and RuboCop config that pass. Only `game.rb` loads SDL, so the
|
|
22
|
+
generated specs run without a display. See [docs/api/cli.md](docs/api/cli.md).
|
|
23
|
+
- **RuboCop cops for games.** The gem ships rgame's `Game/` cops as a RuboCop
|
|
24
|
+
plugin, and `rgame new` enables them. They catch per-frame allocations, a node
|
|
25
|
+
drawn at its own position twice, literal text and Core named from headless
|
|
26
|
+
code. See [docs/api/cli.md](docs/api/cli.md#the-generated-rubocop-configuration).
|
|
27
|
+
- **Translation by default.** `RGame::Game` loads every `locales/**/*.yml` and
|
|
28
|
+
picks the player's language from `RGame::Core.preferred_locales`. A node draws
|
|
29
|
+
an `Engine::Text` built from a key, and a UI button's `label:` is a key. See
|
|
30
|
+
[docs/api/localization.md](docs/api/localization.md).
|
|
31
|
+
- **Fullscreen and scaling.** `Game.new` takes `fullscreen:` and `scale_mode:`,
|
|
32
|
+
and both can change while the game runs. `scale_mode:` maps the logical size
|
|
33
|
+
onto the window, and defaults to `:letterbox`. See
|
|
34
|
+
[docs/api/game.md](docs/api/game.md).
|
|
35
|
+
- **Saving.** `Util::SaveFile` reads and writes a game's state in the player's
|
|
36
|
+
save directory. `Components::Identity` gives a node a stable id to save it
|
|
37
|
+
under. See [docs/api/values.md](docs/api/values.md).
|
|
38
|
+
- **Box colliders.** `Components::BoxCollider` is a rectangle in the same
|
|
39
|
+
`CollisionWorld` as `CircleCollider`. `FeetCollider` derives its box from the
|
|
40
|
+
node's size, so a top-down character collides at its feet. See
|
|
41
|
+
[docs/api/components.md](docs/api/components.md).
|
|
42
|
+
- **Movers that something can stop.** `CharacterBody`, `Velocity` and
|
|
43
|
+
`PathFollow` share the base `Components::Mover`. Its `blocked_by:` lists what
|
|
44
|
+
stops a step: solid tiles, the world's edge or collider layers. `on_blocked` and
|
|
45
|
+
`on_unblocked` report the blocker and the axis. See
|
|
46
|
+
[docs/api/components.md](docs/api/components.md).
|
|
47
|
+
- **Pathfinding.** `Engine::NavGrid` finds routes over a tile grid, and
|
|
48
|
+
`TileWorld#nav_grid` hands one out. `Components::Navigator#go_to` walks a node
|
|
49
|
+
there. The search runs in C, in `Util::SolidGrid`, `Util::RouteSearch` and
|
|
50
|
+
`Util::TileSweep`. See [docs/api/toolbox.md](docs/api/toolbox.md).
|
|
51
|
+
- **`Components::World`** gives a scene its bounds without a tile map.
|
|
52
|
+
`ScreenWrap` and `DespawnOffscreen` read the bounds from it or from
|
|
53
|
+
`TileWorld`. See [docs/api/components.md](docs/api/components.md).
|
|
54
|
+
- **`Components::Hop`** lifts a character's picture off the ground through the
|
|
55
|
+
new `Node2D#elevation`. Colliders and cameras ignore the lift. See
|
|
56
|
+
[docs/api/components.md](docs/api/components.md).
|
|
57
|
+
- **New buttons and styles.** `UI::Button` is the base for a button with its
|
|
58
|
+
own look. `TextButton`, `PanelButton`, `OptionButton` and `IconButton` draw
|
|
59
|
+
their background from a `NineSliceStyle` or a `ShapeStyle`. A `hotkey:` presses
|
|
60
|
+
a button without focusing it. See [docs/api/ui.md](docs/api/ui.md).
|
|
61
|
+
- **Menu layouts and navigations.** `UI::Menu` takes a `layout:` (`Column`,
|
|
62
|
+
`Row` or `Ring`) and a `navigation:` (`Stepping`, `Pointing` or `nil`).
|
|
63
|
+
`UI::RadialMenu` focuses whatever the stick points at. `UI::PanelMenu` draws a
|
|
64
|
+
panel that grows with its buttons. See [docs/api/ui.md](docs/api/ui.md).
|
|
65
|
+
- **Menus open and close.** A menu answers `open?`, `open` and `close`, and
|
|
66
|
+
emits `on_opened` and `on_closed`. A closed menu draws nothing and takes no
|
|
67
|
+
input. `trigger:` names an action that holds a menu open while it is down. See
|
|
68
|
+
[docs/api/ui.md](docs/api/ui.md).
|
|
69
|
+
- **A UI atlas can name images.** A descriptor's `images` section cuts
|
|
70
|
+
rectangles from the sheet, and `Renderer#register_ui_atlas` registers each by
|
|
71
|
+
name. `UI::IconButton.new(image: :home)` then draws one.
|
|
72
|
+
- **Input prompts.** `InputMap#button_for(action, device)` returns the bound
|
|
73
|
+
button that device can press. `Controls.gamepad?` and `Controls.pad_button?`
|
|
74
|
+
tell the two kinds of id apart. See [docs/api/input.md](docs/api/input.md).
|
|
75
|
+
- **Sounds by path.** `audio.play_sound` and `play_music` accept a path and
|
|
76
|
+
load it through the asset manager on first use. `RGame::Game` subscribes an
|
|
77
|
+
`AudioDirector`, so `AudioBus` sounds play without setup. See
|
|
78
|
+
[docs/api/audio.md](docs/api/audio.md).
|
|
79
|
+
- **24 examples, one concept each.** Each is a single `main.rb` that runs on its
|
|
80
|
+
own, from `walk` and `sprite` to `pathfinding` and `localization`. See
|
|
81
|
+
[docs/api/examples.md](docs/api/examples.md).
|
|
82
|
+
- `AssetManager#glob` lists the files under the media root that match a pattern.
|
|
83
|
+
- `Util::Color` gains named colours, from `RED` to `DARK_GRAY`.
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
|
|
87
|
+
- **A subclass may not redefine `Node2D`'s or `Component`'s internal methods.**
|
|
88
|
+
Their private and protected machinery now starts with `_`. A subclass that
|
|
89
|
+
defines one of those names raises `NameError` where the class is defined.
|
|
90
|
+
Before, it silently switched that machinery off. See
|
|
91
|
+
[docs/api/scene_graph.md](docs/api/scene_graph.md).
|
|
92
|
+
- **Nodes draw in local space.** `Node2D#draw` pushes the node's transform
|
|
93
|
+
before it calls `on_draw`, so a node draws at its own origin:
|
|
94
|
+
`renderer.rect(0, 0, width, height)`. Drop `abs_x`/`abs_y` from draw calls, or
|
|
95
|
+
the position applies twice. See
|
|
96
|
+
[docs/api/scene_graph.md](docs/api/scene_graph.md).
|
|
97
|
+
- **`abs_x`, `abs_y` and `abs_angle` are now `world_x`, `world_y` and
|
|
98
|
+
`world_angle`.** A node computes them when read, so they are never a tick
|
|
99
|
+
stale. `world_x=` and `world_y=` place a node in world space. See
|
|
100
|
+
[docs/api/scene_graph.md](docs/api/scene_graph.md).
|
|
101
|
+
- **`UI::Menu` is handed its buttons.** `Menu#add(button)` replaces `add_item`,
|
|
102
|
+
`items` is now `buttons`, and `MenuItem` is now `UI::PanelButton`. The item
|
|
103
|
+
size moves to `layout: UI::Column.new(...)`. Confirm activates a button when
|
|
104
|
+
released. See [docs/api/ui.md](docs/api/ui.md).
|
|
105
|
+
- **`CharacterBody` takes its shape from a sibling collider.**
|
|
106
|
+
`CharacterBody.new(speed:, blocked_by:)` replaces `feet_width:` and
|
|
107
|
+
`feet_height:`. A body with no `blocked_by:` moves freely, and `TileWorld#move`
|
|
108
|
+
is gone. See [docs/api/components.md](docs/api/components.md).
|
|
109
|
+
- **Contacts are edges.** `on_hit` fires once when two colliders start to
|
|
110
|
+
overlap, and the new `on_separated` fires once when they part. Before,
|
|
111
|
+
`on_hit` fired on every step of an overlap.
|
|
112
|
+
- **`I18n` reads YAML tables keyed by locale.** A key falls back from `de-AT`
|
|
113
|
+
to `de` to the default. Plurals follow CLDR rules, and `I18n.missing` decides
|
|
114
|
+
what a missing key shows. `load_file` is gone. See
|
|
115
|
+
[docs/api/localization.md](docs/api/localization.md).
|
|
116
|
+
- **`ScreenWrap` and `DespawnOffscreen` work in world space**, so a node under an
|
|
117
|
+
offset parent wraps at the world's edge. Their `width:` and `height:` are now
|
|
118
|
+
optional. A node with two responses to the edge raises when the second
|
|
119
|
+
attaches.
|
|
120
|
+
- **A component raises when its sibling is missing.** `PlayerController`,
|
|
121
|
+
`WanderController` and `AnimatedSprite` name what they need when they attach.
|
|
122
|
+
A node with two movers raises too.
|
|
123
|
+
- **`AnimatedSprite` follows any mover**, and faces along the larger axis of
|
|
124
|
+
its heading.
|
|
125
|
+
- **Engine internals are private.** `Renderer`'s raw `draw_*`, `push_*`, `pop`
|
|
126
|
+
and `*_record` methods, `Recording#draw_at` and `Song#play_looping` are
|
|
127
|
+
private. Call `rect`, `translated`, `record`, `Recording#draw` and `Song#play`
|
|
128
|
+
instead. The Tiled parse helpers are private too.
|
|
129
|
+
|
|
130
|
+
### Removed
|
|
131
|
+
|
|
132
|
+
- `Engine::CachedLabel`. Use `Engine::Text`.
|
|
133
|
+
- `Engine::Body`, `Engine::Actor`, `Engine::Matrix`, `Engine::Resettable` and
|
|
134
|
+
`Engine::PlayerController`. None had a caller in the engine.
|
|
135
|
+
`Components::PlayerController` stays.
|
|
136
|
+
- `Engine::TileCollision`, which is now `Engine::TileBlockers`.
|
|
137
|
+
- The examples `14_asteroids`, `15_tiled_world` and `16_hello_world`. They are
|
|
138
|
+
whole games rather than examples of one concept, and no longer ship.
|
|
139
|
+
|
|
140
|
+
### Fixed
|
|
141
|
+
|
|
142
|
+
- The README's hello-world gave `on_draw` one parameter; it takes two
|
|
143
|
+
(`renderer, view`).
|
|
144
|
+
- A new `UI::Menu` drew its first item without a highlight until the first
|
|
145
|
+
press.
|
|
146
|
+
- `Path#distance_to` returned wrong distances for Integer coordinates.
|
|
147
|
+
|
|
15
148
|
## [0.2.0] - 2026-08-26
|
|
16
149
|
|
|
17
150
|
### Added
|
|
@@ -89,6 +222,7 @@ First release, and the first version that runs a game end to end.
|
|
|
89
222
|
- **`RGame::Game`** — the entry point that wires the two halves together.
|
|
90
223
|
- Examples: `14_asteroids`, `15_tiled_world`, `16_hello_world`.
|
|
91
224
|
|
|
92
|
-
[Unreleased]: https://github.com/psuessenb/rgame/compare/v0.
|
|
225
|
+
[Unreleased]: https://github.com/psuessenb/rgame/compare/v0.3.0...HEAD
|
|
226
|
+
[0.3.0]: https://github.com/psuessenb/rgame/compare/v0.2.0...v0.3.0
|
|
93
227
|
[0.2.0]: https://github.com/psuessenb/rgame/compare/v0.1.0...v0.2.0
|
|
94
228
|
[0.1.0]: https://github.com/psuessenb/rgame/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -4,7 +4,7 @@ RGame is a small 2D game engine for Ruby, written in Ruby and C. It's build on t
|
|
|
4
4
|
|
|
5
5
|
While still a work in progress, RGame aims to be more than a SDL/OpenGL
|
|
6
6
|
binding - it ships with high level features like a scene graph, sprites,
|
|
7
|
-
collision systems, debugging tools and an UI toolkit.
|
|
7
|
+
collision systems, debugging tools and an UI toolkit. You can check out the examples to get a feel for its capabilities.
|
|
8
8
|
|
|
9
9
|
## Why does this exist and should you use it?
|
|
10
10
|
|
|
@@ -14,19 +14,41 @@ RGame puts a lot of emphazis on testing and being testable: It separates the lay
|
|
|
14
14
|
|
|
15
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.
|
|
16
16
|
|
|
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
|
|
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).
|
|
18
18
|
|
|
19
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!
|
|
20
20
|
|
|
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
|
|
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!
|
|
22
22
|
|
|
23
|
-
|
|
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:
|
|
24
28
|
|
|
25
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
|
|
26
48
|
require 'rgame/game'
|
|
27
49
|
|
|
28
50
|
class Scene < RGame::Engine::Node2D
|
|
29
|
-
def on_draw(renderer)
|
|
51
|
+
def on_draw(renderer, _view)
|
|
30
52
|
renderer.text('Hello world!', 250, 200)
|
|
31
53
|
end
|
|
32
54
|
end
|
|
@@ -41,9 +63,40 @@ game.start
|
|
|
41
63
|
|
|
42
64
|
You can learn more about how it works in the [documentation](docs/api/README.md).
|
|
43
65
|
|
|
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 |
|
|
96
|
+
|
|
44
97
|
## Requirements
|
|
45
98
|
|
|
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.
|
|
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.
|
|
47
100
|
|
|
48
101
|
### C engine
|
|
49
102
|
|
|
@@ -67,20 +120,15 @@ fixture and needs `libvorbisenc` to run.
|
|
|
67
120
|
|
|
68
121
|
### Ruby side
|
|
69
122
|
|
|
70
|
-
- **Ruby 4.0.5**, pinned in `.ruby-version`.
|
|
71
|
-
|
|
72
|
-
`.ruby-version`); any version manager that reads `.ruby-version` works just
|
|
73
|
-
as well.
|
|
74
|
-
- **Ruby development headers.** Version-manager builds (mise, rbenv, rvm,
|
|
75
|
-
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`
|
|
76
125
|
(Debian/Ubuntu). These are what `extconf.rb` compiles against.
|
|
77
126
|
- **Bundler**, then `bundle install` for the dev/test gems (RSpec, RuboCop).
|
|
78
127
|
|
|
79
|
-
Nothing else — the engine has no runtime Ruby dependencies, and the `Gemfile`
|
|
80
|
-
holds only development gems.
|
|
128
|
+
Nothing else — the engine has no runtime Ruby dependencies, and the `Gemfile` holds only development gems.
|
|
81
129
|
|
|
82
130
|
All three platforms below are built and tested on every push by
|
|
83
|
-
[CI](.github/workflows/ci.yml).
|
|
131
|
+
[CI](.github/workflows/ci.yml) - with that people have usually at home, so Apple Silicon and not Apple Intel, etc.
|
|
84
132
|
|
|
85
133
|
### Debian / Ubuntu
|
|
86
134
|
|
|
@@ -180,7 +228,7 @@ rake spec # headless specs: RGame::Util, the engine layer, packaging
|
|
|
180
228
|
rake spec:core # RGame::Core specs; opens real windows, boots its own Xvfb
|
|
181
229
|
rake # everything: make test, rake spec, rake spec:core
|
|
182
230
|
bundle exec rubocop # lint; configured in .rubocop.yml, which also loads the
|
|
183
|
-
# project's own cops from
|
|
231
|
+
# project's own cops from lib/rgame/rubocop/.
|
|
184
232
|
```
|
|
185
233
|
|
|
186
234
|
The two Ruby suites are two directories and two processes on purpose. `spec/`
|
|
@@ -208,8 +256,7 @@ ruby ext/rgame_core/example.rb
|
|
|
208
256
|
|
|
209
257
|
## Packaging
|
|
210
258
|
|
|
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):
|
|
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):
|
|
213
260
|
|
|
214
261
|
```
|
|
215
262
|
gem install rgame # from RubyGems; compiles both extensions here
|
|
@@ -241,10 +288,7 @@ The version is `RGame::VERSION` in [lib/rgame/version.rb](lib/rgame/version.rb).
|
|
|
241
288
|
|
|
242
289
|
## Project structure
|
|
243
290
|
|
|
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:
|
|
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:
|
|
248
292
|
`lib/rgame/util/`, `lib/rgame/core/` and `lib/rgame/engine/`.
|
|
249
293
|
|
|
250
294
|
A file-by-file map of the whole repository is in
|
|
@@ -252,49 +296,7 @@ A file-by-file map of the whole repository is in
|
|
|
252
296
|
|
|
253
297
|
## Roadmap
|
|
254
298
|
|
|
255
|
-
|
|
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.
|
|
299
|
+
There is currently no roadmap for the next version.
|
|
298
300
|
|
|
299
301
|
## AI clause
|
|
300
302
|
|