突然はなしが変わってRuby/Tkでアニメーションを作る話

今まで、マクロをRubyで実現する話を書いていたのですが、突然飽きて最近ActionScript方面でいろいろある物理シミュレーションをRubyで作ってみたくなったので色々やってみたいと思い立ちました。でも、物理は高校で止まっていて今日も図書館で物理の本を読んでみたのですが、ちんぷんかんぷんなので挫折する可能性大です。

とりあえず、ボールを斜め上に投げるよというものです。放物線を描いて地面に当たると弾みます。見るだけです。ボールが画面の外に出たらそれから先は何も起きません。
Ruby/Tkが必要ですが標準で入っているので、Tcl/Tkがインストールされていれば動くんじゃないかなと思います。でも、Ruby 1.9.0では動かなかったです。Ruby 1.8.6で試しています。

追記
プログラムを書き換えて一杯ボールが出るようにしました。しばらく待っているとボールが増えていきます。

require 'tk'
require 'singleton'

class Viewer<TkFrame
  include TkComposite

  WIDTH = 512
  HEIGHT = 512

  def initialize_composite
    @frame.configure(:width=>WIDTH, :height=>HEIGHT)
    @canvas = TkCanvas.new(@frame)
    @canvas.configure(:width=>WIDTH, :height=>HEIGHT)
    @canvas.grid('row'=>0, 'column'=>0, 'sticky'=>'news')
    @frame.grid_propagate(false)
  end

  def draw_oval(x, y, r)
    r2 = r / 2
    TkcOval.new(@canvas, x - r2, HEIGHT - y + r2, x + r2, HEIGHT - y - r2, 'fill' => :red)
  end
end

class Field
  include Singleton

  def initialize
    @view = Viewer.new
    @view.pack(:expand=>true, :fill=>:both)
  end

  def draw_oval(x, y, r)
    @view.draw_oval(x, y, r)
  end
end

class Ball
  @@field = Field.instance

  def initialize(r, x, y, vx, vy)
    @x = x
    @y = x
    @r = r
    @vx = vx
    @vy = vy
    @shape = @@field.draw_oval(@x, @y, @r)
  end

  def redraw
    r2 = @r / 2
    @shape.coords = [
      @x - r2, 
      Viewer::HEIGHT - @y + r2, 
      @x + r2, 
      Viewer::HEIGHT - @y - r2,
      ]
  end

  def compute_next_pos
    @x = @x + @vx
    @y = @y + @vy
    @vy = @vy - 0.1
    if @y < 10 then
      @y = 10
      @vy = (-@vy) * 0.97
    end
    if @x > 600 then
      return false
      @shape.destroy
    end
    redraw
    return true
  end
end

objs = []
objs.push Ball.new(10, 10, 10, 1, 10)
count = 0
objnum = 1
timer = TkAfter.start(10, -1, lambda {
                        i = objnum
                        count = count + 1
                        while i != 0 do
                          i = i - 1
                          if objs[i].compute_next_pos == false then
                            objs[i] = Ball.new(10, rand * 100, 10, 1, 10)
                          end
                        end

                        if count % 780 == 0 then
                          objs.push Ball.new(10, rand * 100, 10, 1, 10)
                          objnum = objnum + 1
                        end
                    })

Tk.mainloop