Playdate SDKにはパーリンノイズに関するAPIもあります。
パーリンノイズを使うと上のキャプチャのように濃淡に連続性がある描画を行うことができます。
今回はパーリンノイズを使ってフィールドのタイルマップを生成してみます。
タイルマップの画像データは縦横20pxの2x2でレイアウトされた下の画像を使います。
tile-table-20-20.png
という名称でsource/Images
に格納してください。
import "CoreLibs/object"
import "CoreLibs/graphics"
import "CoreLibs/sprites"
import "CoreLibs/timer"
local gfx <const> = playdate.graphics
-- タイル
local tilesize <const> = 20
local tileCol = 400 / tilesize
local tileRow = 240 / tilesize
local tileTable = {
1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1
}
-- パーリンノイズ
-- cf. https://chrisdownie.net/software/2022/03/31/playdate-perlin-noise/
local repeatValue = 0 -- 繰り返さない
local octaves = 5 -- 5つのPerlin値を組み合わせる
local persistence = 0.5 -- 各値は、前の値の半分の重さとして計算
-- tableから最小値最大値取得
local function getMinMaxNumTable(table)
local key, min, max = 1, table[1], table[1]
for k, v in ipairs(table) do
if table[k] < min then
min = v
elseif table[k] > max then
max = v
end
end
return min, max
end
function myGameSetUp()
-- タイルセッティング
local tileImageTable = gfx.imagetable.new("Images/tile")
local tileMap = gfx.tilemap.new()
tileMap:setImageTable(tileImageTable)
tileMap:setTiles(tileTable,tilesize)
-- tileCol, tileRow = tileMap:getSize() -- 正確に縦横列の数を取得したい場合はtileMap:getSize()で代入しても良い
-- パーリンノイズ配列作成
local perlinNoiseTable = gfx.perlinArray(tileCol * tileRow,
0.5, 1,
0.5, 0,
0, 0,
repeatValue,
octaves,
persistence)
local min, max = getMinMaxNumTable(perlinNoiseTable)
local thread = (max - min)/4
local thread1 = min + thread
local thread2 = min + thread*2
local thread3 = min + thread*3
local thread4 = min + thread*4
for j=1, tileRow do
for i=1, tileCol do
local tileIndex = 1
local index = (j-1)*tileCol + i
if perlinNoiseTable[index] < thread1 then
tileIndex = 1
elseif perlinNoiseTable[index] < thread2 then
tileIndex = 2
elseif perlinNoiseTable[index] < thread3 then
tileIndex = 3
elseif perlinNoiseTable[index] < thread4 then
tileIndex = 4
end
tileMap:setTileAtPosition(i, j, tileIndex)
end
end
gfx.sprite.setBackgroundDrawingCallback(
function( x, y, width, height )
tileMap:draw(0, 0)
end
)
end
myGameSetUp()
function playdate.update()
gfx.sprite.update()
playdate.timer.updateTimers()
end
まず、タイルに関する変数を記述します。
タイルのサイズ、横・縦列のタイル数、タイルの表示情報のtableを記述しています。tableの値はタイル画像のインデックス値(1〜4)が入ります。
次にパーリンノイズの変数を記述しています。playdateにおけるパーリンノイズの説明については以下ページに詳細が記載されています。
先にmyGameSetUp
関数に記載されているスクリプトから説明します。
タイルパーツとなる2x2のタイル画像をgfx.imagetable.new("Images/tile")
で読み込みます。
次にgfx.tilemap.new()
でタイルマップオブジェクトを生成し、tileMap:setImageTable(tileImageTable)
でタイルのimageTableをセットします。
tileMap:setTiles(tileTable,tilesize)
ではタイルの表示情報のtableとタイルのサイズをセットしています。
次にパーリンノイズ配列を作成します。
gfx.perlinArray([個数], x, dx, y, dy, z, dz, [繰り返し回数],[オクターブ],[持続性])
で指定したパラメータの結果を0〜1の範囲で数値の配列を返します。
続いて生成したパーリンノイズの配列の最小値と最大値を前行で作成したgetMinMaxNumTable
関数を使って取得します。
この取得した最小値と最大値を4つの範囲に区切り、範囲内の値を元にタイル画像のインデックス値を割り振ります。(愚直にやってるthread1〜4が範囲の区切り)
割り振ったタイル画像のインデックスは、tileMap:setTileAtPosition(i, j, tileIndex)
でタイル画像を再設定します。
最後にtileMap:draw(0, 0)
でタイルマップの描画を行います。