※この文書は、Ruby Tutorial · jlnr/gosu Wikiの日本語訳です。
このチュートリアルの翻訳(中国語、スペイン語、フランス語)へのリンクについては、Homeページを参照してください。
このチュートリアルや他のサンプルのコードやアセット(画像など)はgosu-examples gemに含まれています。
gem install gosu-examples
してから gosu-examples
を実行するだけで試せます。
すべてのGosuアプリケーションはGosu::Windowのサブクラスから開始されます。最小のGameWindowクラスはこのようになります:
require 'gosu'
class GameWindow < Gosu::Window
def initialize
super 640, 480, false
self.caption = "Gosu Tutorial Game"
end
def update
end
def draw
end
end
window = GameWindow.new
window.show
コンストラクタは、Gosu::Window
ベースのクラスを初期化します。ここで示されているパラメータが指定された場合、フルスクリーンでない640×480ピクセルのウィンドウを作成します。また、それまで空だったウィンドウのキャプションを変更しています。
update()
とdraw()
は、Gosu::Window
が持っているメソッドを上書きします。update()
はゲームの主要ロジック(オブジェクトの移動、衝突判定など)が含まれるべきで、(デフォルトでは)毎秒60回呼び出されます。
その後(他の理由でウィンドウの再描画が必要になった場合にも)draw()
が呼ばれますが、FPSが低下している場合は一定時間置きにスキップされる場合もあります。draw()
には画面全体を再描画するためのコードのみが含まれるべきで、ロジックを含めてはいけません。
その下はメインプログラムです。ウィンドウはshow()
メソッドが呼ばれてから作成され、ユーザーかコード自身によって閉じられるまで呼び出し元に返りません。(ジャジャーン!)今、あなたは、あなたがタイトルを選んだ小さな黒いウィンドウを持っています!
メインループの図解は、Window Main Loopのページに示されています。
class GameWindow < Gosu::Window
def initialize
super(640, 480, false)
self.caption = "Gosu Tutorial Game"
@background_image = Gosu::Image.new(self, "media/Space.png", true)
end
def update
end
def draw
@background_image.draw(0, 0, 0)
end
end
Gosu::Image#initialize
は3つの引数を取ります。最初に、ほぼ全てのメディアリソースは、ウィンドウ(self)と繋げられます。Gosuの全てのリソースは初期化するためにウィンドウが必要なので、そのウィンドウへの内部参照を保持します。二番目に、画像ファイルのファイル名が与えられます。三番目の引数には、画像の境界をはっきりさせるかどうかを指定します。詳細はBasicConceptsを参照してください。
前の課題で述べたように、ウィンドウのdraw()
メソッドは描画するための場所なので、背景画像もここで描画します。
引数は、ほぼ明白です。この画像は (0, 0) の位置に描画されます(三番目の引数はZ軸の位置です。再度、Basic Conceptsを参照してください)。
ここに簡単なプレイヤークラスを挙げます。
class Player
def initialize(window)
@image = Gosu::Image.new(window, "media/Starfighter.bmp", false)
@x = @y = @vel_x = @vel_y = @angle = 0.0
@score = 0
end
def warp(x, y)
@x, @y = x, y
end
def turn_left
@angle -= 4.5
end
def turn_right
@angle += 4.5
end
def accelerate
@vel_x += Gosu::offset_x(@angle, 0.5)
@vel_y += Gosu::offset_y(@angle, 0.5)
end
def move
@x += @vel_x
@y += @vel_y
@x %= 640
@y %= 480
@vel_x *= 0.95
@vel_y *= 0.95
end
def draw
@image.draw_rot(@x, @y, 1, @angle)
end
end
これについて、いくつか言うべきことがあります。
- Player#accelerateは、クラスメソッドの
offset_x
とoffset_y
を利用しています。これらはsin/cosと同じように使えます。例えば、何かが30°の角度で100ピクセル移動した場合、それは水平にoffset_x(30, 100)
、垂直にoffset_y(30, 100)
移動したことになります。 - BMPファイルを読み込むとき、Gosuは
0xff00ff
(fuchsia/magenta:みにくいピンク色)を透明なピクセルに置き換えます。 - 注:
draw_rot
は (x, y) に画像の中心を置きます(左上ではありません!)。これはcenter_x
とcenter_y
の引数によって制御できます。 - (当然ながら)プレイヤーは、z=1、つまり背景の上に描画されます。この数値はいつでも変更できます。
- また、全ての描画メソッドと引数については、rdocを参照してください。
class GameWindow < Gosu::Window
def initialize
super(640, 480, false)
self.caption = "Gosu Tutorial Game"
@background_image = Gosu::Image.new(self, "media/Space.png", true)
@player = Player.new(self)
@player.warp(320, 240)
end
def update
if button_down? Gosu::KbLeft or button_down? Gosu::GpLeft then
@player.turn_left
end
if button_down? Gosu::KbRight or button_down? Gosu::GpRight then
@player.turn_right
end
if button_down? Gosu::KbUp or button_down? Gosu::GpButton0 then
@player.accelerate
end
@player.move
end
def draw
@player.draw
@background_image.draw(0, 0, 0);
end
def button_down(id)
if id == Gosu::KbEscape
close
end
end
end
見ての通り、キーボードとゲームパッドの入力を取り込んでいます!
update()
およびdraw()
と同様に、Gosu::Window
は上書き可能な2つのメソッドbutton_down(id)
とbutton_up(id)
を提供しており、デフォルトでは何もしません。ここでは、ユーザーがESCを押すとウィンドウが閉じるようにしています。(あらかじめ定義されたボタン定数の一覧については、rdocを参照してください)。
button_down(id)
やbutton_up(id)
でボタンが押されたことを検知している間は、ジャンプやタイプ、対話的なUIなどの単発イベントにするのが適切で、ボタンを押しながら移動するなどの複数のフレームにまたがった動作には適していません。そのような用途でプレイヤーの動作メソッドを呼び出すには、update()メソッドを使用します。この課題のコードを実行すれば、あなたは飛び回ることができるはずです!
まず、Z軸の位置のマジックナンバーを取り除き、以下の定数に置き換えます。
module ZOrder
Background, Stars, Player, UI = *0..3
end
アニメーションとは何でしょうか?それは、一連の画像です。我々は、それらを格納するために、Rubyの組み込み配列を使います。(実際のゲームでは、個々のニーズに合わせてクラスを作成しなければなりませんが、今は簡単な方法を採ります。)
この課題の中心的なオブジェクトである星たちを紹介しましょう。星たちは画面上のランダムな場所にどこからともなく現れ、プレイヤーが収集するまでアニメーションしながら生存します。星クラスの定義はかなり単純です。
class Star
attr_reader :x, :y
def initialize(animation)
@animation = animation
@color = Gosu::Color.new(0xff000000)
@color.red = rand(256 - 40) + 40
@color.green = rand(256 - 40) + 40
@color.blue = rand(256 - 40) + 40
@x = rand * 640
@y = rand * 480
end
def draw
img = @animation[Gosu::milliseconds / 100 % @animation.size];
img.draw(@x - img.width / 2.0, @y - img.height / 2.0,
ZOrder::Stars, 1, 1, @color, :add)
end
end
個々の星について何度もアニメーションを読み込みたくないので、アニメーションの読み込みは星クラスのコンストラクタでは行えず、代わりにここではないどこかから渡すようにします。(このウィンドウはアニメーションを三段階で読み込みます。)
星のアニメーションの異なるフレームを100ミリ秒ごとに表示するため、Gosu::milliseconds
によって返された時間を100で割って、フレーム数の剰余を取ります。この画像は、星の位置を中心にコンストラクタで生成されたランダムな色で付加的に描画されます。
では、プレイヤーに配列からできた星を絶えず収集するための簡単なコードを追加してみましょう。
class Player
...
def score
@score
end
def collect_stars(stars)
if stars.reject! {|star| Gosu::distance(@x, @y, star.x, star.y) < 35 } then
@score += 1
end
end
end
さあ、アニメーションを読み込み、新しい星を生成し、プレイヤーがそれらを収集し、そして残りのオブジェクトが描画されるようにウィンドウを拡張しましょう。
...
class Window < Gosu::Window
def initialize
super(640, 480, false)
self.caption = "Gosu Tutorial Game"
@background_image = Gosu::Image.new(self, "media/Space.png", true)
@player = Player.new(self)
@player.warp(320, 240)
@star_anim = Gosu::Image::load_tiles(self, "media/Star.png", 25, 25, false)
@stars = Array.new
end
def update
...
@player.move
@player.collect_stars(@stars)
if rand(100) < 4 and @stars.size < 25 then
@stars.push(Star.new(@star_anim))
end
end
def draw
@background_image.draw(0, 0, ZOrder::Background)
@player.draw
@stars.each { |star| star.draw }
end
...
これで、星を集めることができるようになりました!
最後に、ビットマップフォントを使用して今のスコアを描画し、プレイヤーが星を収集したときにビープ音を再生したいと思います。ウィンドウがテキストを扱えるように、高さが20ピクセルのフォントを読み込みます。
class Window < Gosu::Window
def initialize
...
@font = Gosu::Font.new(self, Gosu::default_font_name, 20)
end
...
def draw
@background_image.draw(0, 0, ZOrder::Background)
@player.draw
@stars.each { |star| star.draw }
@font.draw("Score: #{@player.score}", 10, 10, ZOrder::UI, 1.0, 1.0, 0xffffff00)
end
end
プレイヤーに残されたのは何でしょうか?正解は、スコアがカウントされたらサウンドを読み込んで再生することです。
class Player
attr_reader :score
def initialize(window)
@image = Gosu::Image.new(window, "media/Starfighter.bmp", false)
@beep = Gosu::Sample.new(window, "media/Beep.wav")
@x = @y = @vel_x = @vel_y = @angle = 0.0
@score = 0
end
...
def collect_stars(stars)
stars.reject! do |star|
if Gosu::distance(@x, @y, star.x, star.y) < 35 then
@score += 10
@beep.play
true
else
false
end
end
end
end
ご覧のように、サウンドや効果音を再生するのはこの上なく簡単です!サウンドを再生するためのより強力な方法(ボリューム周りや、再生位置と速度)については、RDocを参照してください。
以上です!あとはあなたの想像力にかかっています。もし、ゲームを作るのに十分な想像ができないなら、Gosu Showcase boardの例を見てみましょう。