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.
Files changed (265) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +228 -0
  3. data/README.md +142 -243
  4. data/docs/api/README.md +141 -90
  5. data/docs/api/app.md +125 -68
  6. data/docs/api/assets.md +203 -166
  7. data/docs/api/audio.md +130 -89
  8. data/docs/api/cli.md +259 -0
  9. data/docs/api/components.md +1045 -254
  10. data/docs/api/drawing.md +187 -119
  11. data/docs/api/examples.md +263 -0
  12. data/docs/api/game.md +165 -45
  13. data/docs/api/images.md +39 -39
  14. data/docs/api/input.md +344 -85
  15. data/docs/api/internals.md +241 -62
  16. data/docs/api/localization.md +285 -0
  17. data/docs/api/scene_graph.md +503 -123
  18. data/docs/api/signals.md +81 -76
  19. data/docs/api/systems.md +197 -64
  20. data/docs/api/text.md +58 -41
  21. data/docs/api/tile_maps.md +237 -0
  22. data/docs/api/toolbox.md +400 -147
  23. data/docs/api/ui.md +927 -0
  24. data/docs/api/values.md +234 -19
  25. data/examples/assets/README.md +322 -0
  26. data/examples/assets/blip.ogg +0 -0
  27. data/examples/assets/glyphs.json +5 -0
  28. data/examples/assets/glyphs.png +0 -0
  29. data/examples/assets/hero.json +12 -0
  30. data/examples/assets/hero.png +0 -0
  31. data/examples/assets/icons.json +13 -0
  32. data/examples/assets/icons.png +0 -0
  33. data/examples/assets/music.ogg +0 -0
  34. data/examples/assets/skills.json +10 -0
  35. data/examples/assets/skills.png +0 -0
  36. data/examples/assets/tileset.png +0 -0
  37. data/examples/assets/tileset.tsx +65 -0
  38. data/examples/assets/town.tmx +26 -0
  39. data/examples/assets/ui.json +11 -0
  40. data/examples/assets/ui.png +0 -0
  41. data/examples/collision/locales/en.yml +8 -0
  42. data/examples/collision/main.rb +316 -0
  43. data/examples/collision_tiles/locales/en.yml +9 -0
  44. data/examples/collision_tiles/main.rb +274 -0
  45. data/examples/fullscreen/locales/en.yml +10 -0
  46. data/examples/fullscreen/main.rb +216 -0
  47. data/examples/game_menu/locales/en.yml +8 -0
  48. data/examples/game_menu/main.rb +170 -0
  49. data/examples/input_glyphs/locales/en.yml +14 -0
  50. data/examples/input_glyphs/main.rb +213 -0
  51. data/examples/jump_topdown/locales/en.yml +9 -0
  52. data/examples/jump_topdown/main.rb +178 -0
  53. data/examples/localization/locales/de.yml +12 -0
  54. data/examples/localization/locales/en.yml +13 -0
  55. data/examples/localization/main.rb +158 -0
  56. data/examples/menu_navigation/locales/en.yml +23 -0
  57. data/examples/menu_navigation/main.rb +365 -0
  58. data/examples/music/locales/en.yml +7 -0
  59. data/examples/music/main.rb +134 -0
  60. data/examples/pathfinding/locales/en.yml +17 -0
  61. data/examples/pathfinding/main.rb +298 -0
  62. data/examples/pooling/locales/en.yml +7 -0
  63. data/examples/pooling/main.rb +259 -0
  64. data/examples/quick_wheel/locales/en.yml +16 -0
  65. data/examples/quick_wheel/main.rb +184 -0
  66. data/examples/radial_menu/locales/en.yml +16 -0
  67. data/examples/radial_menu/main.rb +184 -0
  68. data/examples/save_load/locales/en.yml +11 -0
  69. data/examples/save_load/main.rb +207 -0
  70. data/examples/save_load_ids/locales/en.yml +11 -0
  71. data/examples/save_load_ids/main.rb +322 -0
  72. data/examples/scroll_map/locales/en.yml +4 -0
  73. data/examples/scroll_map/main.rb +140 -0
  74. data/examples/signals/locales/en.yml +6 -0
  75. data/examples/signals/main.rb +278 -0
  76. data/examples/skill_bar/locales/en.yml +14 -0
  77. data/examples/skill_bar/main.rb +159 -0
  78. data/examples/sound/locales/en.yml +6 -0
  79. data/examples/sound/main.rb +122 -0
  80. data/examples/split_screen/locales/en.yml +9 -0
  81. data/examples/split_screen/main.rb +304 -0
  82. data/examples/sprite/locales/en.yml +8 -0
  83. data/examples/sprite/main.rb +180 -0
  84. data/examples/timer/locales/en.yml +12 -0
  85. data/examples/timer/main.rb +273 -0
  86. data/examples/velocity/locales/en.yml +6 -0
  87. data/examples/velocity/main.rb +196 -0
  88. data/examples/walk/locales/en.yml +4 -0
  89. data/examples/walk/main.rb +99 -0
  90. data/exe/rgame +9 -0
  91. data/ext/README.md +6 -5
  92. data/ext/rgame_core/app/app.c +215 -11
  93. data/ext/rgame_core/app/locale.c +67 -0
  94. data/ext/rgame_core/app/locale.h +28 -0
  95. data/ext/rgame_core/audio/audio.c +113 -2
  96. data/ext/rgame_core/example.rb +18 -56
  97. data/ext/rgame_core/extconf.rb +16 -113
  98. data/ext/rgame_core/graphics/canvas.c +45 -4
  99. data/ext/rgame_core/graphics/canvas.h +65 -10
  100. data/ext/rgame_core/graphics/clip.c +22 -13
  101. data/ext/rgame_core/include/rgame/core.h +151 -5
  102. data/ext/rgame_core/input/gamepad.c +57 -3
  103. data/ext/rgame_core/ruby/audio_ext.c +10 -5
  104. data/ext/rgame_core/ruby/core_ext.c +46 -7
  105. data/ext/rgame_core/ruby/core_ext.h +3 -0
  106. data/ext/rgame_core/ruby/locale_ext.c +44 -0
  107. data/ext/rgame_core/ruby/recording_ext.c +1 -1
  108. data/ext/rgame_core/ruby/renderer_ext.c +42 -19
  109. data/ext/rgame_util/color_ext.c +12 -3
  110. data/ext/rgame_util/extconf.rb +2 -20
  111. data/ext/rgame_util/route_search.c +305 -0
  112. data/ext/rgame_util/route_search.h +86 -0
  113. data/ext/rgame_util/route_search_ext.c +150 -0
  114. data/ext/rgame_util/solid_grid.c +58 -0
  115. data/ext/rgame_util/solid_grid.h +49 -0
  116. data/ext/rgame_util/solid_grid_ext.c +161 -0
  117. data/ext/rgame_util/tile_sweep.c +164 -0
  118. data/ext/rgame_util/tile_sweep.h +62 -0
  119. data/ext/rgame_util/tile_sweep_ext.c +155 -0
  120. data/ext/rgame_util/util_ext.c +3 -0
  121. data/ext/rgame_util/util_ext.h +15 -0
  122. data/lib/rgame/boot.rb +0 -10
  123. data/lib/rgame/cli/new_project.rb +139 -0
  124. data/lib/rgame/cli/templates/Gemfile.tt +23 -0
  125. data/lib/rgame/cli/templates/README.md.tt +93 -0
  126. data/lib/rgame/cli/templates/Rakefile.tt +9 -0
  127. data/lib/rgame/cli/templates/assets/locales/en.yml.tt +10 -0
  128. data/lib/rgame/cli/templates/game.rb.tt +23 -0
  129. data/lib/rgame/cli/templates/gitignore.tt +12 -0
  130. data/lib/rgame/cli/templates/main.rb.tt +11 -0
  131. data/lib/rgame/cli/templates/nodes/root.rb.tt +24 -0
  132. data/lib/rgame/cli/templates/rspec.tt +2 -0
  133. data/lib/rgame/cli/templates/rubocop.yml.tt +75 -0
  134. data/lib/rgame/cli/templates/ruby-version.tt +1 -0
  135. data/lib/rgame/cli/templates/spec/locales_spec.rb.tt +18 -0
  136. data/lib/rgame/cli/templates/spec/nodes/root_spec.rb.tt +18 -0
  137. data/lib/rgame/cli/templates/spec/spec_helper.rb.tt +37 -0
  138. data/lib/rgame/cli.rb +66 -0
  139. data/lib/rgame/core/app.rb +6 -42
  140. data/lib/rgame/core/asset_manager.rb +13 -31
  141. data/lib/rgame/core/audio.rb +37 -16
  142. data/lib/rgame/core/font.rb +0 -3
  143. data/lib/rgame/core/input.rb +35 -41
  144. data/lib/rgame/core/locale.rb +22 -0
  145. data/lib/rgame/core/nine_slice.rb +0 -21
  146. data/lib/rgame/core/recording.rb +3 -1
  147. data/lib/rgame/core/renderer.rb +75 -84
  148. data/lib/rgame/core/sprite_sheet.rb +0 -3
  149. data/lib/rgame/core/tile_map_renderer.rb +77 -65
  150. data/lib/rgame/core/ui_atlas.rb +28 -13
  151. data/lib/rgame/core.rb +1 -8
  152. data/lib/rgame/engine/actor_blockers.rb +131 -0
  153. data/lib/rgame/engine/animation_set.rb +1 -0
  154. data/lib/rgame/engine/audio_director.rb +36 -6
  155. data/lib/rgame/engine/bounds_blockers.rb +74 -0
  156. data/lib/rgame/engine/camera.rb +55 -10
  157. data/lib/rgame/engine/circle_collider.rb +4 -2
  158. data/lib/rgame/engine/collision_box.rb +26 -1
  159. data/lib/rgame/engine/collision_system.rb +110 -22
  160. data/lib/rgame/engine/component.rb +35 -1
  161. data/lib/rgame/engine/components/action_trigger.rb +0 -1
  162. data/lib/rgame/engine/components/animated_sprite.rb +31 -23
  163. data/lib/rgame/engine/components/box_collider.rb +99 -0
  164. data/lib/rgame/engine/components/camera_follow.rb +45 -0
  165. data/lib/rgame/engine/components/character_body.rb +21 -41
  166. data/lib/rgame/engine/components/circle_collider.rb +47 -11
  167. data/lib/rgame/engine/components/collision_world.rb +159 -31
  168. data/lib/rgame/engine/components/despawn_offscreen.rb +24 -8
  169. data/lib/rgame/engine/components/feet_collider.rb +61 -0
  170. data/lib/rgame/engine/components/hop.rb +76 -0
  171. data/lib/rgame/engine/components/identity.rb +73 -0
  172. data/lib/rgame/engine/components/mover.rb +285 -0
  173. data/lib/rgame/engine/components/navigator.rb +145 -0
  174. data/lib/rgame/engine/components/path_follow.rb +123 -31
  175. data/lib/rgame/engine/components/player_controller.rb +5 -2
  176. data/lib/rgame/engine/components/pool.rb +1 -1
  177. data/lib/rgame/engine/components/screen_wrap.rb +33 -11
  178. data/lib/rgame/engine/components/sprite.rb +22 -6
  179. data/lib/rgame/engine/components/targeting.rb +9 -11
  180. data/lib/rgame/engine/components/thrust_controller.rb +1 -1
  181. data/lib/rgame/engine/components/tile_world.rb +70 -29
  182. data/lib/rgame/engine/components/timer.rb +1 -1
  183. data/lib/rgame/engine/components/velocity.rb +23 -7
  184. data/lib/rgame/engine/components/wander_controller.rb +6 -2
  185. data/lib/rgame/engine/components/world.rb +133 -0
  186. data/lib/rgame/engine/contact_set.rb +74 -0
  187. data/lib/rgame/engine/culling.rb +45 -0
  188. data/lib/rgame/engine/debug_overlay.rb +23 -18
  189. data/lib/rgame/engine/i18n/plural.rb +45 -0
  190. data/lib/rgame/engine/i18n/plural_rules.rb +82 -0
  191. data/lib/rgame/engine/i18n/template.rb +59 -0
  192. data/lib/rgame/engine/i18n.rb +276 -51
  193. data/lib/rgame/engine/input/action_mapper.rb +76 -22
  194. data/lib/rgame/engine/input/actions.rb +63 -12
  195. data/lib/rgame/engine/input/input_map.rb +196 -0
  196. data/lib/rgame/engine/layout.rb +82 -0
  197. data/lib/rgame/engine/nav_grid.rb +87 -0
  198. data/lib/rgame/engine/node2d.rb +364 -80
  199. data/lib/rgame/engine/path.rb +4 -6
  200. data/lib/rgame/engine/player.rb +69 -0
  201. data/lib/rgame/engine/player_layer.rb +70 -0
  202. data/lib/rgame/engine/players.rb +205 -0
  203. data/lib/rgame/engine/presentation.rb +171 -0
  204. data/lib/rgame/engine/scene/scene_stack.rb +29 -7
  205. data/lib/rgame/engine/sealed_privates.rb +54 -0
  206. data/lib/rgame/engine/spatial_hash.rb +53 -8
  207. data/lib/rgame/engine/text.rb +194 -0
  208. data/lib/rgame/engine/tile_blockers.rb +63 -0
  209. data/lib/rgame/engine/tile_map.rb +2 -3
  210. data/lib/rgame/engine/tile_map_layer.rb +82 -0
  211. data/lib/rgame/engine/tileset.rb +2 -4
  212. data/lib/rgame/engine/timer.rb +2 -2
  213. data/lib/rgame/engine/ui/button.rb +248 -0
  214. data/lib/rgame/engine/ui/column.rb +20 -0
  215. data/lib/rgame/engine/ui/icon_button.rb +93 -0
  216. data/lib/rgame/engine/ui/menu.rb +290 -0
  217. data/lib/rgame/engine/ui/navigation.rb +57 -0
  218. data/lib/rgame/engine/ui/nine_slice_style.rb +50 -0
  219. data/lib/rgame/engine/ui/option_button.rb +163 -0
  220. data/lib/rgame/engine/ui/panel_button.rb +32 -0
  221. data/lib/rgame/engine/ui/panel_menu.rb +36 -0
  222. data/lib/rgame/engine/ui/pointing.rb +146 -0
  223. data/lib/rgame/engine/ui/radial_menu.rb +85 -0
  224. data/lib/rgame/engine/ui/ring.rb +55 -0
  225. data/lib/rgame/engine/ui/row.rb +21 -0
  226. data/lib/rgame/engine/ui/shape_style.rb +102 -0
  227. data/lib/rgame/engine/ui/stack.rb +58 -0
  228. data/lib/rgame/engine/ui/stepping.rb +93 -0
  229. data/lib/rgame/engine/ui/text_button.rb +59 -0
  230. data/lib/rgame/engine/view.rb +76 -0
  231. data/lib/rgame/engine/viewports.rb +171 -0
  232. data/lib/rgame/engine/world_view.rb +71 -0
  233. data/lib/rgame/engine.rb +43 -25
  234. data/lib/rgame/game.rb +164 -22
  235. data/lib/rgame/rubocop/cop/game/draw_in_local_space.rb +103 -0
  236. data/lib/rgame/rubocop/cop/game/hot_path.rb +36 -0
  237. data/lib/rgame/rubocop/cop/game/layer_boundary.rb +43 -0
  238. data/lib/rgame/rubocop/cop/game/no_core_in_engine_layer.rb +100 -0
  239. data/lib/rgame/rubocop/cop/game/no_engine_in_core_layer.rb +84 -0
  240. data/lib/rgame/rubocop/cop/game/no_interpolation_in_hot_path.rb +50 -0
  241. data/lib/rgame/rubocop/cop/game/no_literal_text.rb +41 -0
  242. data/lib/rgame/rubocop/cop/game/no_needless_allocation.rb +112 -0
  243. data/lib/rgame/rubocop/default.yml +39 -0
  244. data/lib/rgame/rubocop/plugin.rb +45 -0
  245. data/lib/rgame/rubocop.rb +11 -0
  246. data/lib/rgame/util/color.rb +20 -24
  247. data/lib/rgame/util/controls.rb +106 -44
  248. data/lib/rgame/util/route_search.rb +27 -0
  249. data/lib/rgame/util/save_file.rb +107 -0
  250. data/lib/rgame/util/solid_grid.rb +37 -0
  251. data/lib/rgame/util/tensor.rb +0 -9
  252. data/lib/rgame/util/tile_sweep.rb +36 -0
  253. data/lib/rgame/util/z.rb +123 -0
  254. data/lib/rgame/util.rb +5 -3
  255. data/lib/rgame/version.rb +1 -1
  256. data/lib/rgame.rb +0 -15
  257. metadata +176 -20
  258. data/lib/rgame/engine/actor.rb +0 -53
  259. data/lib/rgame/engine/body.rb +0 -49
  260. data/lib/rgame/engine/cached_label.rb +0 -33
  261. data/lib/rgame/engine/camera_view.rb +0 -28
  262. data/lib/rgame/engine/input/player_controller.rb +0 -14
  263. data/lib/rgame/engine/matrix.rb +0 -32
  264. data/lib/rgame/engine/resettable.rb +0 -67
  265. data/lib/rgame/engine/tile_collision.rb +0 -78
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d5bcc5fc00641f0ff886974c919bdebc2c9ae3260d4333740243a4ff118ed55
4
- data.tar.gz: 42d88501d55f42b2ecc2a037d080e74d2f1420bf46ad1aa965cc1cc069288d7e
3
+ metadata.gz: 54caa0772adc1eb3211edbd694540c196be0018d6482089447d727f5c871e19a
4
+ data.tar.gz: 16b3d1de8baefc2579bbf5a89fb7b42041d174567b6c7059d5c967f2598621f5
5
5
  SHA512:
6
- metadata.gz: d119e1dfe2b47bbc9444633a95cc8b3d4d4c4a1c2ae94329993e107df68f5f63421581c4bee023bf7ebaeca736410fbcca7c2366d8ba5fbdbc338421a11b545e
7
- data.tar.gz: de260cc2b6a2c8ebf01db793da132d11a1bf5fa973426e4b846b22cdab369f7674f5045b48231b0a823961c5a36a220d3a09db67a601fc60998a44878f856872
6
+ metadata.gz: ab441100a4d86c34d93efed615b350a7d65ad819d3b7929e868e709c6a9790b9c78a956b9860632e62750f0880ca9656a1f7f6c98cc3df6c1b19f7d229e40919
7
+ data.tar.gz: e98b7a96d21700bf0533f0155be99ffc9f480e35fda2375aaee4b8b2a7bb65b871d0a70a1f2486873b32e28a83aaa4abfc91a14991226153f57959b0d62237b4
data/CHANGELOG.md ADDED
@@ -0,0 +1,228 @@
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.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
+
148
+ ## [0.2.0] - 2026-08-26
149
+
150
+ ### Added
151
+
152
+ - **Split-screen.** A game has seats (`RGame::Game.new(players: 2)`), and a
153
+ `RGame::Engine::Player` owns a device, a binding table, a camera and a region
154
+ of the screen. The shared world is updated once and drawn once per viewport by
155
+ a `WorldView`. Which player a node answers to is inherited down the tree like
156
+ its transform, so `ship.input_owner = players[1]` moves a whole subtree.
157
+ New: `Player`, `Players`, `Viewports`, `View`, `WorldView`, `Layout`.
158
+ - **`RGame::Engine::InputMap`** — one binding table per player, written in terms
159
+ of physical ids from `RGame::Util::Controls`, so a game names keys and pad
160
+ buttons in one place and reads named actions everywhere else.
161
+ - **Draw bands** (`RGame::Util::Z`): `:world`, `:hud`, `:overlay` and `:debug`.
162
+ A band beats every `z` in the tree, so nothing in the world can draw over the
163
+ HUD. Inherited down the tree, and set by the nodes that exist to mark one.
164
+ - **Culling** (`RGame::Engine::Culling`, `view.visible?`), which stops being an
165
+ optimisation once the world is drawn once per player.
166
+ - **A bare-bones UI package**: `PlayerLayer` gives a player their own screen, and
167
+ `UI::Menu` / `UI::MenuItem` navigate it by focus and activation — enough for
168
+ keyboard and controller menus. Layout, nesting, scrolling and text entry are
169
+ not in it; see [docs/api/ui.md](docs/api/ui.md).
170
+ - **Pausing** (`Node2D#paused`), which stops `control` and `update` for a node
171
+ and its whole subtree while it keeps drawing — a frozen world under a cutscene
172
+ overlay that goes on animating.
173
+ - `Components::CameraFollow`, and `TileMapLayer` as a node of its own.
174
+ - **`tools/drive_example.rb`** — boots an example unmodified, feeds it a
175
+ scripted input backend and reports what the game actually asked for: draws,
176
+ clips, sounds, scenes, ticks against frames.
177
+ - **macOS and Windows support**, and CI that runs every verification tier on
178
+ all three platforms.
179
+ - This changelog, linked from the gem's RubyGems page through `changelog_uri`.
180
+
181
+ ### Changed
182
+
183
+ - `Node2D#draw` and `on_draw` take the viewport being drawn into:
184
+ `on_draw(renderer, view)`. Most nodes ignore it; laying out against the edges
185
+ of a player's region, and culling, need it.
186
+ - `z` orders a node among its **siblings** only. It is never added to anything
187
+ and never reaches the renderer, so a node's whole subtree draws before or
188
+ after a sibling's, never interleaved with it. This replaces the additive
189
+ `abs_z = parent.abs_z + z`, under which a node at z 2 with a child at z 5
190
+ resolved to 7 and overtook a sibling at 4.
191
+ - `F2` quits and `F1` toggles the debug overlay. `Esc` is deliberately left to
192
+ the game, because it is the button a player expects to back out of a menu.
193
+ - Reading an action no `InputMap` declares raises `KeyError` instead of reading
194
+ as "never pressed" forever.
195
+
196
+ ### Removed
197
+
198
+ - `RGame::Engine::CameraView`. A camera belongs to a `Player` and is applied by
199
+ the `View` being drawn.
200
+
201
+ ## [0.1.0] - 2026-08-20
202
+
203
+ First release, and the first version that runs a game end to end.
204
+
205
+ ### Added
206
+
207
+ - **The C engine**, as two Ruby extensions built from one source tree: an SDL2
208
+ window and a fixed-timestep main loop, keyboard and gamepad input with
209
+ hot-plug, a z-sorted batching renderer with transforms, clipping and baked
210
+ recordings, text from a shipped TrueType font, and audio (samples and
211
+ streamed Ogg Vorbis or WAV).
212
+ - **`RGame::Core`** — the half that owns the window, the GPU and the sound
213
+ device: `App`, `Input`, `Gamepad`, `Image`, `Renderer`, `Recording`, `Font`,
214
+ `Audio`, plus the asset layer (`AssetManager`, `SpriteSheet`, `NineSlice`,
215
+ `UIAtlas`, `TileMapRenderer`).
216
+ - **`RGame::Util`** — the graphics-free half, so values can be required with no
217
+ SDL and no OpenGL in the process: `Tensor`, `Color`, `Controls`.
218
+ - **`RGame::Engine`** — the scene graph a game is written in: nodes,
219
+ components, signals, sprites, tile maps, collision and pathfinding. Pure
220
+ Ruby, and unable to name `RGame::Core` at all, which is what lets game logic
221
+ and its specs run with no display.
222
+ - **`RGame::Game`** — the entry point that wires the two halves together.
223
+ - Examples: `14_asteroids`, `15_tiled_world`, `16_hello_world`.
224
+
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
227
+ [0.2.0]: https://github.com/psuessenb/rgame/compare/v0.1.0...v0.2.0
228
+ [0.1.0]: https://github.com/psuessenb/rgame/releases/tag/v0.1.0