パーティクルというもの
しばらくダラダラとツイッターに書いていたのでブログには初登場のパーティクルなのだが。DXRubyに組み込んで簡単に使えるといいなあ、と思っていたのだ。
どういうものかというと、動きのパラメータを定義して、その通りに勝手に動くオブジェクト、という感じで、ゲームではエフェクトなどに使われる。丁度、DXRubyFw時代のSpriteが半分ぐらいは近いのではないか。あれのコマンド実行をなくした自動移動するものって感じだろう。
3D空間のパーティクルではもともとのワールドがリアルなため、衝突判定や物理演算が無いとイマイチ感であふれてしまうかもしれないが、2Dだと単純にエフェクトを作るという用途でいいような気がする。DXRuby1.4のSpriteで一つ一つコード書いてくのもしんどいので、ちょっとしたものは簡単に作れるといいな、と思ったわけだ。
先日のSceneにゲームループを突っ込むのもそうだけども、ちょっとだけ凝ったゲームを手軽に作るにはもう一段ほど上の機能が必要になると考えていて、パーティクルの機能もその延長である。
まあ、とりあえずRubyで書いてみた。
画面はこんな感じになるが、見ても何もわからないかもしれない。DXRuby1.4で動作するコードを全部載せておくので動かしてみてもらえるといいと思う。
プロトタイプレベルのParticleFactoryクラスを実装すると、こんなコードが動くようになるイメージだ。
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 hoge = ParticleFactory.new hoge.angle_rand = 360 hoge.speed_rand = 10 hoge.lifespan = 120 hoge.d_alpha = -2 hoge.image = image Window.testloop do if Input.mouse_push?(M_LBUTTON) hoge.x = Input.mouse_pos_x hoge.y = Input.mouse_pos_y hoge.generate(100) end end
角度と速度に対してランダムの範囲と切片(今回は使ってないけど)とその他いろいろ設定してParticleFactory(仮名)を生成、そいつに生成座標を設定しつつgenerate(100)すると100個のパーティクルが生成されて、あとは自動で動く。lifespanのフレームが過ぎると勝手に消える。あとはもっとたくさんのパラメータ(重力とか摩擦とか)や、画像の回転、生成座標範囲の指定などを追加するといろいろなことができるようになるんじゃなかろうか。
現状では全部Rubyで書いているが100個程度の生成ではボタンを連打してもぜんぜん遅くない。これを1000個にするとちょっと連打すると遅くなってしまうが、そんなに生成するものなのかというとそんなこともなく、ひょっとしたらRubyで書いておけば実用レベルには十分なのではないかと思ったりしなくもないわけだが、とりあえずCで実装することを目標にはしておく。
以下はライブラリ側のコードである。
require 'dxruby' module Window def self.testloop loop do yield ParticleFactory.update ParticleFactory.draw ParticleFactory.clean end end end class Particle < Sprite def initialize(x, y, ang, spd, parent) @parent = parent @lifespan = parent.lifespan @angle = ang @speed = spd self.x = x self.y = y self.image = parent.image self.target = parent.target self.alpha = parent.alpha end def update if @lifespan @lifespan -= 1 if @lifespan == 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 += Math.cos(@angle / 180.0 * Math::PI) * @speed self.y += Math.sin(@angle / 180.0 * Math::PI) * @speed end end class ParticleFactory attr_accessor :target, :image, :x, :y, :angle_rand, :angle_intercept, :speed_rand, :speed_intercept, :alpha, :lifespan, :d_alpha @@objects = [] def self.update Sprite.update @@objects end def self.draw Sprite.draw @@objects end def self.clean Sprite.clean @@objects @@objects.delete_if do |a| a.size == 0 end end def initialize @particles = [] @target = nil @image = nil @x = @y = @dx = @dy = @angle_rand = @angle_intercept = @speed_rand = @speed_intercept = 0 @alpha = 255 @lifespan = nil @d_alpha = 0 end def generate(count) @@objects.push Array.new(count) { Particle.new(@x, @y, rand() * @angle_rand + @angle_intercept, rand() * @speed_rand + @speed_intercept, self) } end end