Skip to content

Commit 904644e

Browse files
committed
Add basic readme
1 parent 4765847 commit 904644e

1 file changed

Lines changed: 248 additions & 17 deletions

File tree

README.md

Lines changed: 248 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,271 @@
11
# RichEngine
22

3-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rich_engine`. To experiment with that code, run `bin/console` for an interactive prompt.
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.
44

5-
TODO: Delete this and the text above, and describe your gem
5+
At its core, you subclass `RichEngine::Game`, implement a few lifecycle hooks, and draw to a `Canvas` each frame.
66

7-
## Installation
7+
## Quick start: build a simple game
88

9-
Add this line to your application's Gemfile:
9+
Below is a minimal, complete example showing how to:
10+
- create a game by subclassing `RichEngine::Game`
11+
- quit on a key press
12+
- sleep to cap the frame rate
13+
- draw text and shapes to the screen
1014

1115
```ruby
12-
gem 'rich_engine'
16+
require "rich_engine"
17+
18+
class MyGame < RichEngine::Game
19+
using RichEngine::StringColors
20+
21+
TITLE = "Catch the Star"
22+
23+
PLAYER_CHAR = "@"
24+
PLAYER_COLOR = :yellow
25+
26+
ITEM_COLORS = [:green, :magenta, :cyan]
27+
ITEM_CHAR = "*"
28+
29+
HUD_HEIGHT = 3
30+
31+
def on_create
32+
@score = 0
33+
@player_x = 2
34+
@player_y = field_height / 2
35+
@timer = RichEngine::Cooldown.new(5.0)
36+
spawn_item
37+
end
38+
39+
# elapsed_time: seconds since last frame (Float)
40+
# key: last key pressed (Symbol) or nil
41+
def on_update(elapsed_time, key)
42+
quit! if key == :q || key == :esc
43+
44+
# Move player with arrow keys
45+
case key
46+
when :left then @player_x -= 1
47+
when :right then @player_x += 1
48+
when :up then @player_y -= 1
49+
when :down then @player_y += 1
50+
end
51+
52+
# Keep player inside the game field (above the HUD)
53+
@player_x = @player_x.clamp(0, @width - 1)
54+
@player_y = @player_y.clamp(0, field_height - 1)
55+
56+
# Game over if time runs out
57+
@timer.update(elapsed_time)
58+
if @timer.finished?
59+
@game_over = true
60+
quit!
61+
end
62+
63+
# Pick up item -> +1 point, respawn, reset timer
64+
if @player_x == @item_x && @player_y == @item_y
65+
@score += 1
66+
spawn_item
67+
@timer.reset!
68+
end
69+
70+
# drawing to the canvas
71+
@canvas.clear
72+
@canvas.bg = RichEngine::UI::Textures.solid.bright_white
73+
74+
# Item and player
75+
@canvas.write_string(ITEM_CHAR, x: @item_x, y: @item_y, fg: @item_color, bg: :gray)
76+
@canvas.write_string(PLAYER_CHAR, x: @player_x, y: @player_y, fg: PLAYER_COLOR, bg: :gray)
77+
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)
82+
83+
# sleep to cap FPS
84+
sleep 1 / 60.0
85+
end
86+
87+
def on_destroy
88+
puts(@game_over ? "Game over! Final score: #{@score}" : "Thanks for playing! Score: #{@score}")
89+
end
90+
91+
private
92+
93+
def hud_y
94+
@height - HUD_HEIGHT
95+
end
96+
97+
def field_height
98+
@height - HUD_HEIGHT
99+
end
100+
101+
def spawn_item
102+
# Spawn above the HUD so it won't overlap with on-screen text
103+
@item_x = rand(@width)
104+
@item_y = rand(field_height)
105+
@item_color = ITEM_COLORS.sample
106+
end
107+
end
108+
109+
MyGame.play(width: 50, height: 12)
110+
```
111+
112+
Notes
113+
- Hooks:
114+
- `on_create` runs once at start
115+
- `on_update(elapsed_time, key)` runs every frame
116+
- `on_destroy` runs when the game exits.
117+
- Keys: letters are symbols (e.g., `:q`), plus arrows (`:up`, `:down`, `:left`, `:right`), `:space`, `:enter`, `:esc`, `:pg_up`, `:pg_down`, `:home`, `:end`.
118+
- Drawing: all drawing happens on `@canvas`. Call `@canvas.clear` each frame if you want to redraw from scratch.
119+
- Rendering is handled for you. `Game` will flush the canvas to the terminal after each `on_update`.
120+
121+
## Canvas essentials
122+
123+
`@canvas` exposes a few handy methods:
124+
- `write_string(str, x:, y:, fg: :white, bg: :transparent)` — write colored text; pass a single color or an array (arrays will cycle per character)
125+
- `draw_rect(x:, y:, width:, height:, char: "█", color: :white)` — draw a filled rectangle
126+
- `draw_circle(x:, y:, radius:, char: "█", color: :white)` — draw a filled circle
127+
- `draw_sprite(sprite, x: 0, y: 0, fg: :white)` — draw a multi-line string as a sprite; spaces are transparent
128+
- `clear` — clear the entire canvas; `bg=` changes the background fill character and clears
129+
130+
Colors are provided via a refinement used internally by the canvas. For text, prefer the `fg:` and `bg:` options on `write_string`.
131+
132+
## Helpers you can use
133+
134+
All helpers live under `RichEngine::...` and are independent utilities you can use inside your game code.
135+
136+
### Timer
137+
138+
- `Timer` accumulates elapsed time; you drive it by calling `update(dt)` with the `elapsed_time` from `on_update`.
139+
- `Timer.every(seconds:)` returns a small scheduler that fires a block at a fixed interval.
140+
141+
```ruby
142+
tick = RichEngine::Timer.new
143+
144+
def on_update(dt, _key)
145+
tick.update(dt)
146+
if tick.get > 2
147+
# do something every ~2 seconds
148+
tick.reset!
149+
end
150+
end
151+
152+
# Fixed interval
153+
spawn = RichEngine::Timer.every(seconds: 0.5)
154+
def on_update(dt, _key)
155+
spawn.update(dt)
156+
spawn.when_ready { spawn_enemy! }
157+
end
13158
```
14159

15-
And then execute:
160+
### Cooldown
16161

17-
$ bundle install
162+
Track a fixed delay and check if it’s ready.
18163

19-
Or install it yourself as:
164+
```ruby
165+
shoot_cd = RichEngine::Cooldown.new(0.25) # seconds
166+
167+
def on_update(dt, key)
168+
shoot_cd.update(dt)
169+
if key == :space && shoot_cd.ready?
170+
shoot!
171+
shoot_cd.reset!
172+
end
173+
end
174+
```
175+
176+
### Chance (random helpers)
177+
178+
```ruby
179+
RichEngine::Chance.of(0.2) # 20% chance
180+
RichEngine::Chance.of(20) # also 20% (percent form)
181+
RichEngine::Chance.of_one_in(10) # 1 in 10 chance
182+
```
183+
184+
### Enum and Enum::Mixin
185+
186+
Create ergonomic, comparable enums with query methods.
187+
188+
```ruby
189+
# Standalone enum
190+
STATE = RichEngine::Enum.new(:state, {idle: 0, running: 1, paused: 2})
191+
STATE.idle.value #=> 0
192+
STATE.running > STATE.idle #=> true
193+
194+
# In a class via Mixin
195+
class Player
196+
include RichEngine::Enum::Mixin
197+
enum :state, {idle: 0, running: 1, paused: 2}
198+
199+
def initialize
200+
@state = :idle
201+
end
202+
203+
def update
204+
if state.running?
205+
# ...
206+
end
207+
end
208+
end
209+
```
210+
211+
### Matrix (2D grid)
212+
213+
A simple 2D matrix utility with convenience methods.
20214

21-
$ gem install rich_engine
215+
```ruby
216+
grid = RichEngine::Matrix.new(width: 10, height: 5, fill_with: 0)
217+
grid[2, 3] = 1
218+
219+
grid.each { |cell| puts cell }
22220

23-
## Usage
221+
# Fill regions
222+
grid.fill(x: 0..2, y: 0..1, with: 9)
24223

25-
TODO: Write usage instructions here
224+
# Zip two matrices into pairs
225+
other = RichEngine::Matrix.new(width: 10, height: 5, fill_with: :a)
226+
pairs = grid.zip(other) # => matrix of [left, right]
227+
```
26228

27-
## Development
229+
### UI::Textures (useful glyphs)
28230

29-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
231+
Convenience characters for shading and blocks: `empty`, `solid`, `light_shade`, `medium_shade`, `dark_shade`, `top_half`, `bottom_half`, `left_half`, `right_half`, `plaid`.
30232

31-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
233+
```ruby
234+
@canvas.draw_rect(x: 10, y: 6, width: 8, height: 2, char: RichEngine::UI::Textures.solid, color: :magenta)
235+
```
32236

33-
## Contributing
237+
## Examples
34238

35-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rich_engine.
239+
See the `examples/` folder for more complete samples:
240+
- `timer.rb` — using timers and intervals
241+
- `noise.rb` — colorful random output
242+
- `background.rb` — background fill and drawing
243+
- `grains_of_sand.rb` — simple cellular-like simulation
36244

245+
## Install and run locally (optional)
246+
247+
Add to a Gemfile, then bundle:
248+
249+
```ruby
250+
gem "rich_engine"
251+
```
252+
253+
```sh
254+
bundle install
255+
```
256+
257+
Or install directly:
258+
259+
```sh
260+
gem install rich_engine
261+
```
262+
263+
Then run one of the examples:
264+
265+
```sh
266+
ruby examples/timer.rb
267+
```
37268

38269
## License
39270

40-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
271+
MIT

0 commit comments

Comments
 (0)