メインループの呪縛を解除する案

11 views
Skip to first unread message

Hajime Hoshi

unread,
Jun 12, 2008, 2:13:31 AM6/12/08
to Star Ruby
星です。

とある方から、「メインループを後で実行する形にしてしまうと、
インタプリタで実行するメリットが失われる」旨の指摘を受けました。
たしかにその通りで、更新タイミングを自分で制御できないので、
たとえば irb で逐次実行などができません。

そこで提案なのですが、いまの Game.run メソッドを、次のように分解するのはどうでしょうか。
(Game がモジュールからクラスになるので、その点非互換です。)

def Game.run(*args, &block)
begin
g = Game.new(*args)
loop do
g.wait
g.update
break if g.terminated?
end
ensure
g.dispose
end
end

Game.run メソッドをつかわないで、 new を使うことによって
ゲームループを自由に制御することができるようになります。

Game.screen などはどうするんだとか、いろいろ問題は出てきそうですけれども。

ご意見お待ちしております。

--
Hajime Hoshi <hajim...@gmail.com>

Hajime Hoshi

unread,
Jun 12, 2008, 2:18:52 AM6/12/08
to Star Ruby
星です。

yield が抜けてました。

def Game.run(*args)


begin
g = Game.new(*args)
loop do
g.wait

yield(g)


g.update
break if g.terminated?
end
ensure
g.dispose
end
end

--
Hajime Hoshi <hajim...@gmail.com>

Daigo

unread,
Jun 23, 2008, 5:41:22 AM6/23/08
to Star Ruby
なんか、美しくないですね・・・・。
Timerクラスみたいなのを作った方がいいかも??
マルチスレッドみたいにするひと出てきそうですし。

StarRubyの多機能化はいいことだと思うけれども、難易度があがらないかだけが、心配です。
> Hajime Hoshi <hajimeho...@gmail.com>

Daigo

unread,
Jun 24, 2008, 6:37:34 AM6/24/08
to Star Ruby
星君と話しましたが、
ただの拡張ということで、以前のようにプログラミングできるということなので、
問題なさそうですね。

hajimehoshi

unread,
Jul 3, 2008, 6:46:38 AM7/3/08
to Star Ruby
星です。

現状 (trunk にあがっているもの) の状態をお知らせします。
問題なければ次のバージョンである 0.3 にそのまま含める予定です。

* 変更点の要点

** Game のクラス化

Game がモジュールからクラスになりました。

** Game.run の変更

いままで:

Game.title = "foo"
Game.fps = 30
Game.run(320, 240) do
Game.terminate if ...
...
end

であったのが

Game.run(320, 240,
:title => "foo", :fps => 30) do |game|
break if ...
...
end

となります。
game は Game クラスのオブジェクトです。
Game.terminate は非推奨になりました。 break を使ってください。

今までのものも動作しますが、非推奨である旨の警告が出ます。

** ループの分解

Game.run は、実質以下と同様になります (各メソッドの詳細は後述)。

def Game.run(*args)
g = Game.new(*args)
begin
g.wait
g.update_state
break if g.window_closed?
yield(g)
g.update_screen
ensure
g.dispose
end
end

* クラスメソッド

** Game.current
現在の Game オブジェクトを取得します。
実行していない場合は nil を返します。

** Game.fps
(非推奨: Game#fps を使ってください。)

** Game.fps=(fps)
(非推奨: Game#fps= を使ってください。)
Game.current が nil の場合は、いったん値が保存され、
次の Game.run/new 時に使用されます。

** Game.new(*args)
(新設)
(ゲームループを自分で制御する際に使います。
それ以外の場合では使う必要はありません。)
新しい Game オブジェクトを生成します。
引数は Game.run と同じです。
ゲーム実行中に Game.new を呼ぶと例外になります。

** Game.real_fps
(非推奨: Game#real_fps を使ってください。)

** Game.run(width, heigth, options = {}){ ... }
オプションに :title と :fps が追加されました。

** Game.running?
(非推奨)

** Game.screen
(非推奨にするかどうか迷っているが、非推奨にしようかな。)

** Game.terminate
(非推奨: break を使ってください。)
Game#window_closed? とは無関係です。

** Game.ticks
変更なし。ミリ秒を取得します。

** Game.title
(非推奨: Game#title を使ってください。)

** Game.title=
(非推奨: Game#title= を使ってください。)
Game.current が nil の場合は、いったん値が保存され、
次の Game.run/new 時に使用されます。

* インスタンスメソッド

** dispose
(ゲームループを自分で制御する際に使います。
それ以外の場合では使う必要はありません。)
ゲームを終了します。

** screen
画面 (Texture オブジェクト) を取得します。

** fps
FPS を取得します。

** fps=(fps)
FPS を設定します。

** real_fps
実際の FPS を取得します。

** title
タイトルを取得します。

** title=(title)
タイトルを設定します。

** update_screen
(ゲームループを自分で制御する際に使います。
それ以外の場合では使う必要はありません。)
画面を更新します。

** update_state
(ゲームループを自分で制御する際に使います。
それ以外の場合では使う必要はありません。)
入力、オーディオの状態を更新します。

** wait
(ゲームループを自分で制御する際に使います。
それ以外の場合では使う必要はありません。)
FPS にあわせて適切にスリープします。

** window_closed?
ウィンドウが閉じられた場合 true を、
それ以外の場合は false を返します。

** window_scale
ウィンドウの大きさの倍率を取得します。

hajimehoshi

unread,
Jul 3, 2008, 6:48:59 AM7/3/08
to Star Ruby
星です。

間違えた、訂正

** ループの分解

Game.run は、実質以下と同様になります (各メソッドの詳細は後述)。

def Game.run(*args)
g = Game.new(*args)
begin
loop do
g.wait
g.update_state
break if g.window_closed?
yield(g)
g.update_screen

ISA

unread,
Jul 3, 2008, 1:44:26 PM7/3/08
to Star Ruby
とても良いと思います。

Game.screenがGame.runの前に取得できなかったので、Scene.screenが初期化できなかったんですよね。
それを取るために初回にのみ回るProcを作っていました。
こういう形になればもっと素直に実装できそうです。

それぞれのSceneは
> loop do
> g.wait
> g.update_state
> break if g.window_closed?
> yield(g)
> g.update_screen
> end
のループさえ記憶していれば良いだけになりますし。


描画が重い場合のフレームスキップも、
renderイベントだけではなくGame#update_screenの呼び出し自体を飛ばせるのでFPSを確保しやすくなりそうですね。


Game#waitですが、LifeGame等はfpsに関係なく最高速度で処理したいと思うので呼ばなくても良くなりますね。
それ以外では、アニメーションや台詞のスキップなんかで使用されるんでしょうか。


Game#window_scaleはどういう場合に使われるのでしょう?
320*240のゲーム画面をフルスクリーン表示した際などでしょうか?


既存のコードに影響はほとんどないと思うので、分離するのは良いことだと思います。
というか、一番嬉しいのはフレームワーク側だったり。

Hajime Hoshi

unread,
Jul 3, 2008, 2:01:16 PM7/3/08
to star...@googlegroups.com
星です。

> とても良いと思います。

どうもありがとうございます。

> Game.screenがGame.runの前に取得できなかったので、Scene.screenが初期化できなかったんですよね。
> それを取るために初回にのみ回るProcを作っていました。
> こういう形になればもっと素直に実装できそうです。
>
> それぞれのSceneは
>> loop do
>> g.wait
>> g.update_state
>> break if g.window_closed?
>> yield(g)
>> g.update_screen
>> end
> のループさえ記憶していれば良いだけになりますし。

なるほど。
スクリーンはいままで入りえなかった場所 (def Game.run(...) と loop do ... end との間) で
取得できますね。

> 描画が重い場合のフレームスキップも、
> renderイベントだけではなくGame#update_screenの呼び出し自体を飛ばせるのでFPSを確保しやすくなりそうですね。

確かにそうですね。
あとは Game.tick を使って自分でフレームレート管理ができますね。

> Game#waitですが、LifeGame等はfpsに関係なく最高速度で処理したいと思うので呼ばなくても良くなりますね。
> それ以外では、アニメーションや台詞のスキップなんかで使用されるんでしょうか。

あまりそこらへん考えていませんでしたが (笑)、
irb から使うなど「FPS が関係ない」場合に使えるのではないかと思いました。

> Game#window_scaleはどういう場合に使われるのでしょう?
> 320*240のゲーム画面をフルスクリーン表示した際などでしょうか?

0.2 にも window_scale オプションは Game.run メソッドにすでにあります。
ウィンドウを単純に拡大したい場合に使います。
単純な拡大なのでドットが立ち、そういう低解像度ゲームが好きな人にはよいかと。

> 既存のコードに影響はほとんどないと思うので、分離するのは良いことだと思います。
> というか、一番嬉しいのはフレームワーク側だったり。

なるほど。
そういうご意見は貴重 (Star Ruby のユーザーが少ない上、フレームワーク作成者はもっと少ない) ので
とてもありがたいです。

--
Hajime Hoshi <hajim...@gmail.com>

Reply all
Reply to author
Forward
0 new messages