Getting Started with Ruby and Ruby2D
Ruby2D exposes a compact game runtime: mutable visual objects, event handlers, and an update loop that turns state into motion.
Ruby2D provides a window, drawing primitives, audio, input events, and a render loop while leaving application state in ordinary Ruby variables and objects. A game can therefore be read as a sequence of state changes rather than as a hidden engine configuration.
Ruby Values as Game State
Ruby variables hold references to values. Numbers represent coordinates, speed, health, timers, and scores. Arrays represent ordered collections such as a Snake body or a list of enemies. Hashes map named keys to values and work well for configuration, input mappings, and entity properties.
player_x = 120
velocity = { x: 180, y: 0 }
inventory = ['key', 'potion']
player_x can be reassigned as the player moves. velocity[:x] reads the horizontal value from the hash. Ruby’s dynamic objects make small experiments quick, but the state still needs clear ownership: player state belongs together, level data belongs together, and transient input should not be confused with persistent world state.
Window and Visual Objects
require 'ruby2d' loads the Ruby2D DSL. set configures the window, and show enters the runtime loop. Square.new, Rectangle.new, Circle.new, Image.new, and Text.new create objects already attached to that window.
require 'ruby2d'
set title: 'Ruby2D', width: 800, height: 450, background: '#101418'
player = Square.new(x: 100, y: 100, size: 32, color: 'lime')
show
player is a reference to one square, not a static drawing command. Its properties can change during execution: player.x += 4 moves the same object rightward. Creating another square every frame produces a growing collection of objects; mutating the existing object represents motion.
Events and Intent
Ruby2D dispatches keyboard events through on :key_down, on :key_held, and on :key_up. The event object contains the key that changed. Input handlers should update intent; the simulation applies that intent during its update pass.
move_x = 0
on :key_down do |event|
move_x = -1 if event.key == 'left'
move_x = 1 if event.key == 'right'
end
This separates “the right key was pressed” from “the player moved.” The distinction matters once movement depends on acceleration, collision, stamina, menus, or paused state.
The Update Loop
update do ... end registers a block Ruby2D calls repeatedly, close to the display refresh rate. The block changes state; the runtime renders the current object attributes afterward.
speed = 4
update do
player.x += move_x * speed
end
This is the core game loop in reduced form. Input produces move_x, the update block changes player.x, and Ruby2D draws the square at its new coordinate. Larger games preserve this structure while storing state in classes or hashes and separating simulation from rendering.
Time and Object Lifetime
Frame-based movement is convenient for a fixed experiment but ties speed to frame rate. Time-based movement multiplies velocity by elapsed time, following position += velocity × dt. Ruby2D exposes the number of rendered frames and current fps; a game can also measure elapsed time directly with Time.now when it needs a slower fixed tick, as Snake does.
Object lifetime is equally important. A player exists for the entire level. A bullet exists until it hits something or leaves the world. A particle exists for a short timer. The update loop should iterate through active objects, update their state, and remove objects whose lifetime has ended. That pattern scales from a single square to a game world of entities.