📋 What You'll Build
In this tutorial, you'll create a tower stacking game where players must time their clicks to stack blocks precisely on top of each other. The blocks move horizontally, and players must stop them at just the right moment to create the tallest tower possible!
🚀 Getting Started
1Install Ruby2D
First, you need to install the Ruby2D gem:
gem install ruby2d
2Create Your Game File
Create a new file called tower_stack.rb and add the basic setup:
require 'ruby2d'
set title: "Tower Stacking Game"
set width: 400
set height: 600
set background: '#1a1a2e'
show
This creates a window with a dark blue background.
🎯 Core Concepts
Game State Variables
We need to track several pieces of information throughout the game:
@blocks = [] # Array to store all stacked blocks
@current_block = nil # The block currently moving
@direction = 1 # Movement direction (1 = right, -1 = left)
@speed = 2 # How fast blocks move
@score = 0 # Player's score
@game_over = false # Game state flag
@base_width = 100 # Width of the starting block
@block_height = 30 # Height of each block
Creating UI Elements
Add text elements to display game information:
@score_text = Text.new(
'Score: 0',
x: 10, y: 10,
size: 20,
color: 'white'
)
@instruction_text = Text.new(
'Press SPACE to stack',
x: 10, y: 40,
size: 16,
color: 'gray'
)
@game_over_text = Text.new(
'GAME OVER! Press R to restart',
x: 50, y: 250,
size: 16,
color: 'red',
opacity: 0 # Hidden initially
)
🏗️ Building the Game Logic
3Create the Base Block
Every tower needs a foundation. This method creates the first block at the bottom:
def create_base
base = Rectangle.new(
x: (Window.width - @base_width) / 2, # Center horizontally
y: Window.height - @block_height - 10, # Near bottom
width: @base_width,
height: @block_height,
color: '#16a085'
)
@blocks << {
rect: base,
x: base.x,
width: @base_width
}
end
We store both the Rectangle object and its properties for later calculations.
4Create Moving Blocks
This method creates a new block that moves horizontally:
def create_block
return if @game_over
last_block = @blocks.last
y_pos = last_block[:rect].y - @block_height # Stack on top
@current_block = {
rect: Rectangle.new(
x: 0,
y: y_pos,
width: last_block[:width], # Same width as previous
height: @block_height,
color: ['#e74c3c', '#3498db', '#f39c12',
'#9b59b6', '#e67e22'].sample # Random color
),
width: last_block[:width],
moving: true
}
end
Each new block starts at the same width as the previous one and gets a random color!
5The Stacking Mechanism
This is the heart of the game - calculating overlap and trimming blocks:
def stack_block
return if !@current_block || @game_over
last_block = @blocks.last
current_x = @current_block[:rect].x
last_x = last_block[:x]
# Calculate overlap region
overlap_start = [current_x, last_x].max
overlap_end = [current_x + @current_block[:width],
last_x + last_block[:width]].min
overlap = overlap_end - overlap_start
if overlap <= 0
# No overlap - game over!
@game_over = true
@game_over_text.opacity = 1
@current_block[:rect].color = '#c0392b'
return
end
# Trim block to overlap area
@current_block[:rect].x = overlap_start
@current_block[:rect].width = overlap
@current_block[:width] = overlap
@current_block[:x] = overlap_start
@current_block[:moving] = false
@blocks << @current_block
@current_block = nil
@score += 1
@score_text.text = "Score: #{@score}"
@speed += 0.1 # Increase difficulty
create_block
end
🎮 Game Loop and Controls
Movement Logic
The update block runs every frame and handles block movement:
update do
if @current_block && @current_block[:moving] && !@game_over
# Move block horizontally
@current_block[:rect].x += @speed * @direction
# Bounce off walls
if @current_block[:rect].x <= 0
@direction = 1 # Move right
elsif @current_block[:rect].x + @current_block[:width] >= Window.width
@direction = -1 # Move left
end
end
end
Input Handling
Respond to player keyboard input:
on :key_down do |event|
if event.key == 'space'
stack_block unless @game_over
elsif event.key == 'r'
reset_game if @game_over
end
end
6Reset Function
Allow players to restart after game over:
def reset_game
@blocks.each { |b| b[:rect].remove } # Remove all blocks
@blocks.clear
@current_block[:rect].remove if @current_block
@current_block = nil
@score = 0
@speed = 2
@game_over = false
@score_text.text = "Score: 0"
@game_over_text.opacity = 0
create_base
create_block
end
🎨 Customization Ideas
- Add sound effects when blocks are stacked
- Create power-ups that slow down block movement
- Add a high score system that persists between games
- Implement different block shapes for variety
- Add particle effects when blocks are stacked perfectly
- Create difficulty levels with different starting speeds
📚 Key Takeaways
- Game State Management: Use instance variables to track game data
- Collision Detection: Calculate overlapping regions mathematically
- Progressive Difficulty: Increase speed as score increases
- Visual Feedback: Use colors and text to communicate game state
- Clean Code Structure: Separate concerns into different methods
🎯 Next Steps
Now that you understand the basics, try:
- Adding more visual polish with gradients and shadows
- Implementing a leaderboard system
- Creating different game modes (timed, endless, etc.)
- Building a mobile version using different input methods
More Ruby tutorials:
