エミッタ機能追加
なんもないエミッタではちょっと寂しいので、機能を追加してみた。追加してみた、というだけで、名前とか使い勝手とか実装とかはいい加減なのだが、こんなようなことができるといいのかなあ、という雰囲気の提示である。
以下に貼り付けてあるがちょっと長くなってきた。
require 'dxruby' class StandardParticle < Sprite attr_accessor :dx, :dy, :lifetime, :parent def initialize @dx = 0 @dy = 0 @lifetime = nil end def update if @lifetime @lifetime -= 1 if @lifetime == 0 self.vanish return end end if self.alpha > 0 self.alpha += @parent.d_alpha self.alpha = 0 if self.alpha < 0 end self.x += @dx self.y += @dy @dy += @parent.gravity end end class ParticleFactory attr_accessor :particles @@all_objects = [] def self.step obj = @@all_objects.map{|o|o.particles} Sprite.update obj Sprite.draw obj Sprite.clean obj @@all_objects.delete_if {|v| v.particles.empty?} end def initialize(particle_class) @particle_class = particle_class @particles = [] end def generate(count) flag = @particles.empty? count.times do |i| temp = @particle_class.new yield temp, i @particles << temp end @@all_objects.push(self) if flag end end class SampleEmitter attr_accessor :gravity, :d_alpha, :image class EmitterGenerator attr_accessor :b, :interval, :count def initialize(b, interval) @b = b @interval = interval @count = 0 end end def initialize(particle_class) @factory = ParticleFactory.new(particle_class) @screen = Sprite.new @screen.collision = [0, 0, 639, 479] @generator = [] end def generate(count) @factory.generate(count) do |p, i| p.parent = self yield p, i end end def generate_line(count, x1, y1, x2, y2) diff_x = x2 - x1 + 1 diff_y = y2 - y1 + 1 @factory.generate(count) do |p, i| r = rand() p.x = x1 + r * diff_x p.y = y1 + r * diff_y p.parent = self p.image = @image yield p, i if block_given? end end def generate_circle(count, x, y, r, angle_min, angle_max) @factory.generate(count) do |p, i| angle = angle_min + rand() * (angle_max - angle_min) p.x = x + Math.cos(angle / 180.0 * Math::PI) * r p.y = y + Math.sin(angle / 180.0 * Math::PI) * r p.parent = self p.image = @image yield p, i, angle if block_given? end end def add_generator(interval, &b) @generator << EmitterGenerator.new(b, interval) end def update @generator.each do |g| g.count += 1 if g.count >= g.interval g.count = 0 g.b.call end end @factory.particles = @screen.check(@factory.particles) end end image = Image.new(3,3) image[1,0] = [128, 255, 255, 255] image[1,2] = [128, 255, 255, 255] image[0,1] = [128, 255, 255, 255] image[2,1] = [128, 255, 255, 255] image[1,1] = C_WHITE emitter = SampleEmitter.new(StandardParticle) emitter.d_alpha = 0 emitter.gravity = 0.05 emitter.image = image emitter.add_generator(1) do emitter.generate_line(20, 0, 300, 639, 400) end emitter.add_generator(5) do emitter.generate_circle(50, 400, 200, 50, 0, 360) do |p| p.dx = -1 end end Window.loop do emitter.update ParticleFactory.step end
今回いじったところだが、とりあえずParticleFactoryをArrayから継承していたのをやめた。Sprite.checkで衝突判定して画面外チェックをするとArrayが返ってくるから、そこからParticleFactoryを生成するのが面倒だったからだ。インスタンス変数に持たせておけば実用上の問題は無いのでこのほうがやっぱりいいかもしれない。
Emitterのほうは変わったというか、もともと何もなかったので機能追加というか、追加したら別物になったというか。主に画面外チェックと、生成範囲指定と、指定フレーム間隔で生成する機能を追加している。
画面外チェックはいつものSprite.checkでやる方法。生成範囲指定はgenerate_lineとgenerate_circleの追加で、それぞれ2点指定の線分上ランダムと指定円の円周上ランダムという生成パターンをサポートする。ブロックを呼ぶ前に座標などを設定してくれているので、必要な追加設定がなければブロックを渡す必要も無い。こういう数字がだらだら並んでしまう引数の場合にキーワード引数を使うとよいのだろうなと思った。あと、指定フレーム間隔での生成はadd_generatorメソッドで、渡したブロックを引数で指定したフレーム数に1回呼び出してくれる。複数登録できる。んで毎フレームEmitter#updateを呼んでおけばあとは自動で。
いまんとことりあえず書いて動かしてみたという感じだから今後大きく変わるとは思うが、こんなことができればまあいいのかなあー??といったところ。実用的に扱うにはまだ足りないものも多いだろうし、それを実現するには今の作りではいけないということもありそうで、予断を許さない状況である。