るびまに載ってたゲームの動きです。
Fiberクラスを使っているので、ruby1.9でしか動きません。


#!/usr/bin/env ruby

require 'sdl'
require 'cairo'


class Sprite
  attr_accessor :x, :y, :alive, :enabled, :idx, :info
  attr_writer :draw, :matrix

  def initialize
    @x, @y = 0, 0
    @alive = true
    @enabled = true
    @idx = 0
    @info = {}
    @matrix = Cairo::Matrix.identity
  end

  def renew(cr)
    return if !@enabled
    cr.matrix = @matrix * Cairo::Matrix.translate(@x, @y)
    @draw[cr]
  end
end

class Phase
  attr_accessor :handles, :task

  def initialize(w, h, info=nil)
    @image = Cairo::ImageSurface.new(w, h)
    @info = info
    @sprites = []
    @handles = {}
  end

  def addSprite
    @sprites << Sprite.new
    yield(@sprites.last)
    @sprites.last
  end

  def renew(drawFlag=true)
    @task[] if @task
    if drawFlag
      cr = Cairo::Context.new(@image)
      @sprites.reject!{|obj| !obj.alive}
      @sprites.each {|obj| obj.renew(cr)}
      @image
    else
      nil
    end
  end

  def layerSort
    @sprites.sort!{|a, b| a.idx <=> b.idx}
  end
end

SDL.init(SDL::INIT_VIDEO)
SDL::WM.set_caption("Game","")
w, h = 640, 480
screen = SDL::Screen.open(w, h, 32, 0)
info = {:symbol => :title, :hi_score => 0}
phase = {}
[:title, :play, :over].each do |sym|
  phase[sym] = Phase.new(w, h, info)
end
# title phase
phase[:title].addSprite do |obj|
  obj.draw = proc do |cr|
    cr.set_source_color(Cairo::Color::GRAY)
    cr.rectangle(0, 0, w, h)
    cr.fill
  end
end
font_size = 25
phase[:title].addSprite do |obj|
  obj.x, obj.y = 100,50
  obj.draw = proc do |cr|
    cr.set_source_color(Cairo::Color::BLACK)
    cr.font_size = font_size
    cr.move_to(0, font_size)
    cr.show_text("Hi-Score : %d" % info[:hi_score])
  end
end
yy = 100
["Game Start : Return Key", "Game EXit : Escape Key"].each do |s|
  phase[:title].addSprite do |obj|
    obj.x, obj.y = 100,yy
    obj.draw = proc do |cr|
      cr.set_source_color(Cairo::Color::BLACK)
      cr.font_size = font_size
      cr.move_to(0, font_size)
      cr.show_text(s)
    end
  end
  yy += 50
end
phase[:title].handles[SDL::Event::Quit] = proc{exit}
phase[:title].handles[SDL::Event::KeyDown] = proc do|e|
  case e.sym
  when SDL::Key::RETURN
    info[:symbol] = :play
  when SDL::Key::ESCAPE
    exit
  end
end
# play phase
active = false
score = 0
dx = 0
phase[:play].addSprite do |obj|
  obj.draw = proc do |cr|
    cr.set_source_color(Cairo::Color::AQUA)
    cr.rectangle(0, 0, w, 400)
    cr.fill
    cr.set_source_color(Cairo::Color::DARK_GREEN)
    cr.rectangle(0, 400, w, h)
    cr.fill
  end
end
phase[:play].addSprite do |obj|
  obj.x, obj.y = 50, 50
  obj.draw = proc do |cr|
    cr.set_source_color(Cairo::Color::BLACK)
    cr.font_size = font_size
    cr.move_to(0, font_size)
    cr.show_text("SCORE : %d" % score)
  end
end
player = phase[:play].addSprite do |obj|
  obj.x, obj.y = w / 2, 385
  sw, sh = 30, 30
  obj.draw = proc do |cr|
    cr.set_source_color(Cairo::Color::ORANGE)
    cr.rectangle(-sw / 2, -sh / 2, sw, sh)
    cr.fill
  end
end
collide_lengh = {:bomb => 43, :apple => 56}
items = []
phase[:play].task = proc do
  if !active
    score = 0
    player.x, dx = w / 2, 0
    active = true
  end
  player.x += dx * 5
  items.each{|obj| obj.y += obj.info[:dy]}
  point = Cairo::Point.new(player.x, player.y)
  items.each do |obj|
    if point.distance(Cairo::Point.new(obj.x, obj.y))  < collide_lengh[obj.info[:kind]]
      case obj.info[:kind]
      when :bomb
        info[:hi_score] = score if info[:hi_score] < score
        items.each{|obj| obj.alive = false}
        active = false
        info[:symbol] = :over
      when :apple
        score += 10
        obj.alive = false
      end
    end
  end
  items.each{|obj| obj.alive = false if obj.y > h}
  items.reject!{|obj| !obj.alive}
  while items.size < 5
    items << phase[:play].addSprite do |obj|
      obj.x = rand(w)
      obj.info[:dy] = rand(9) + 4
      if rand(100) < 60
        obj.info[:kind] = :bomb
        obj.draw = proc do |cr|
          cr.set_source_color(Cairo::Color::BLACK)
          cr.circle(0, 0, 20)
          cr.fill
        end
      else
        obj.info[:kind] = :apple
        obj.draw = proc do |cr|
          cr.set_source_color(Cairo::Color::RED)
          cr.circle(0, 0, 22)
          cr.fill
        end
      end
    end
  end
end
phase[:play].handles[SDL::Event::KeyDown] = proc do |e|
  case e.sym
  when SDL::Key::LEFT
    dx -= 1
  when SDL::Key::RIGHT
    dx += 1
  end
end
phase[:play].handles[SDL::Event::KeyUp] = proc do |e|
  case e.sym
  when SDL::Key::LEFT
    dx += 1
  when SDL::Key::RIGHT
    dx -= 1
  end
end
# over phase
phase[:over].addSprite do |obj|
  obj.draw = proc do |cr|
    cr.set_source_color(Cairo::Color::BLACK)
    cr.rectangle(0, 0, w, h)
    cr.fill
  end
end
phase[:over].addSprite do |obj|
  obj.x, obj.y = 100,100
  obj.draw = proc do |cr|
    cr.set_source_color(Cairo::Color::WHITE)
    cr.font_size = font_size
    cr.move_to(0, font_size)
    cr.show_text("Game Over")
  end
end
wait_fiber = Fiber.new do
  loop do
    50.times {Fiber.yield}
    info[:symbol] = :title
    Fiber.yield
  end
end
phase[:over].task = proc do
  wait_fiber.resume
end
loop do
  while e = SDL::Event.poll
    if handle = phase[info[:symbol]].handles[e.class]
      handle[e]
    end
  end
  image = phase[info[:symbol]].renew
  surface = SDL::Surface.new_from(image.data, w, h, 32, image.stride, 0, 0, 0, 0)
  screen.put(surface,0,0)
  screen.flip
  SDL.delay(20)
end