まだソースをざっと見ただけですが、気になるところを。
* Sprite が Texture の子クラスであるのが気になりました。
コンポジットにしなかった理由はありますか?
* Scene の method_missing は便利かもしれませんがやりすぎではありませんか。
* Eventable は、意味は分かるのですが、そういう英単語はないと思います。
* イベント周りですが、メソッドのフックを使うと綺麗に書けそうな気がします。
http://ja.doukaku.org/169/lang/ruby/
* StarFrame.camelize メソッドは、グローバルなメソッドにするより
String クラスに追加定義したほうが使いやすくないでしょうか。
その他 Collision とか便利そうだなーとか思ったので後でじっくり拝見させていただきます。
--
Hajime Hoshi <hajim...@gmail.com>
実行しようとしたのですが、エラーになりました。
$ ruby main.rb
./lib/starframe/sprite.rb:89: warning: parenthesize argument(s) for
future version
./lib/starframe/loadable.rb:4:in `open': No such file or directory -
images (Errno::ENOENT)
from ./lib/starframe/loadable.rb:4:in `foreach'
from ./lib/starframe/loadable.rb:4:in `load'
from ./lib/starframe/image.rb:9:in `load'
from main.rb:6
--
Hajime Hoshi <hajim...@gmail.com>
次はこのようなエラーが…
ひょっとして Ruby 1.8.7 用ですか?
$ ruby main.rb
./lib/starframe/sprite.rb:89: warning: parenthesize argument(s) for
future version
./lib/starframe.rb:21:in `camelize': wrong argument type Symbol
(expected Proc) (TypeError)
from ./lib/starframe.rb:20:in `map'
from ./lib/starframe.rb:20:in `camelize'
from ./lib/starframe/loadable.rb:8:in `load'
該当箇所の map(&:capitalize) は map{|s| s.capitalize} と直しました。
が、次なるエラーが…。
$ ruby main.rb
./lib/starframe/sprite.rb:89: warning: parenthesize argument(s) for
future version
main.rb:45:in `init': uninitialized constant StarFrame::Image::Back3x1
(NameError)
from ./lib/starframe/eventable.rb:12:in `call_init_without_animation'
from ./lib/starframe/sprite/animatable.rb:18:in `call_init'
from ./lib/starframe/scene.rb:16:in `initialize'
from ./lib/starframe/scene.rb:27:in `new'
from ./lib/starframe/scene.rb:27:in `to_proc'
from ./lib/starframe/scene.rb:28:in `call'
from ./lib/starframe/scene.rb:28:in `to_proc'
from main.rb:145:in `run'
from main.rb:145
--
Hajime Hoshi <hajim...@gmail.com>
main.rb 動かせました。
なかなかキャッチーな映像が出てきました。
> > * Sprite が Texture の子クラスであるのが気になりました。
> 意味的に、Spriteのベース部分は「座標等の情報を持つだけのTexture」なのかなと思ってそうしました。
> render_textureのオプションで渡せる部分をインスタンスで保持できて、「Sprite#render_to texture」で描画でき
> ると楽かなといったところです。
> 逆に、Texture#render_spriteを実装してしまったほうが良いのかもしれません。
>
> ただ、サブクラスである利点は現状殆どないですね。
> renderイベント内でrender系メソッドを関数的に使用できるくらいでしょうか。
関数的に指定できるのは嬉しいのですが、
Texture の子クラスであるという縛りをつけてまでそれが嬉しいかというと
僕はちょっと疑問です。
速度面まで考えると逆に ISA さんの方法のほうがいいのかな。うーん。
> > * Eventable は、意味は分かるのですが、そういう英単語はないと思います。
> そうなんです。ありません(笑)
> 何か他に適切な名称があれば変更したいところです。
EventHandleable とか?
> > * イベント周りですが、メソッドのフックを使うと綺麗に書けそうな気がします。
> イベント周りはRailsの手法を参考にしました。
> これだとモジュール側の実装は手間ですが、対象のメソッドの前後どちらに(あるいは両方に)割り込む場合にでも同じ形式で記述でき、
> 実行時の処理が簡潔になるので速度面で有利かなと思いました。
Eventable を使用して、こんなに簡潔に書けるようになった、というサンプルがやっぱりほしいです。
> > その他 Collision とか便利そうだなーとか思ったので後でじっくり拝見させていただきます。
> ありがとうございます。
> Collisionは悩みましたが、悩んだだけのものは出来たんじゃないかと思っています。
> 開発メモを同梱し忘れたので別途上げました。
> http://isaisstillalive.googlepages.com/starframe_memo.txt
> 上記の通り、Numericのダブルディスパッチとcoerceを意識しています。(coercingは未実装ですが)
>
> ただ、Spriteが矩形である以上、ダブルディスパッチだけでもある程度上手くいくと思うので、ここまでリッチにする必要はないのかもしれません。
> ゲームである以上、正確さはほどほどにして速度を優先しなければならない部分も多いと思うので、そこが悩みどころです。
イベントと同様に、実際のサンプルを見てみたいです。
フレームワークは、実際のゲームを作りながら考えていったほうが、
必要十分なものができると思うのですがいかがでしょうか。
> ・常にrender_textureのオプションを指定している
> ->オプションを全て切捨てたら
> 40fps -> 46fps (*1.15)
実は、 options 引数が省略されたときに、オプション関係の処理を飛ばす
ようになっています。
> ->ここから、モジュールを使用せずに直接Spriteクラスに移動用メソッドを定義し、メソッドチェインの回数を減らすと
> 210fps -> 260fps (*1.24)
>
> おいつけない……。
>
> モジュールによる拡張方式は速度の面で不利なようですね。
> もう少し良い手段がないか考えてみます。
ふむー、モジュールをいったんかむと遅くなってしまうのですね。
効率を考えるとモジュールや継承を避けたほうがいいのかなあ。
あといろいろ見て気づいたことです:
* require 'lib/...' が気になりました (パスと名前空間が一致してない)。
$LOAD_PATH に './lib' を加えてしまうのはどうでしょう。
* Loadable についてですが、文字列 (String) で指定するのに比べて
どのようなメリットがあるのかありますか。
定数でとってしまうのはいろいろトラブルになりそうな気がしますが。
* 点をあらわすのを Vector でやっていますが、やっぱり配列で十分な気がします
(もうそうなっていると思われますが)。
Vector (Matrix) は Ruby べた書きなので速度面では期待できません。
* Animatable モジュールは Eventable モジュール include 前提ですか?
* Movable モジュールは分ける必要あったのでしょうか。
Sprite クラスに組み込みにしてもよかったんじゃないかなあ。
--
Hajime Hoshi <hajim...@gmail.com>
>> Texture の子クラスであるという縛りをつけてまでそれが嬉しいかというと
>> 僕はちょっと疑問です。
>
> 良く考えると、スプライトにはrenderイベントはいらないかもしれませんね。
> 背景がある上でさらに何か書いてから、というのはスプライトの範疇を超えているような気がしてきました。
>
> そして、オフスクリーンに一旦描画していたのは、
> alphaが設定されている時にrenderイベントで描画されたものをどうするかという問題があったからです。
> (renderイベントでscreenに直接半透明で描いてしまうと、背景が透けてしまう)
>
> そうすると、「保持している画像を適宜図形変換させて指定されたTextureに描画する」という単純な機能だけで良さそうです。
> (純正のものと同じです)
Texture#render_texture のオプション云々を管理してくれるクラスとして、
それに専念するということでしょうか。
>> * Loadable についてですが、文字列 (String) で指定するのに比べて
>> どのようなメリットがあるのかありますか。
>> 定数でとってしまうのはいろいろトラブルになりそうな気がしますが。
>
> あまり利点はないかもしれないですねー。
> Pathだけ保持するようにして、簡単にアクセスできる、程度でも良いかと思われます。
むしろ定数名とファイル名がかぶってしまえるのがちょっと怖いなあと。
> ローダが存在するのは、exerbで固める際に、
> exyファイルにデータのパスを全て含める機能を付けたいと思ってるからです。
> http://exerb.sourceforge.jp/man/doc/recipe.ja.html#0304
> 暗号化はできませんが、まとめてexeにしてしまうだけでもある程度の効果はあるんじゃないかと思っています。
Exerb と似たようなもので rubyscript2exe っていうものがあるのですが、
ディレクトリを tar で固めて exe にしてくれたりします。
>> * 点をあらわすのを Vector でやっていますが、やっぱり配列で十分な気がします
>> (もうそうなっていると思われますが)。
>> Vector (Matrix) は Ruby べた書きなので速度面では期待できません。
>
> 配列で十分だと思いました(笑)
> むしろ、@x,@yプロパティにして、positionをメソッドにしちゃっても良いですね。
> def position; [@x, @y]; end
それはいいですね。取得も直感的。
x, y = hoge.position
>> * Animatable モジュールは Eventable モジュール include 前提ですか?
>
> そうです。
> 汎用的な描画メソッド(to_texture等)が存在すればそこにホックをかけても良いかと思っているのですが、
> どちらにせよupdateイベントがないとアニメのフレームを自動更新できないんですよね。
すると Animatable モジュールが Eventable モジュールを include すべきじゃないかと。
>> * Movable モジュールは分ける必要あったのでしょうか。
>> Sprite クラスに組み込みにしてもよかったんじゃないかなあ。
>
> 移動しないオブジェクトもスプライトで表示すると思うので、そういった場合にincludeしなければ速度面で有利かなと思ったんですが、
> 移動するオブジェクトの際に速度面で有利なほうが現実的ですね。
> 組み込みにしちゃいましょうか。
そのほうがよいかと思います。
もともと組み込みなら include のコスト云々考える必要もないですね。
(移動する / しない の区別をつける必要がそもそもあったのでしょうか。)
--
Hajime Hoshi <hajim...@gmail.com>
> Sprite::MovableをSpriteに統合し、Spriteクラスにフレームごとの移動量を持たせることにしました。
>
> SpriteをTextureクラスのサブクラスにするのをやめ、
> 「一枚のTextureとそれを表示するためのオプション全てを管理するクラス」と再定義しました。
了解です。
> 上記に伴い、Sprite::Animatableモジュールについても仕様変更を行おうと思っていて、
> 「Spriteのオプションを時間軸に沿って変更するモジュール」と再定義しようと思っています。
>
> ・複数の画像を横に並べた一枚の画像を、:src_xを変更して表示することでアニメーションさせることができる。
> ・「一瞬だけ光って元に戻る」といったエフェクトをループしないアニメーションのオン/オフで制御できる。
> ・フレームごとに角度を微妙に変更することで回転アニメーションができる。
> (回転は移動ではなくアニメーションになる)
>
> といった表現が可能になります。
>
> 懸念点として、1番は表示するTextureを変更するのに比べて遅くなりそうな気がします。
> オプションのほかに、表示するTexture自体も変更できるようにしたほうが良いかもしれません。
>
> ----
> これを書きながら思いついたのですが、
> 一つのSpriteに複数のTextureをHashで保持することができるようになると、
> RPGでの上下左右を向いた画像や、STGでの機体の傾きを管理するのが便利かもしれませんね。
>
> アニメーションも、表示するキーを変更するだけになるので扱いが楽で良いかも。
昔親子関係をもつスプライトというのを作ったことがありますが、
もしかしたら便利かもしれません。
> > すると Animatable モジュールが Eventable モジュールを include すべきじゃないかと。
> Eventableモジュールは、SpriteとSceneで同じことをしていたので抜き出しただけなんです。
> ユーザがEventableモジュールをincludeして使うことは考えていないので、他のモジュールがincludeする必要はないと考えてま
> す。
よくわからなかったのですが、 Animatable モジュールはユーザが include する可能性はありますか。
> > むしろ定数名とファイル名がかぶってしまえるのがちょっと怖いなあと。
> これについてですが、定数はLoadableモジュールをincludeしたモジュールのネームスペース下に作成されます。
> これもEventableモジュールと同じく、画像、音声、シーンをそれぞれロードする必要があったために作成したモジュールですので、
> あまり衝突はしないのではないかと考えています。
それもそうなんですが、モジュールを完璧に名前空間的に使うのには若干の不安があります。
include すれば上書きできてしまうので。
また、文字列よりも定数で扱うほうが決定的に便利だ、という点は特になさそうなのですが、
わざわざ定数にする必要はあったのかなーと思いました。
たとえば Image に [] 特異メソッドを定義して
texture = Image[:back_3x1]
とかでも十分できますよね。
> http://isaisstillalive.googlepages.com/starframe_2.zip
> Spriteクラス、Sprite::Animatableモジュールを修正したファイルを上げました。
>
> 今回の特徴はアニメーションです。
> main.rbのうち、
> ・点滅しながらバウンドする星(alphaの変化)
> ・左上から右下まで弧を描くように動く音符(x,yの変化)
> ・背景の切り替え(src_xの変化)
> をSprite::Animatableで行っています。
>
> ----
> class AnimatedSprite < StarFrame::Sprite
> animate 0, [0, 1, 2, 3] do |c|
> self.x = c
> end
> end
>
> のように、クラス定義内でanimateメソッドを使用することでアニメーションを作成することができます。
>
> animateメソッドの引数は
> ・開始フレーム
> ・各コマでブロックに渡される値の配列
> ・コマの間隔(1で毎フレーム)
> となり、同時に渡されたブロックが適宜実行されます。
>
> コマ数は第二引数の配列サイズで、「コマ数×間隔」フレームでループします。
> 複数のアニメーションを定義することができ、その場合は一番長いものに合わせます。
なるほど。時間軸にそって変わってゆくと。
だいごくんの Interval クラスと似ていますね。
ゲームにおいては「右に 10 ピクセル、上に 10 ピクセル、左に 10 ピクセル」などの
「普通の手続き型プログラムではでは表しにくい」動きが多かろうと思うのですが、
そこらへんは表現できますか。
> また、第二引数の配列を簡単に作成するためにIntegerにメソッドを追加しています。
> ・linearメソッド
> 0からselfの直前までを直線的に表した配列
> ex) 10.linear #=> [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
> ・accelerateメソッド
> 0からselfの直前までを二次関数的な加速で表した配列
> ex) 10.accelerate #=> [0.0, 0.1, 0.4, 0.9, 1.6, 2.5, 3.6, 4.9, 6.4,
> 8.1]
> ・decelerateメソッド
> 0からselfの直前までを二次関数的な減速で表した配列
> ex) 10.decelerate #=> [0.0, 1.9, 3.6, 5.1, 6.4, 7.5, 8.4, 9.1, 9.6,
> 9.9]
linear は Range オブジェクトでいいような気がします。
0...10
decelerate メソッドも Range#map でいけますね。
(0...10).map{|x| (x**2)/10.0}
個人的には既存クラスにメソッドを増やすのはあまり好きではなく、
よほど汎用的に使えるものではない限り、増やすのはどうかなーと思うのですが。
ところで、浮動小数点を加算してふやす形にすると、
丸め誤差が生じますがそこらへんどうでしょう。
> ※このあたりはStepManiaというDDR(ダンスゲーム)クローンのアニメーション機能を参考にしています。
> http://foonmix.nothing.sh/foonplus_html/bganimations.html
ほうほう。後で見てみます。
--
Hajime Hoshi <hajim...@gmail.com>
> > よくわからなかったのですが、 Animatable モジュールはユーザが include する可能性はありますか。
>
> Animatableモジュールはユーザがincludeすることではじめて使用できます。
>
> class StarFrame::Sprite
> include StarFrame::Sprite::Animatable
>
> animate ...
> ...
> end
> end
>
> という形で使用します。
>
> これは、第三者が作成したSpriteへの機能拡張をプラグイン形式で共有できるように
> モジュール単位で機能を追加していこうと考えているためです。
> 全てincludeしておいてしまうと処理に時間がかかってしまうのでこうしてます。
> class StarFrame::Sprite
の部分は継承してオリジナルなクラスを作るのが正しいのかな?
class HogeClass < StarFrame::Sprite
Sprite が Eventable を元から include しているので、 Animatable が
Eventable を include してしまうと二重 include になってしまう、
ということでしょうか。なるほど。
Animation で、「Eventable を include してないならば include する」というのを
付け加えるのはどうでしょう。実際に動かしていませんが以下の要領でできるかと。
unless self.ancestors.include?(Eventable)
include Eventable
end
Sprite が Animatable を最初から include してもいいような気がしてきました。
> > なるほど。時間軸にそって変わってゆくと。
> > だいごくんの Interval クラスと似ていますね。
> > ゲームにおいては「右に 10 ピクセル、上に 10 ピクセル、左に 10 ピクセル」などの
> > 「普通の手続き型プログラムではでは表しにくい」動きが多かろうと思うのですが、
> > そこらへんは表現できますか。
>
> コンセプトは近いと思います。
> こちらはSpriteのサブクラスに対して、それぞれ一種類しか定義できないのが欠点ですね。
> アニメーションを使いまわせる&切り替えられる上手い手段が欲しいところです。
> モジュールかなぁ。
>
> 上記の移動は
> animate 0, 0...10 do
> @x += 1
> end
> animate 10, 0...10 do
> @y -= 1
> end
> animate 20, 0...10 do
> @x -= 1
> end
> 開始フレームをズラし、アニメーションを複数指定することで表現できます。
> アニメーションを後ろに足していく指定方法ではないので、
> 開始フレームが重なってしまうと平行して実行されてしまうので注意が必要です。
それは途中で挿入したくなった場合や、一部フレーム数が変更になった場合に厳しいのでは。
> > ところで、浮動小数点を加算してふやす形にすると、
> > 丸め誤差が生じますがそこらへんどうでしょう。
>
> 加算や減算をする場合は初期値が重要だと思うので、それをどこかに保存しておかなければなりませんね。
> 誤差に関しては、このブロックはインスタンスメソッドになるので、
> animate 0, 0...10 do |c|
> @x_animate += 1
> @x = @x_animate*1.6
> end
> というように、間にIntegerのインスタンス変数をはさむのはどうでしょう。
>
> そうするとわざわざ第二引数に「係数の配列」を設定する必要がなくなってしまう気はしますが。
> animate 0, [0x00, 0xFF] do |c|
> self.alpha = c # 点滅
> end
> animate 0, 17.linear do |c|
> self.tone = 256-16*c # 白く光って元に戻る
> end
> 初期値が関係ないアニメーションならば係数を利用するほうが楽だと思います。
ふーむ。
今のところ animate メソッドは、フレームごとにある規則にしたがって
更新される数値を使って、自身のプロパティをどうにかするというものですよね。
線形変化や二次関数的変化できるルーチンが用意されたとして、
ちょっとでもその用意されたものから離れたアニメ (更新) をしたくなった場合、
結局自分で書くことになりそうです。
すると極端な話、最初から何もない (単純に更新時に呼ばれるルーチンをユーザーが定義) か、
実際にゲームを作り分析した上で、必要だと思われるルーチンを抽出するかの
どちらかになると思います。
フレームワークとしてどこまで提供するのかは難しいですね。
アニメーションや、もっと一般的に「時間軸にそって更新されゆくもの」は
さまざまな表現がありますが (だいご君の Interval など)、
何がベストだというのはいまだにわからず、非常に興味深いものがあります。
(コルーチンでかけるのがもっとも自然だと思うのですが、
Ruby のコルーチン (もとい Continuation) は重くて使い物になりません。)
--
Hajime Hoshi <hajim...@gmail.com>
>> Animation で、「Eventable を include してないならば include する」というのを
>> 付け加えるのはどうでしょう。実際に動かしていませんが以下の要領でできるかと。
> それは安全だとは思いますが、そこまでモジュールが面倒を見てしまってよいのかとも思います。
> Eventableをincludeしていなければ例外を発生させるという手もあると思いますし、
> eachなしのEnumerableと同様に、実行時に例外を投げるというのもありますね。
まあ例外なげるならそうですね。
いまの状態だとうっかりエラーになるのが遅延するかなーと思って。
>> それは途中で挿入したくなった場合や、一部フレーム数が変更になった場合に厳しいのでは。
> 事前に作成しておいたアニメーションを実行する、という形のアニメーションしか考えてませんでした。
> クラス定義で設定したアニメーション設定を、initialize実行時に内部で使いやすい形に変換してしまっているので動的に変更するのは難しいです
> ね。
> インスタンス単位でアニメーションを作成できるほうが便利なのかもしれません。
動的に、もそうですけど、ゲームを作っている最中の変更にも弱いかなと思いました。
>> アニメーションや、もっと一般的に「時間軸にそって更新されゆくもの」は
>> さまざまな表現がありますが (だいご君の Interval など)、
>> 何がベストだというのはいまだにわからず、非常に興味深いものがあります。
>> (コルーチンでかけるのがもっとも自然だと思うのですが、
>> Ruby のコルーチン (もとい Continuation) は重くて使い物になりません。)
> そこらへんは、Ruby1.9のFiberがあれば楽そうですね。
> ゲームへの応用例もいくつかありますし。
>
> http://mono.kmc.gr.jp/~yhara/d/?date=20080210
> http://blogs.wankuma.com/myugaru/archive/2008/03/10/127059.aspx
>
> 1.8のEnumerable::Enumeratorで同じようなことができましたが、
> 内部でGeneratorを使用しているようで、さらにGeneratorは内部でcallccを使っているそうなので結局は重そうです。
1.8 は結局 Continuation になるのでとても重いです。
Fiber はあまりいじってませんが、その記事みる限りではいけそうですね。
1.8 でも拡張ライブラリレベルで Fiber を実現することは可能なのかなあ…。
調べてみます。
--
Hajime Hoshi <hajim...@gmail.com>
間違い発見!
> (args.shift).__send__(self, args)
は
(args.shift).__send__(self, *args)
でした。
--
Hajime Hoshi <hajim...@gmail.com>