🎮 Tower Stacking Game Tutorial in Ruby2D

Recording of the actual game:

📋 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
💡 Note: Instance variables (prefixed with @) persist throughout the game's lifetime, allowing us to track state across different methods.

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
🧮 How Overlap Works: We find where both blocks exist on the X-axis. The overlapping region becomes the new block. If blocks don't overlap at all, it's game over!

🎮 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

📚 Key Takeaways

  1. Game State Management: Use instance variables to track game data
  2. Collision Detection: Calculate overlapping regions mathematically
  3. Progressive Difficulty: Increase speed as score increases
  4. Visual Feedback: Use colors and text to communicate game state
  5. Clean Code Structure: Separate concerns into different methods

🎯 Next Steps

Now that you understand the basics, try:

More Ruby tutorials:

Bubble sort visualization

Animated plasma effect