Skip to main content
This example demonstrates how to build a complete breakout-style game using a modular, multi-file architecture. You’ll learn about project organization, texture loading, collision detection, and game state management in a real-world game project.

Overview

The breakout game features:
  • Modular file structure with separate components
  • Texture loading for sprites
  • Paddle and ball physics
  • Brick collision and destruction
  • Score system with multipliers
  • Camera shake effects
  • Game states (Start, Playing, Won, Over)

Project Structure

breakout/
├── main.wren          # Entry point and render texture
├── game.wren          # Main game logic and state management
├── ball.wren          # Ball entity and physics
├── paddle.wren        # Paddle entity and controls
├── brick.wren         # Brick entity and grid generation
├── particle.wren      # Particle system (for effects)
├── score.wren         # Score indicator animations
├── utils.wren         # Shared constants
└── res/
    ├── tennis.png     # Ball sprite
    ├── paddle.png     # Paddle sprite
    └── brick.png      # Brick sprite

Code Files

import "raylib" for Color, Raylib, Rectangle, Vector2, Camera2D, KeyCode, Texture2D
import "math" for Math
import "./game" for Game

var width = 600
var height = 800
var title = "Sample"

Raylib.initWindow(width, height, title)

var min = Fn.new { |a, b| 
  if (a < b) return a
  return b
}

var game = Game.new()

Raylib.setTargetFPS(60)

var target = Raylib.loadRenderTexture(width, height)

while (!Raylib.windowShouldClose()) {
  game.update()

  var width_scale = Raylib.getScreenWidth() / width
  var height_scale = Raylib.getScreenHeight() / height
  var scale = min.call(width_scale, height_scale)

  // Render to texture for scaling
  Raylib.beginTextureMode(target)
  game.draw()
  Raylib.endTextureMode(target)

  Raylib.beginDrawing()
  Raylib.clearBackground(Color.new(20, 20, 20, 255))

  // Draw the render texture to screen
  Raylib.drawTexturePro(
    target.texture,
    Rectangle.new(0.0, 0.0, target.texture.width, target.texture.height),
    Rectangle.new(0.0, 0.0, target.texture.width, target.texture.height),
    Vector2.new(0.0, 0.0),
    0.0,
    Color.new(255, 255, 255, 255)
  )

  Raylib.endDrawing()
}

Raylib.clearBackground(Color.Gray)
Raylib.unloadRenderTexture(target)
Raylib.closeWindow()
Key Features:
  • Uses a render texture for resolution-independent rendering
  • Calculates scaling to maintain aspect ratio
  • Separates game logic (Game class) from rendering setup

Key Concepts

1. Modular Architecture

The game is split into separate files, each responsible for a specific component:
  • Separation of concerns: Each file handles one aspect of the game
  • Reusability: Components can be easily reused or modified
  • Maintainability: Changes to one component don’t affect others

2. Render Textures

var target = Raylib.loadRenderTexture(width, height)
Raylib.beginTextureMode(target)
game.draw()
Raylib.endTextureMode(target)
Render textures allow you to draw to an offscreen buffer, enabling:
  • Resolution-independent rendering
  • Post-processing effects
  • Scaling without quality loss

3. Delta Time Physics

var dt = Raylib.getFrameTime()
_ball.rec.x = _ball.rec.x + _ball.vel.x * dt
Multiplying movement by delta time ensures consistent speed regardless of frame rate.

4. Collision Detection

apply_collission(rec) {
  var prev_x = _rec.x - _vel.x
  var prev_y = _rec.y - _vel.y

  // Determine which side was hit
  if (prev_y + _rec.height <= rec.y) {
    _vel.y = _vel.y * -1.0
  }
}
Using previous position to determine collision direction ensures accurate bounce behavior.

5. Score Multiplier System

_score = _score + 1 * _score_multiplier 
_score_multiplier = _score_multiplier + 1

// Reset on paddle hit
if (Raylib.checkCollisionRecs(_ball.rec, _paddle.rec)) {
  _score_multiplier = 1
}
Encourages keeping the ball in play without hitting the paddle by increasing score for consecutive brick hits.

Game Mechanics

  • Controls: Left/Right arrow keys to move paddle
  • Objective: Destroy all bricks without letting the ball fall
  • Scoring: Each brick destroyed adds points multiplied by the current combo
  • Combo System: Combo increases with each brick hit, resets when ball hits paddle
  • Win Condition: All bricks destroyed
  • Lose Condition: Ball falls below the paddle

Enhancements to Try

  1. Power-ups: Add special bricks that drop power-ups (wider paddle, slower ball)
  2. Multiple balls: Split the ball when hitting special bricks
  3. Brick durability: Some bricks require multiple hits
  4. Particle effects: Add visual effects when bricks break
  5. Level progression: Load different brick patterns
  6. Sound effects: Add audio for collisions and events
  7. High score persistence: Save and load high scores

Build docs developers (and LLMs) love