(2018.2.7. 公開) (2018.8.26. 対象をバージョン 11 系列に変更)
丸とか四角だけでもゲームは作れるけど、やはりキャラクタが表示できた方が面白い。キャラクタを PNG 形式で用意して、使ってみよう。サイズは何でもよい。下の画像は 32x32(2倍拡大で表示しているけど)。
PNG 画像を読み込むには、love.graphics.newImage()
を使う。
fish0 = love.graphics.newImage('fish0.png')
newImage()
の引数 'fish0.png'
は、画像のファイル名。この場合、main.lua
と同じディレクトリに画像ファイルが置いてあることになる。
画像ファイルがたくさんある時は、専用のディレクトリを作る方がいいかもしれない。
progname --- main.lua
|- images --- fish0.png
|- fish1.png
|- ...
この時は、もちろんコードは下のようになる。
fish0 = love.graphics.newImage('images/fish0.png')
注意すべき点は、love.graphics.newImage()
は1つの画像について一度だけ呼び出すようにすること。呼び出すたびに画像データ分のメモリを消費するため、何度も呼び出すとメモリ不足を引き起こす。このため、love.graphics.newImage()
の呼び出しを love.update()
や love.draw()
の中に書いてはいけない。これらのコールバックは、ゲーム中に何度も繰り返し実行されるからである。
シンプルなゲームの場合は、love.load()
の中で、すべてのキャラクタ画像を読み込んでおくのがよい。
function love.load()
fish0 = love.graphics.newImage('fish0.png')
man0 = love.graphics.newImage('man.png')
ghost0 = love.graphics.newImage('ghost.png')
...
end
なお、キャラクタの「周囲」は透明色にしておくこと。うっかり「白」のままにしておくと、下のように四角い枠がついてしまう。
キャラクタのデザインって結構難しいんだよね。探せばフリー素材がいろいろ見つかるので、うまく活用しましょう。絵のうまい友達と仲良くなっておくのも一案です。
上で読み込んだデータは、love.graphics.draw()
で表示することができる。コールバックの love.draw()
と混同しないように。
love.graphics.draw(fish0, x, y)
x, y
は画面上の位置。この場合、キャラクタの左上の位置がちょうど (x, y)
に来るように表示される。
love.graphics.draw()
には面白い機能がある。オプションの引数をつけることで、画像を回転させたり反転させたりできる。
まず、回転させてみよう。画像, x座標, y座標
の次に回転角
を指定することで、画像を回転させて表示できる。回転角はラジアンで指定する(180度をπ=3.14159... として角度を測る単位)。45度は 0.785
、90度は 1.571
となる。図からわかるように、角度が大きくなると時計回りに回転する。
残念なのは、左上を中心に回転させるため、回転させるとキャラクタが左にずれてしまうこと。表示位置を合わせようとすると、かなり面倒な計算をしないといけなくなる。
幸い、love.graphics.draw()
には「画像の中心位置」を指定する機能がある。これを使って、32x32 の画像の真ん中を「中心位置」に設定してみる。
love.graphics.draw(fish0, x, y, 0, 1, 1, 16, 16)
回転角 0
のあとの1, 1
は拡大率(後で説明する)で、そのあとの 16, 16
が「画像の中心位置」。このように指定すると、下のように「画像の中心」の回りに回転させてくれる。ただし、表示位置も「画像の中心」で指定することに注意する。
love.graphics.draw()
で、画像を反転させることもできる。
回転角 0
のあとの-1, 1
が x, y 方向の「拡大率」。これを -1
にすると、その方向が反転される。上の例では、x 方向の拡大率を -1 にして、左右を反転している。
回転の時と同じく、左上を基準にして左右を反転させると、画像が左に寄ってしまう。これを防ぐには、「画像の中心位置」を指定して、下のようにすればよい。
回転と反転を同時に指定したらどうなるのでしょうか。例えば、先ほどのこの状態で、x 方向の拡大率を -1 にしたらどうなるか。「回転してから反転」するなら、右上を向くことになるし、「反転してから回転」するなら、右下を向くことになる。
正解はこちら。つまり、「反転してから回転」が正しい。
拡大縮小・反転・回転が組み合わさっている時は、どういう姿になるのか、考えてもなかなかわかりにくい。こういう時は、実験して確かめる方が早道かもしれない。
拡大率は、もちろん±1である必要はないし、x, y 方向で異なる値でも構わない。
また、「画像の中心位置」の後ろにさらに数値を置くと、「斜め変形」を指定することができる。
斜め変形は少しわかりにくい。下のような変換である。x, y 方向を同時に指定すると混乱するので、どちらか一方だけ指定して、あとは拡大・縮小と回転の組み合わせで欲しい形に変形するとよい。
デモを作ってみました。
-- サンプルプログラム 5-01 main.lua
-- fish0.png が必要
-- 最初に1回だけ呼び出されるコールバック関数
function love.load()
width = love.graphics.getWidth() -- 画面の横幅
height = love.graphics.getHeight() -- 画面の高さ
love.graphics.setBackgroundColor(0, 0.24, 0) -- 背景を濃い緑色に
fish0 = love.graphics.newImage('fish0.png') -- 画像を読み込む
stime = love.timer.getTime() -- スタート時刻を記録
sx = 5 -- x方向の倍率
sy = 5 -- y方向の倍率
rot = 0 -- 回転角
kx = 0 -- x方向の斜め変形
ky = 0 -- y方向の斜め変形
end
-- [0, 1] をシグモイド型に変形する関数
function sigmoid(x, p)
local t = x
if x > 0.5 then t = 1 - t end
t = math.pow(t * 2, p) / 2
if x > 0.5 then t = 1 - t end
return t
end
-- 定期的に呼び出されるコールバック関数
function love.update(dt)
local t = love.timer.getTime() - stime -- スタート時刻からの経過時間
if t < 1 then
rot = 0
elseif t < 13 then
-- 時計回り、速くなって遅くなって 51回転半(51.5*2π) で止まる
rot = sigmoid((t - 1) / 12, 4) * 103 * 3.1416
elseif t < 16 then
-- 反時計回り、ゆっくり1回転半(1.5*2π)
rot = (1 - sigmoid((t - 13) / 3, 2)) * 3 * 3.1416
elseif t < 18 then
-- 小さくなる
sx = 5 * math.cos((t - 16) / 4 * 3.1415927)
sy = math.abs(sx)
elseif t < 21 then
-- 左右を反転して大きくなって画面からはみ出す
sx = -((t - 18) * 3 + 80 * math.pow((t - 18) / 3, 4))
sy = math.abs(sx)
elseif t < 24 then
-- 左右を元に戻して元の大きさに戻る
sx = (5 + (24 - t) * 4 / 3 + 80 * math.pow((24 - t) / 3, 4))
sy = sx
elseif t < 25 then
sx = 5
sy = 5
elseif t < 30 then
-- 横方向拡大・縮小
sx = 5 * math.pow(4, math.sin((t - 25) * 3.1415927 * 2 / 5))
sy = 5
elseif t < 31 then
sx = 5
sy = 5
elseif t < 36 then
-- 縦方向拡大・縮小
sx = 5
sy = 5 * math.pow(4, math.sin((t - 31) * 3.1415927 * 2 / 5))
elseif t < 37 then
sx = 5
sy = 5
elseif t < 42 then
-- 横方向斜め変形
kx = 5 * math.sin((t - 37) * 3.1415927 * 2 / 5)
elseif t < 43 then
kx = 0
elseif t < 48 then
-- 縦方向斜め変形
ky = 5 * math.sin((t - 43) * 3.1415927 * 2 / 5)
else
ky = 0
end
love.timer.sleep(0.01)
end
-- 画面を書き換えるコールバック関数
function love.draw()
local x, y
x = width / 2
y = height / 2
love.graphics.draw(fish0, x, y, rot, sx, sy, 16, 16, kx, ky)
end
目次