2013/12/07(土) [n年前の日記]
#4 [dxruby][game] DXRubyで例の多関節を実験
今日もDXRubyを使って、動きを作ってみます。
今回のネタは、昔懐かしの、あの多関節を試してみようかなと。学生時代、ファミコン版でプレイしていて、必ずこの敵で何機か失ったことを思い出します。やられグセがつくと、なかなか抜け出せなくて…。
自分、この処理は、書いたことがないんですよね…。昔、隣で先輩が書いてるソレを見て、「スゴーイ。まさしくアレだー」と子供のように喜んでしまった記憶があります。さて、自分は上手く書けるだろうか…。少し不安。
とりあえず、できた…かな…と思います。こんな感じの動きで合ってますかね? 微妙に何か違う気もするけど…。
一応ざっくりと説明を。
今回は、DXRubyの Spriteクラスを使ってみました。Spriteクラスを継承して、ボディ部分を担当するクラスと、腕関節一つ分を担当するクラスを作ったわけです。自分は、Rubyについては初心者なので、こういう書き方でいいのかよく分かってませんが。
腕関節には、誰が親なのか、覚えさせておきます。@parent がソレです。また、親からの距離、親の角度+自分の角度を使って、自分の位置も求めます。
図で描いたほうが分かりやすいですかね…。一応描いてみましたが…。
この、親、子、孫…という状態が、10個前後ほど、ずっと続いているわけです。
子は、親からの距離、親の角度+自分の持ってる角度で、自分の位置を決めます。図で言えば、小さい四角い点がソレですね。また、スプライト描画の回転中心位置を、この小さい四角の位置にしておけば、各関節が滑らかに繋がっているように見えるはずです。
ひょっとして、位置の求め方が分からない方が居たりするのかもしれないか…。距離と角度があれば、sin関数、cos関数を使って、位置を求めることができますよ。
それにしても、Ruby って、度とラジアンの変換関数は無いのでしょうかね…? ググってみても、見つかりませんが。
さておき。
もしも、子供達全員が持っている角度が、0度だったら。この仕組みだと、どんな見た目になるでしょうか。…皆でピーンと、一直線になりますね。
子供達が持っている角度が、例えば10度だったら…。下の方に曲がっていく線になるはずです。逆に、全員が、-10度を持ってたら、上の方に曲がっていく線になるでしょう。
そして、子供達全員が持っている角度が、毎フレーム、少しずつ増えていったら…。線の曲がり具合が、時間と共に、グングン変わっていくはずです。
とまあ、こんな仕組みで、あのグルグルはできていたのではないかなあ、と思いますが、本当にコレで正解なのかどうか…。まあ、それらしく動いてるから、コレはコレでOKかなと。
長年、例のアレって、どうやって動いてたのかビミョーに気になっていたのですが。今回もどうにか実証(?)できたので、ちょっぴり嬉しいです。こんなにもお手軽に、実験を可能にしてくれる、DXRubyよ、ありがとう…。
とりあえず、画像とソースも置いときますね。Public Domain ってことで一つ。
_rolltaka.zip
おっと。書き忘れてた。一昨日の記事で、二足歩行の動きを試した際に、IKがどうとか書きましたが。今回のソレは、各関節が角度を持っていて、根元からの角度で位置決めをしていくわけですから…。概念としてはFKに近いのではないかと思います。もちろん、3Dでグリグリ動いちゃう今時のアレコレと比べると、原始的なものですが…。
今回のネタは、昔懐かしの、あの多関節を試してみようかなと。学生時代、ファミコン版でプレイしていて、必ずこの敵で何機か失ったことを思い出します。やられグセがつくと、なかなか抜け出せなくて…。
自分、この処理は、書いたことがないんですよね…。昔、隣で先輩が書いてるソレを見て、「スゴーイ。まさしくアレだー」と子供のように喜んでしまった記憶があります。さて、自分は上手く書けるだろうか…。少し不安。
とりあえず、できた…かな…と思います。こんな感じの動きで合ってますかね? 微妙に何か違う気もするけど…。
# ぐるぐる回る、あの多関節のテスト require 'dxruby' # ボディ部分のスプライト class BodySpr < Sprite # 初期化処理 def initialize(sx, sy, dist, ang, img) super @px, @py = sx, sy # 自身の座標格納用 @cx, @cy = sx, sy # 円運動の中心座標 @dist = dist # 円運動の半径 @ang = ang # 円運動の角度 self.image = img # 画像 self.scale_x = 1.6 # 拡大縮小率 self.scale_y = 1.6 end attr_accessor :px, :py # 毎フレーム呼ばれる処理 def update # 円運動をさせる rad = @ang * Math::PI / 180.0 @px = @cx + @dist * Math.cos(rad) @py = @cy + @dist * Math.sin(rad) # 描画位置を設定 self.x = @px - self.image.width / 2 self.y = @py - self.image.height / 2 @ang += 0.7 end end # 腕関節一つ分のスプライト class Arm < Sprite # 初期化処理 def initialize(sx, sy, dist, ang, img, parent) super @px, @py = sx, sy @cx, @cy = sx, sy @parent = parent # 親を記録 @dist = dist # 親からの距離 @ang = ang # 自分の角度 @angt = ang # 親と自分の角度を加算した角度 self.image = img self.center_x = img.width # 回転描画の中心位置をずらす end attr_accessor :angt, :px, :py # 毎フレーム呼ばれる処理 def update if @parent.instance_of?(BodySpr) # 親がボディ部分だった場合 @ang += 1 @angt = @ang self.angle = @angt bx = @parent.px by = @parent.py else # 親が腕部分だった場合 @angt = @parent.angt + @ang # 親の角度+自分の角度を得る self.angle = @angt bx = @parent.px by = @parent.py @ang += 0.3 # 自分の角度を少しずつ増やしてく @ang = -60 if @ang > 60 end # 自身の座標を算出 rad = @angt * Math::PI / 180.0 @px = bx + @dist * Math.cos(rad) @py = by + @dist * Math.sin(rad) # 描画位置を算出 self.x = @px - self.image.width self.y = @py - self.image.height / 2 end end # 画像読み込み img = Image.load("arm.png") imgbody = Image.load("ufo.png") # ボディ部分の発生 cx = Window.width / 2 cy = Window.height / 2 body = BodySpr.new(cx, cy, 80, 0, imgbody) # 腕の初期化処理 sprs = [] armmax = 4 # 腕の本数 jointmax = 12 # 関節数 dist = 22 # 関節の長さ # 何本か腕を延ばす armmax.times do |i| parent = nil # 腕一つにつき、n個スプライトを使う jointmax.times do |j| if j == 0 # 腕の根元 s = Arm.new(cx, cy, 32, i * (360 / armmax), img, body) else # 腕 s = Arm.new(cx, cy, dist, 0, img, parent) end sprs.push(s) # リストに登録 parent = s # 次の親として扱う end end # メインループ Window.loop do break if Input.keyPush?(K_ESCAPE) body.update # ボディを移動 Sprite.update(sprs) # 腕を移動 Sprite.draw(sprs) # 腕を描画 body.draw # ボディを描画 endこれまた長くなってしまいました。もっと短く、スッキリ書けないものか…。
一応ざっくりと説明を。
今回は、DXRubyの Spriteクラスを使ってみました。Spriteクラスを継承して、ボディ部分を担当するクラスと、腕関節一つ分を担当するクラスを作ったわけです。自分は、Rubyについては初心者なので、こういう書き方でいいのかよく分かってませんが。
腕関節には、誰が親なのか、覚えさせておきます。@parent がソレです。また、親からの距離、親の角度+自分の角度を使って、自分の位置も求めます。
図で描いたほうが分かりやすいですかね…。一応描いてみましたが…。
子は、親からの距離、親の角度+自分の持ってる角度で、自分の位置を決めます。図で言えば、小さい四角い点がソレですね。また、スプライト描画の回転中心位置を、この小さい四角の位置にしておけば、各関節が滑らかに繋がっているように見えるはずです。
ひょっとして、位置の求め方が分からない方が居たりするのかもしれないか…。距離と角度があれば、sin関数、cos関数を使って、位置を求めることができますよ。
x座標 = 距離 * cos(角度) + 親の位置 x y座標 = 距離 * sin(角度) + 親の位置 yソース中では、 rad = @angt * Math::PI / 180.0 なんて行もありますけど。これは、度からラジアンへの変換をしています。というのも、プログラムを書く際の sin関数、cos関数は、えてして、度ではなくてラジアンを与えないといけないので…。
それにしても、Ruby って、度とラジアンの変換関数は無いのでしょうかね…? ググってみても、見つかりませんが。
さておき。
もしも、子供達全員が持っている角度が、0度だったら。この仕組みだと、どんな見た目になるでしょうか。…皆でピーンと、一直線になりますね。
子供達が持っている角度が、例えば10度だったら…。下の方に曲がっていく線になるはずです。逆に、全員が、-10度を持ってたら、上の方に曲がっていく線になるでしょう。
そして、子供達全員が持っている角度が、毎フレーム、少しずつ増えていったら…。線の曲がり具合が、時間と共に、グングン変わっていくはずです。
とまあ、こんな仕組みで、あのグルグルはできていたのではないかなあ、と思いますが、本当にコレで正解なのかどうか…。まあ、それらしく動いてるから、コレはコレでOKかなと。
長年、例のアレって、どうやって動いてたのかビミョーに気になっていたのですが。今回もどうにか実証(?)できたので、ちょっぴり嬉しいです。こんなにもお手軽に、実験を可能にしてくれる、DXRubyよ、ありがとう…。
とりあえず、画像とソースも置いときますね。Public Domain ってことで一つ。
_rolltaka.zip
おっと。書き忘れてた。一昨日の記事で、二足歩行の動きを試した際に、IKがどうとか書きましたが。今回のソレは、各関節が角度を持っていて、根元からの角度で位置決めをしていくわけですから…。概念としてはFKに近いのではないかと思います。もちろん、3Dでグリグリ動いちゃう今時のアレコレと比べると、原始的なものですが…。
[ ツッコむ ]
以上です。