クリックして生成された円が動き回ります。ぶつかると完全弾性衝突をします。端に到達すると跳ね返ります。
ここの 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 を使って、ちがうやり方でアニメーションできるのかなあ。