LÖVE (Love2D) 入門編:7.画像の一部を使う (Quad)

(2018.2.24. 公開) (2018.8.26. 対象をバージョン 11 系列に変更)

1. テクスチャアトラス

 ゲームでは、小さな画像をたくさん使うことが多い。これらの画像を平面上に並べて1枚の画像にまとめたものを「テクスチャアトラス」と呼ぶ。たとえばこういうもの。

 LÖVE でテクスチャアトラス中の画像を表示するには、テクスチャアトラスを1枚の画像として読み込んだ上で、その画像と「画像内の位置とサイズ」を表すデータを合わせて love.graphics.draw() に渡す。

atlas = love.graphics.newImage("atlas.png") -- テクスチャアトラスを読み込む
quad = love.graphics.newQuad(0, 0, 32, 32, atlas:getDimensions())  -- Quad データを作成
love.graphics.draw(atlas, quad, 0, 0) -- テクスチャアトラスの指定位置を表示

 「画像内の位置とサイズ」を表すデータを Quad と呼び、love.graphics.newQuad() で作成する。

love.graphics.newQuad(x, y, w, h, sx, sy)

 (x, y) は画像内の位置(左上が (0, 0))、(w, h) は画像内のサイズ、(sx, sy) はテクスチャアトラス全体のサイズ。テクスチャアトラス全体のサイズが必要なのは、OpenGL での描画命令を作成する時に使われるため。

2. 実際に使ってみる

 下のようなマージャン牌の画像データがあるとする。実寸は 512x256 ピクセルの png ファイルです。

(このデータはゲームデザイン (http://www.gamedesign.jp/) ご提供の「フリー素材」からいただきました。現在はリンクがなくなっているようです。)

 この中に34種類の牌が含まれていて、1個あたり 44x62 ピクセルとなっている。そこで、牌1種類ごとに Quad を1つずつ作ることにする。

quads = {}                                   -- Quad のテーブル
image = love.graphics.newImage("tiles.png")  -- テクスチャアトラスを読み込む
wid0, high0 = image:getDimensions()          -- アトラスのサイズ (512x256)
wid = 44                                     -- 画像1つの幅
high = 62                                    -- 画像1つの高さ
for i = 1, 34 do                             -- 34個分
  local x = wid * ((i - 1) % 9)              -- アトラス中の x 座標
  local y = high * (math.floor((i - 1) / 9)) -- アトラス中の y 座標
  quads[i] = love.graphics.newQuad(x, y, wid, high, wid0, high0)
end

 ランダムに選んだ牌の絵柄で画面を埋め尽くしてみる。

-- サンプルプログラム 7-01 main.lua
-- tiles.png が必要
function love.load()
  quads = {}                                   -- Quad のテーブル
  image = love.graphics.newImage("tiles.png")  -- テクスチャアトラスを読み込む
  wid0, high0 = image:getDimensions()          -- アトラスのサイズ (512x256)
  wid = 44                                     -- 画像1つの幅
  high = 62                                    -- 画像1つの高さ
  for i = 1, 34 do                             -- 34個分
    local x = wid * ((i - 1) % 9)              -- アトラス中の x 座標
    local y = high * (math.floor((i - 1) / 9)) -- アトラス中の y 座標
    quads[i] = love.graphics.newQuad(x, y, wid, high, wid0, high0)
  end
end

function love.draw()
  -- t1 = love.timer.getTime()
  for n = 1, 100 do
    sw, sh = love.window.getMode()
    for i = 1, math.floor(sh / high) + 1 do
      for j = 1, math.floor(sw / wid) + 1 do
        love.graphics.draw(image, quads[math.floor(math.random() * 34) + 1], (j - 1) * wid, (i - 1) * high)
      end
    end
  end
  -- t2 = love.timer.getTime()
  -- print(t1, t2, t2-t1)       -- 時間を測る(テスト用)
end

 次のようになる。

 牌の絵柄を1つずつ love.graphics.newImage() で読み込むのと何が違うのだろうか。試しに、上のコードを次のように書き換えてみる。牌の画像データ tile01.png, tile02.png,...こちらに置いてあります。

-- サンプルプログラム 7-02 main.lua
-- tile01.png, tile02.png,... が必要
function love.load()
  images = {}                                  -- イメージのテーブル
  wid = 44                                     -- 画像1つの幅
  high = 62                                    -- 画像1つの高さ
  for i = 1, 34 do                             -- 34個分
    images[i] = love.graphics.newImage(string.format("tile%02d.png", i)) -- イメージを読み込む
  end
end

function love.draw()
  -- t1 = love.timer.getTime()
  for n = 1, 100 do
    sw, sh = love.window.getMode()
    for i = 1, math.floor(sh / high) + 1 do
      for j = 1, math.floor(sw / wid) + 1 do
        love.graphics.draw(images[math.floor(math.random() * 34) + 1], (j - 1) * wid, (i - 1) * high)
      end
    end
  end
  -- t2 = love.timer.getTime()
  -- print(t1, t2, t2-t1)       -- 時間を測る(テスト用)
end

 画像表示の時に使う glBindTexture() は遅い、という説がある。絵柄が別々の画像になっていたら、その都度 glBindTexture() を呼ぶから、実行時間に差が出そうである。そこで、牌の絵柄を書く部分の時間を測ってみた。100回ループして love.timer.getTime() の差をとり、1/100 倍して求めたもの。

 Mac だとかなり差があるけど、ラズパイだとあんまり変わらんなあ。

3. 色をつける

 love.graphics.draw() の前に love.graphics.setColor() で色を指定すると、画像に色がつく。これはテクスチャアトラスでなくて普通の画像でも有効。

-- サンプルプログラム 7-03 main.lua
-- tiles.png が必要
function love.load()
  image = love.graphics.newImage("tiles.png")  -- テクスチャアトラスを読み込む
  wid0, high0 = image:getDimensions()          -- アトラスのサイズ (512x256)
  quad = love.graphics.newQuad(0, 0, 44, 62, wid0, high0)
  love.graphics.setBackgroundColor(0, 0.24, 0)
end

function love.draw()
  love.graphics.setColor(1, 1, 1)
  love.graphics.draw(image, quad, 60, 60)
  love.graphics.setColor(1, 0, 0)
  love.graphics.draw(image, quad, 180, 60)
  love.graphics.setColor(0, 1, 0)
  love.graphics.draw(image, quad, 60, 140)
  love.graphics.setColor(0, 0, 1)
  love.graphics.draw(image, quad, 180, 140)
end

目次