読者です 読者をやめる 読者になる 読者になる

Green Shoes で動くものを作る

Ruby Green Shoes

クリックして生成された円が動き回ります。ぶつかると完全弾性衝突をします。端に到達すると跳ね返ります。

ここJavaScript で書かれたプログラムとほぼ同じように動作します。JavaScript 版の方はこちらで実行できます。遊んでみて下さい。

require 'bundler/setup'
require 'green_shoes'

class Vector
  def initialize(x, y)
    @x = x; @y = y
  end
  attr_accessor :x, :y

  def mul(a)
    Vector.new(a * @x, a * @y)
  end
  
  def *(v)
    @x * v.x + @y * v.y
  end
  
  def +(v)
    Vector.new(@x + v.x, @y + v.y)
  end
  
  def -(v)
    Vector.new(@x - v.x, @y - v.y)
  end
  
  def absl2
    @x ** 2 + @y ** 2
  end
end

class Circle
  def initialize(x, y)
    @cl = [rand(256), rand(256), rand(256)]
    @v = Vector.new((rand * 2 + 4) * (rand - 0.5) * 4, (rand * 2 + 4) * (rand - 0.5) * 4)
    @o = Vector.new(x, y)
  end
  attr_accessor :cl, :v, :o, :cir
  
  def dist(c)
    Math.sqrt((o - c.o).absl2)
  end
  
  def near(cir)
    cir.each {|c| return true if dist(c) <= R * 2}
    false
  end
end

def geneArray(cir)
  fl = Array.new(a = cir.size)
  a.times do |i|
    fl[i] = Array.new(a, true)
    a.times {|j| fl[i][j] = false if i <= j}
  end
  fl
end


Wd = 500

Shoes.app width: Wd, height: Wd do
  circles = []
  background black
  R = 20
  animate 30 do
    fl = geneArray(circles)
    circles.each_with_index do |c1, i|
      c1.cir.remove
      stroke(rgb(c1.cl[0], c1.cl[1], c1.cl[2]))
      fill(rgb(c1.cl[0], c1.cl[1], c1.cl[2]))
      c1.o += c1.v
      c1.v.x = - c1.v.x if c1.o.x >= Wd - R or c1.o.x <= R
      c1.v.y = - c1.v.y if c1.o.y >= Wd - R or c1.o.y <= R
      c1.cir = oval(c1.o.x - R, c1.o.y - R, R)
      
      circles.each_with_index do |c2, j|
        next unless fl[i][j]
        next if c1.dist(c2) > R * 2
        ra = c1.o - c2.o
        tmp = ra.mul((ra * (c1.v - c2.v)) / ra.absl2)
        c1.v -= tmp; c2.v += tmp
        fl[i][j] = false
      end
    end
  end
  
  click do |button, left ,top|
    c = Circle.new(left, top)
    unless c.near(circles)
      stroke(rgb(c.cl[0], c.cl[1], c.cl[2]))
      fill(rgb(c.cl[0], c.cl[1], c.cl[2]))
      c.cir = oval(c.o.x - R, c.o.y - R, R)
      circles << c
    end
  end
end

ところどころで処理が止まるのは、フレーム毎に円のオブジェクトを全部消して、新しいオブジェクトを作っているからだと思う。Green Shoes を使って、ちがうやり方でアニメーションできるのかなあ。