Tutorial:Tile-based Scrolling (日本語)

この小型のチュートリアルではかつてゲームの背景を描画するために広く使用されていた技法を網羅しています: タイルマップ。この技法を使用することで、背景は断片 (タイル)から構成を行い、一緒に描画されるため、大量のメモリを消費せずに、巨大かつより複雑なレベルの作成を可能にします。基本概念は二次元値のテーブルから構成されており、各値はタイルを表します。テーブルを使用して、マップの幅、高さ、および仮想ビューポートにおける現在座標といった数種類の基本的な引数を指定することで、どのタイルを指定位置へ描画するかどうかを識別します。これらの用語が理解できるかどうか心配する必要はなく、コーディングを始めるにつれてより詳細な説明をします。 さて、一部の変数にて開始します:

-- タイル
   tile = {}
   for i=0,3 do -- タイル画像の個数を -1 から 3 へ変更します。
      tile[i] = love.graphics.newImage( "tile"..i..".png" )
   end
 
   love.graphics.setNewFont(12)
 
   -- map 変数
   map_w = 20
   map_h = 20
   map_x = 0
   map_y = 0
   map_offset_x = 30
   map_offset_y = 30
   map_display_w = 14
   map_display_h = 10
   tile_w = 48
   tile_h = 48

最初にタイルを格納するための Lua テーブルを作成します。既に言及したとおり、小さな断片から構成される、モザイクのようなものであり、これらはマップ描画するために使用する小型のビットマップです。これは非常に単純なタイルマップであり、4種類のタイルを使用するため、ループの内側で読み込みます。さて、次に行うことは少量のマップおよびタイル変数を設定してマップを正確に描画できるようにすることです。これは説明不要であるべきです: タイルおよびタイルマップの幅と高さに加え、表示を行うタイル領域の大きさがあります。二種類の offset (オフセット) 変数は描画開始地点を定義するために使用されます。結果を見るためにこれらの変数で少し動作させたいと思っているかもしれません。さて、マップ自体の構造は:

map={
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
   { 0, 1, 0, 0, 2, 2, 2, 0, 3, 0, 3, 0, 1, 1, 1, 0, 0, 0, 0, 0},
   { 0, 1, 0, 0, 2, 0, 2, 0, 3, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0},
   { 0, 1, 1, 0, 2, 2, 2, 0, 0, 3, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0},
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
   { 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
   { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
   { 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
   { 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
   { 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
   { 0, 2, 2, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 2, 0, 0, 0, 0, 0, 0},
   { 0, 2, 0, 0, 0, 3, 0, 3, 0, 1, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0},
   { 0, 2, 0, 0, 0, 3, 0, 3, 0, 1, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0},
   { 0, 2, 2, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 2, 2, 2, 0, 0, 0, 0},
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
}


ご覧の通り、ずば抜けた手品はここにはなく、タイル・テーブルの索引を表すために二次元テーブルを定義しています。この用例では 0 は雑草を表し、 1 は泥を表し、 2 は岩石、さらに 3 は水辺を表します。明白に、そのように希望すればより構造の部品であっても、タイルは希望するものを表します。 さて、続けます:

function draw_map()
   for y=1, map_display_h do
      for x=1, map_display_w do                                                         
         love.graphics.draw( 
            tile[map[y+map_y][x+map_x]], 
            (x*tile_w)+map_offset_x, 
            (y*tile_h)+map_offset_y )
      end
   end
end


love.draw の清潔さを維持するために、小型のマップを描画するルーチンを関数にして移動することに決定しました。再び、全く印象的なことはありませんが、各列 (y 値) に対して 各行 (x 値) を確認して、 map テーブルを経由します。 map_display_hmap_display_w 変数を変更するだけで、望むだけタイルを描画することができます。あまりにも高過ぎる値を指定すると画面外にタイルの描画を開始してしまい、処理時間および資源の浪費になることに注意してください。 map_x および map_y の値を変更することにより描画される領域の座標を変更することができます。

明白に、 love.draw コールバックで draw_map() を呼び出す必要があります。さて、用例を実行すると、画面上に小型のタイルマップが表示されるはずです。今のところはスクロールは全くできませんが、 love.keypressed コールバックへ下記を追加することで、できるようになります:

function love.keypressed(key, unicode)
   if key == 'up' then
      map_y = map_y-1
      if map_y < 0 then map_y = 0; end
   end
   if key == 'down' then
      map_y = map_y+1
      if map_y > map_h-map_display_h then map_y = map_h-map_display_h; end
   end
 
   if key == 'left' then
      map_x = math.max(map_x-1, 0)
   end
   if key == 'right' then
      map_x = math.min(map_x+1, map_w-map_display_w)
   end
end


これはかなり単純です: 最初に、私たちは、矢印キーが押されているかどうかの確認を行い、それに応じて map_x および map_y 変数を更新します。留意して欲しいのは、ここではタイル座標の議論ですが、 map_x または map_y を一つずつ増加または減少させる場合、実際は 64 ピクセル (タイルひとつあたりの大きさ) 移動します。最後に、マップ外のスクローリングを回避するのに、なんらかの境界の確認用コードを算入します。 y 座標の境界を確認するための単純な方法を示す一方では x に対する境界の確認には、より簡潔な異形を使用します:

function love.draw()
  draw_map()
end

最後に、実際にタイルを表示するために以前作成した draw_map() を使用します。

さてスクリプトを実行してみましょう。 LÖVE はクールですか? そうであると断言します。初めてのタイルマップ・スクローラー作成おめでとうです! この小さな用例で実行には十分足りますが、非常に多数の拡張可能なものがあります:

  • マップを手打ちすることは少し下らないことであり、 Tiled のようなツールを作成してゲームで読み込むべきです。
  • タイルマップ内に自機を描画する。
  • 素晴らしいスクロール: 恐らく気付いているでしょうが、ピクセルにより自機/オブジェクトのピクセルを移動させる必要がある場合はおそらくは望ましいことではありません (用例)。
  • 多層マップ: 実際のタイルエンジンでは一つ以上の階層を描画することができます。素晴らしいスクローリングと一緒に、特定の速度で近接する階層をスクロールして、その背後にある別のものを、さらにゆっくりした速度で移動させることができます。
  • 自機のマップ衝突判定

関連

素晴らしいタイル・ベースのスクローリング
効率的なタイル・ベースのスクローリング



そのほかの言語