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でグリグリ動いちゃう今時のアレコレと比べると、原始的なものですが…。
[ ツッコむ ]
以上です。
