Skip to content

Commit 6e58a13

Browse files
committed
Update readme
1 parent 4df2bd0 commit 6e58a13

1 file changed

Lines changed: 54 additions & 43 deletions

File tree

README.md

Lines changed: 54 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
# RichEngine
22

3-
RichEngine is a tiny terminal game engine for Ruby. It gives you a simple game loop, a 2D character canvas with colors, non-blocking keyboard input, and a handful of helpers (timers, cooldowns, RNG, enums, matrices) so you can ship playful ASCII games quickly.
3+
RichEngine is a tiny terminal game engine for Ruby. It gives you a simple game
4+
loop, a 2D character canvas with colors, non-blocking keyboard input, and a
5+
handful of helpers (timers, cooldowns, RNG, enums, matrices) so you can ship
6+
playful ASCII games quickly.
47

5-
At its core, you subclass `RichEngine::Game`, implement a few lifecycle hooks, and draw to a `Canvas` each frame.
8+
At its core, you subclass `RichEngine::Game`, implement a few lifecycle hooks,
9+
and draw to a `Canvas` each frame.
610

711
## Quick start: build a simple game
812

913
Below is a minimal, complete example showing how to:
1014
- create a game by subclassing `RichEngine::Game`
1115
- quit on a key press
12-
- sleep to cap the frame rate
1316
- draw text and shapes to the screen
17+
- use canvas slots to keep a bottom HUD separate from the playfield
1418

1519
```ruby
1620
require "rich_engine"
@@ -19,20 +23,19 @@ class MyGame < RichEngine::Game
1923
using RichEngine::StringColors
2024

2125
TITLE = "Catch the Star"
22-
2326
PLAYER_CHAR = "@"
2427
PLAYER_COLOR = :yellow
25-
2628
ITEM_COLORS = [:green, :magenta, :cyan]
2729
ITEM_CHAR = "*"
28-
2930
HUD_HEIGHT = 3
3031

3132
def on_create
3233
@score = 0
3334
@player_x = 2
3435
@player_y = field_height / 2
3536
@timer = RichEngine::Cooldown.new(5.0)
37+
@field = @canvas.slot(x: 0, y: 0, width: @width, height: field_height, bg: RichEngine::UI::Textures.solid.bright_white)
38+
@hud = @canvas.slot(x: 0, y: field_height, width: @width, height: HUD_HEIGHT)
3639
spawn_item
3740
end
3841

@@ -60,25 +63,22 @@ class MyGame < RichEngine::Game
6063
quit!
6164
end
6265

63-
# Pick up item -> +1 point, respawn, reset timer
66+
# Pick up item
6467
if @player_x == @item_x && @player_y == @item_y
6568
@score += 1
6669
spawn_item
6770
@timer.reset!
6871
end
6972

70-
# drawing to the canvas
73+
# rendering the frame
7174
@canvas.clear
72-
@canvas.bg = RichEngine::UI::Textures.solid.bright_white
7375

74-
# Item and player
75-
@canvas.write_string(ITEM_CHAR, x: @item_x, y: @item_y, fg: @item_color, bg: :bright_white)
76-
@canvas.write_string(PLAYER_CHAR, x: @player_x, y: @player_y, fg: PLAYER_COLOR, bg: :bright_white)
76+
@field.write_string(ITEM_CHAR, x: @item_x, y: @item_y, fg: @item_color)
77+
@field.write_string(PLAYER_CHAR, x: @player_x, y: @player_y, fg: PLAYER_COLOR)
7778

78-
# HUD at the bottom rows
79-
@canvas.write_string(TITLE.ljust(@width), x: 0, y: hud_y, fg: :bright_cyan)
80-
@canvas.write_string("Score: #{@score}".ljust(@width), x: 0, y: hud_y + 1, fg: :bright_yellow)
81-
@canvas.write_string("Time: #{format('%.1f', @timer.get)}s".ljust(@width), x: 0, y: hud_y + 2, fg: :bright_green)
79+
@hud.write_string(TITLE, x: 0, y: 0, fg: :bright_cyan)
80+
@hud.write_string("Score: #{@score}", x: 0, y: 1, fg: :bright_yellow)
81+
@hud.write_string("Time: #{format('%.1f', @timer.get)}s", x: 0, y: 2, fg: :bright_green)
8282
end
8383

8484
def on_destroy
@@ -87,16 +87,11 @@ class MyGame < RichEngine::Game
8787

8888
private
8989

90-
def hud_y
91-
@height - HUD_HEIGHT
92-
end
93-
9490
def field_height
9591
@height - HUD_HEIGHT
9692
end
9793

9894
def spawn_item
99-
# Spawn above the HUD so it won't overlap with on-screen text
10095
@item_x = rand(@width)
10196
@item_y = rand(field_height)
10297
@item_color = ITEM_COLORS.sample
@@ -113,7 +108,9 @@ Notes
113108
- `on_destroy` runs when the game exits.
114109
- Keys: letters are symbols (e.g., `:q`), plus arrows (`:up`, `:down`, `:left`, `:right`), `:space`, `:enter`, `:esc`, `:pg_up`, `:pg_down`, `:home`, `:end`.
115110
- Drawing: all drawing happens on `@canvas`. Call `@canvas.clear` each frame if you want to redraw from scratch.
116-
- Rendering is handled for you. `Game` will flush the canvas to the terminal after each `on_update`.
111+
- Rendering and frame pacing are handled for you:
112+
- `Game` flushes the canvas after each frame
113+
- `Game` auto-sleeps to hit your target FPS (60 by default, but configurable via `target_fps:` on `Game.play`)
117114

118115
## Canvas essentials
119116

@@ -124,6 +121,20 @@ Notes
124121
- `draw_sprite(sprite, x: 0, y: 0, fg: :white)` — draw a multi-line string as a sprite; spaces are transparent
125122
- `clear` — clear the entire canvas; `bg=` changes the background fill character and clears
126123

124+
### Canvas slots (sub-canvases)
125+
126+
Slots are sub-regions of a canvas that translate local coordinates and clip drawing automatically. Great for HUDs and side panels.
127+
128+
```ruby
129+
canvas = RichEngine::Canvas.new(100, 40)
130+
hud = canvas.slot(x: 0, y: 35, width: 100, height: 5, bg: " ")
131+
hud.clear
132+
hud.write_string("Score: 10", x: 2, y: 1, fg: :bright_yellow)
133+
134+
log = canvas.slot(x: 80, y: 0, width: 20, height: 35)
135+
log.write_string("Hello", x: 1, y: 1) # writes to (81, 1) on the parent canvas
136+
```
137+
127138
Colors are provided via a refinement used internally by the canvas. For text, prefer the `fg:` and `bg:` options on `write_string`.
128139

129140
## Helpers you can use
@@ -139,18 +150,18 @@ All helpers live under `RichEngine::...` and are independent utilities you can u
139150
tick = RichEngine::Timer.new
140151

141152
def on_update(dt, _key)
142-
tick.update(dt)
143-
if tick.get > 2
144-
# do something every ~2 seconds
145-
tick.reset!
146-
end
153+
tick.update(dt)
154+
if tick.get > 2
155+
# do something every ~2 seconds
156+
tick.reset!
157+
end
147158
end
148159

149160
# Fixed interval
150161
spawn = RichEngine::Timer.every(seconds: 0.5)
151162
def on_update(dt, _key)
152-
spawn.update(dt)
153-
spawn.when_ready { spawn_enemy! }
163+
spawn.update(dt)
164+
spawn.when_ready { spawn_enemy! }
154165
end
155166
```
156167

@@ -162,11 +173,11 @@ Track a fixed delay and check if it’s ready.
162173
shoot_cd = RichEngine::Cooldown.new(0.25) # seconds
163174

164175
def on_update(dt, key)
165-
shoot_cd.update(dt)
166-
if key == :space && shoot_cd.ready?
167-
shoot!
168-
shoot_cd.reset!
169-
end
176+
shoot_cd.update(dt)
177+
if key == :space && shoot_cd.ready?
178+
shoot!
179+
shoot_cd.reset!
180+
end
170181
end
171182
```
172183

@@ -190,18 +201,18 @@ STATE.running > STATE.idle #=> true
190201

191202
# In a class via Mixin
192203
class Player
193-
include RichEngine::Enum::Mixin
194-
enum :state, {idle: 0, running: 1, paused: 2}
204+
include RichEngine::Enum::Mixin
205+
enum :state, {idle: 0, running: 1, paused: 2}
195206

196-
def initialize
197-
@state = :idle
198-
end
207+
def initialize
208+
@state = :idle
209+
end
199210

200-
def update
201-
if state.running?
202-
# ...
203-
end
211+
def update
212+
if state.running?
213+
# ...
204214
end
215+
end
205216
end
206217
```
207218

0 commit comments

Comments
 (0)