弾幕を動かしたら、生き物みたいになった。
プログラムの定数を変えると、雰囲気が変わって楽しいです。
Fiberクラスを使っている為、rubyヴァージョン1.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("Moving Barrage","")
w, h = 500, 500
screen = SDL::Screen.open(w, h, 32, 0)
phase = Phase.new(w, h)
phase.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
cores = []
r = 6
2.times do
  cores << phase.addSprite do |obj|
    obj.x, obj.y = w / 2, h / 2
    obj.idx = 10
    obj.info[:rx] = Range.new(r, w - r)
    obj.info[:ry] = Range.new(r, h - r)
    obj.info[:dx] = 0
    obj.info[:dy] = 0
    obj.draw = proc do |cr|
      cr.set_source_color(Cairo::Color::RED)
      cr.circle(0, 0, r)
      cr.fill
    end
  end
end
move_fiber = Fiber.new do
  loop do
    20.times {Fiber.yield}
    cores.each do |obj|
      obj.info[:dx] = [0, 1, -1][rand(30) % 3]
      obj.info[:dy] = [0, 1, -1][rand(30) % 3]
    end
    Fiber.yield
  end
end
vector = []
(0..359).step(360 / 60) do |deg|
  rad= deg * Math::PI / 180
  vector << [Math::cos(rad), Math::sin(rad)]
end
items = []
phase.task = proc do
  move_fiber.resume
  cores.each do |obj|
    obj.x += obj.info[:dx] * 5 if obj.info[:rx].include?(obj.x + obj.info[:dx] * 5)
    obj.y += obj.info[:dy] * 5 if obj.info[:ry].include?(obj.y + obj.info[:dy] * 5)
  end
  items.reject!{|obj| !obj.alive}
  items.each do |obj|
    obj.info[:br] += 10
    obj.alive = false if obj.info[:br] > 200
  end
  color = Cairo::Color::RGB.new(rand / 2 + 0.5, rand / 2 + 0.5, rand / 2 + 0.5)
  cores.each do |core|
    items << phase.addSprite do |obj|
      obj.x, obj.y = core.x, core.y
      obj.idx = 1
      obj.info[:br] = 8
      obj.draw = proc do |cr|
        cr.set_source_color(color)
        vector.each do |x,y|
          px,py = obj.info[:br] * x, obj.info[:br] * y
          cr.circle(px, py, 2)
          cr.fill
        end
      end
    end
  end
  phase.layerSort
end
phase.handles[SDL::Event::Quit] = proc{exit}
phase.handles[SDL::Event::KeyDown] = proc{|e| exit if e.sym == SDL::Key::ESCAPE}
loop do
  while e = SDL::Event.poll
    if handle = phase.handles[e.class]
      handle[e]
    end
  end
  image = phase.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