mieki256's diary



2013/12/08() [n年前の日記]

#1 [dxruby][game] DXRubyを使って星を飛ばしてみる

今日も DXRuby を使って何か書いてみますよ。そろそろ自分の中でネタ切れ感がありますけど…。

今回は、星をたくさん飛ばしてみようかと。今風のソレでカッコよく言うと、パーティクル処理…ってこのレベルでそんな呼び方をしたら、なんだか怒られそうな気もしますが…。

まずは単純に、等速度運動で飛ばしてみました。

等速度運動で飛んでいく星達
# 星を飛ばす。単純な等速度運動

require 'dxruby'

# 星一つ分のSprite
class Star < Sprite
  attr_accessor :dx, :dy, :rx, :ry, :cx, :cy

  # 初期化処理
  def initialize(cx, cy, img)
    super
    self.cx = cx
    self.cy = cy
    self.image = img
    self.init
  end

  # 座標初期化処理
  def init
    self.rx = self.cx # 発生位置を設定
    self.ry = self.cy
    
    rad = rand(360) * Math::PI / 180.0 # 乱数で角度を得る
    speed = rand(6) + 2 # 乱数で速度を得る
    
    self.dx = speed * Math.cos(rad) # sin と cos を使って、速度x,yを求める
    self.dy = speed * Math.sin(rad)
  end

  # 毎フレーム呼ばれる処理
  def update
    # 速度を加算
    self.rx += self.dx
    self.ry += self.dy

    # 描画座標を設定。画像の中心を基準位置にしてる
    self.x = self.rx - self.image.width / 2
    self.y = self.ry - self.image.height / 2

    if self.rx < 0 or self.rx > Window.width or self.ry < 0 or self.ry > Window.height
      # 画面外に飛び出したら座標や速度を再初期化
      self.init
    end
  end
end


img = Image.load("star.png")

# 発生位置。画面の右上のほうに設定
cx = Window.width * 0.75
cy = Window.height * 0.25

sprs = []
64.times do
  sprs.push(Star.new(cx, cy, img)) # 星を発生させて配列に登録
end

# メインループ
Window.loop do
  break if Input.keyPush?(K_ESCAPE)

  Sprite.update(sprs)
  Sprite.draw(sprs)
end
また長くなった…。もっとシンプルに書けないのかな…。

やってることは、星一つ分を表示するクラスを作って、大量に発生させてるだけです。 これを延々繰り返してるだけですね。

2Dゲームなら、この動きでも十分と言えば十分、ではあるのですけど…。例えば…。 そのような調整を加えるだけで、結構見た目が変わりますし。

でも、ちょっと欲が出てきました。もう少し、一工夫してみようかなと。

とりあえず、座標計算だけは3Dにしてみたりして。

座標計算だけ3Dになってる星達
# 星を飛ばす。座標だけ3D計算

require 'dxruby'

# 星一つ分のSprite
class Star < Sprite
  attr_accessor :rx, :ry, :rz, :cx, :cy

  @@scrz = 200 # 画面までの距離
  @@dz = -2.5 # z座標の速度
  @@w = 256 # 星の発生範囲

  # 初期化処理
  def initialize(cx, cy, img)
    super
    self.cx = cx
    self.cy = cy
    self.image = img
    self.init
  end

  # 座標初期化処理
  def init
    self.rx = rand(@@w) - (@@w / 2) # 発生位置を設定
    self.ry = rand(@@w) - (@@w / 2)
    self.rz = rand(256) + 10
  end

  # 毎フレーム呼ばれる処理
  def update
    # 速度を加算
    self.rz += @@dz
    
    # 画面のこちら側に飛び出してたら座標を初期化
    self.init if self.rz <= 0.0
    
    # 描画座標を算出
    px = self.rx * @@scrz / self.rz
    py = self.ry * @@scrz / self.rz
    self.x = self.cx + px - self.image.width / 2
    self.y = self.cy + py - self.image.height / 2

    if self.x < 0 or self.x > Window.width or
        self.y < 0 or self.y > Window.height
      # 画面外に飛び出したら座標や初期化
      self.init
    end
  end
end


img = Image.load("star.png")

# 発生位置。画面の右上のほうに設定
cx = Window.width * 0.75
cy = Window.height * 0.25

sprs = []
64.times do
  sprs.push(Star.new(cx, cy, img)) # 星を発生させて配列に登録
end

# メインループ
Window.loop do
  break if Input.keyPush?(K_ESCAPE)

  Sprite.update(sprs)
  Sprite.draw(sprs)
end
うーん。GIFアニメのフレームレートが低くて、パッと見では、違いが分かりづらいかなあ…。16FPSでは、こうなっちゃうか…。実際に、DXRubyのウインドウ上で、60FPSで眺めると、印象が随分違うのですけど…。まあ、いいや。

ちなみに、DXRuby のサンプルスクリプト中にも、3D計算をしていて、もっと見た目がカッコイイ、flight.rb というサンプルがありますので、そちらのほうが参考になりそうな気もします。

flight.rb って、一目見ただけで、「アレが…作れそう!」と思うサンプルですよね…。眺めてるだけで、夢がひろがりんぐ。

さておき。「3D計算ってどうやればいいの?」と疑問を持つ方も居るかもしれないので、一応念のために説明図も置いときます。真横から見たらこうなるよ、という図になってます。…3D計算というか、透視投影? 透視変換? ですね。
3D計算
3D空間上のx,y,z座標値と、カメラ位置から画面までの距離を使って、画面上のx,y座標を求めたい ―― 図で言えば sy を求めたいわけですけど、こんな感じで求められますよ、と。

「カメラ位置から画面までの距離って、どんな値を決めておけばいいの?」と思われる方も居るのかな…。どうせ2Dゲームで使う予定ですし、見た目がそれっぽくなればOKなので、テキトーな値でいいです。

や、広角だの望遠だの焦点距離だのを意識して、真面目に各値を算出するのもアリなんですけど。所詮はゲームなので、見た目が大事じゃないのかなと。アレコレ正確な値よりも、見た時にイケてるかどうかが大事、と自分は思ってまして。…アニメの作画と似たノリがあるような気もします。それでカッコよくなるならインチキ上等! みたいな。

ところで、この星達。遠くの星も、近くの星も、描画画像の大きさが同じなので、なんだか今一つですね…。

一応、z座標値に基づいて、大きさを変えてみましょうかね。
距離に応じて星の大きさを変えてみる
# 星を飛ばす。座標だけ3D計算

require 'dxruby'

# 星一つ分のSprite
class Star < Sprite
  attr_accessor :rx, :ry, :rz, :cx, :cy

  @@scrz = 200 # 画面までの距離
  @@dz = -2.5 # z座標の速度
  @@w = 256 # 星の発生範囲

  # 初期化処理
  def initialize(cx, cy, img)
    super
    self.cx = cx
    self.cy = cy
    self.image = img
    self.init
  end

  # 座標初期化処理
  def init
    self.rx = rand(@@w) - (@@w / 2) # 発生位置を設定
    self.ry = rand(@@w) - (@@w / 2)
    self.rz = rand(256) + 10
  end

  # 毎フレーム呼ばれる処理
  def update
    # 速度を加算
    self.rz += @@dz
    
    # 画面のこちら側に飛び出してたら座標を初期化
    self.init if self.rz <= 0.0
    
    # 描画座標を算出
    px = self.rx * @@scrz / self.rz
    py = self.ry * @@scrz / self.rz
    self.x = self.cx + px - self.image.width / 2
    self.y = self.cy + py - self.image.height / 2

    # 大きさを変えてみる
    scale = @@scrz / self.rz
    self.scale_x = scale
    self.scale_y = scale

    if self.x < 0 or self.x > Window.width or
        self.y < 0 or self.y > Window.height
      # 画面外に飛び出したら座標や初期化
      self.init
    end
  end
end


img = Image.load("star.png")

# 発生位置。画面の右上のほうに設定
cx = Window.width * 0.75
cy = Window.height * 0.25

sprs = []
64.times do
  sprs.push(Star.new(cx, cy, img)) # 星を発生させて配列に登録
end

# メインループ
Window.loop do
  break if Input.keyPush?(K_ESCAPE)

  Sprite.update(sprs)
  Sprite.draw(sprs)
end
うむ。ド派手になった。

や。もっとさりげなく変わるかなーと思って処理を追加したけど、予想外の結果に。こんなはずでは…。まあ、これはこれで、派手だから良し。カッコよければ、なんでもOK。

updateメソッドの中で、「scale = @@scrz / self.rz」という行がありますが、ここでスケールを変えてます。変数 scale に、0.5 とか 0.25 とか掛けてあげれば、もっとさりげない見た目になるはずです。

ということで、今回は星をたくさん飛ばしてみました。一応、画像とソースを置いときますね。Public Domain ってことで。

_movestar.zip

ちなみに。

Unity には標準でパーティクルシステムがついていて、プログラムをわざわざ書かなくても、GUIで各パラメータを調整していけるのでした…。しかも、当然3Dで動くし…。ギャフン。

_Unity - パーティクル アニメータ(旧パーティクルシステム)
_Unity - パーティクルシステム(Shuriken)

でも、見るからに、操作方法を習得するのが大変そうですな…。もちろん、習得すれば、複雑なパーティクルを出せるのでしょうけど。

アプリ配布時の容量や、メモリ消費量で特に問題が無いなら、この手のソレを画像で丸々持ってしまって済ませちゃうのもアリかもですね。パーティクルを使って画像生成するソフトを紹介しておきます。

_Prominence
_発色弾
_Detonation
_パーティクリン

これらソフトで画像を作って、その画像を今回のような方法で描画するのも全然アリです。ますますド派手な画面が作れるのではないかなと。

以上です。

過去ログ表示

Prev - 2013/12 - Next
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project