playdateを注文したけど届くのずっと先なのでゲームを作って待つことにした

以前から気になっていたガジェット「playdate」がついに発売されました。
小さくて画面はモノクロ、十字キーにABボタン、初代のゲームボーイのような見た目ですが、手回しのハンドルがついている風変わりなゲーム機です。
開発は「いたずらガチョウがやって来た」のゲームで有名なPanicさん。このゲームはプレイしたことはないけどYoutubeでプレイ動画を見て家族で和んでました。面白いものを作ってくれました。
ゲーム自体はカセットを買って差し込むわけではなく、はじめの12週間は毎週2本ずつ新しいゲームを受け取れるようです。Wi-Fiがついているのでおそらくそれ経由でインストールされるのだと思います。
その後のことは今準備中とのことですが、僕が一番このガジェットで気に入っているのは「ゲームを自分で作れる」ことです。
開発者向けサイトからSDKを手に入れれば、ゲームを自分で作ることができ、また作ったゲームをシェアすることができます。
このガジェットを見つけたのは数年前だったけど、発売が延期になり忘れてました。ですが最近出荷が開始された記事を読んで、また興味が湧いてきました。
でも、買ってもすぐに飽きて使わなくなってしまうのは良く無いので、買う前に簡単なゲームを作ってみて面白いかどうか試してみることにしました。
ゲーム開発はいくつか方法があります。
はじめは簡単そうなpulpを試しました。これはブラウザ上で動く開発環境で、プログラム以外にもグラフィックや音楽も作れるかなり強力なツールです。最初に触ってみて雰囲気を掴むにはいいと思います。
キャラクターを操作してフィールドを歩きまわるような初代ゼルダの伝説のようなゲームはこれで作れるかもしれません。
一番最初のゲームはこれで作ってみるつもりでしたが、pulpが想定した形のゲームじゃないと難しいことがわかりました。(例えば、必ずプレイヤーが操作するプレイヤーが1人画面上にいなければならないなど)

次に、Lua向けのSDKをダウンロードしました。C言語のほうがパフォーマンスが出るようですが、まだそこまでは求めてないし、サクッと簡単に開発できそうなほうにしました。
Luaは触るのはほぼ初めてです。昔Ultima Onlineをやってた時、Luaでアプリをカスタマイズできるというのを聞いて手を出そうとしたのですが、その時はうまくいきませんでした。Lua再入門です。
と言っても、ググりながら書けば問題ないです。array と table が同じなのはいいけど、indexが1から始まるのには面食らいましたw
教材はSDKについてるサンプルコードとリファレンスです。
サンプルコードはよくできています。でも読んでる時間が惜しいので目ぼしいところだけ参考に自分で書きました。
(今見てみたら、チュートリアルがありました。前からあったのかな?)
リファレンスが頼みの綱ですが、正直ちょっと分かりにくいです。
一つ一つはわかるけど、全体のつながりがわかりにくい。サウンド関連がたくさんありすぎて、どれを使ったらいいのかはじめわかりませんでした。(今もたぶんわかってないです)
とりあえず、MIDIデータを track につっこんで、それを sequence につっこんでplayすれば音が鳴るのでそれでBGMもSEもまかなってます。
例えばこんな感じです。JSONでMIDIデータを書いてます。step は、タイムラインのフレームのようなもので、デフォルトでは 16step/秒 です。noteはドレミです。72はC5、つまり5オクターブのドの音です。
synth = playdate.sound.synth.new()
track = playdate.sound.track.new()
dat = '[{"note":76,"length":1,"velocity":1,"step":3},{"note":74,"length":1,"velocity":1,"step":2},{"note":72,"length":1,"velocity":1,"step":1}]'
track:setNotes(json.decode(dat))
track:setInstrument(synth)
seq:addTrack(track)
seq:play()
mp3やwavが扱えるので、普通はガレージバンド等で作って読み込んだ方が楽なんでしょうが、せっかくレトロ風なガジェットなのでファミコン風の音を鳴らしたいし(ガレージバンドでも8bit音源で出来ますが)、プログラムで音を作るのも楽しそうなのでそうしました。
あまり凝ったことができない分、割り切って開発できるのでそれはそれでいいです。ガレージバンドが面白くなっちゃうといつまでもゲームが完成しないのでw
ただ、このJSONを作るのは、手打ちだとやりにくかったので、Google Spreadsheet に打ち込んで Apps Script を書いて JSON を書き出すようにしました。


実行するとクリップボードにコピーされるので、それをコードに貼り付けます。

Code.gs
function main() {
var html = HtmlService.createTemplateFromFile("dialog").evaluate();
SpreadsheetApp.getUi().showModalDialog(html, 'Playdate MIDI Data Maker');
}
function calc() {
var sheet = SpreadsheetApp.getActiveSheet();
var values = sheet.getDataRange().getValues();
var idxS = 0, idxE = 0;
for (var i = 0; i < values[1].length; i++) {
if (values[1][i] === 'S') {
idxS = i;
}
else if (values[1][i] === 'E') {
idxE = i;
}
}
var noteList = [];
for (var i = 2; i < values.length; i++) {
var noteName = values[i][2];
if (noteName === '') continue;
var noteData = null;
for (var j = idxS; j <= idxE; j++) {
if (values[i][j] !== '') {
if (noteData === null) {
noteData = {
note: noteName,
length: 0,
velocity: 1,
step: j - idxS + 1
}
}
noteData.length++;
}
if ((values[i][j] === '' || j === idxE) && noteData !== null) {
noteList.push(noteData);
noteData = null;
}
}
}
var json = JSON.stringify(noteList);
console.log(json);
return json;
dialog.html
<!DOCTYPE html> <html> <head> <base target="_top"> </head> <body> <button onclick="onCopyButtton()">Copy to Clipboard</button> <script type='text/javascript'> function onCopyButtton() { var content = <?= calc(); ?>; console.log('content', content); navigator.clipboard.writeText(content) .then(() => { console.log("Text copied to clipboard...") }) .catch(err => { console.log('Something went wrong', err); }) } </script> </body> </html>
あと、コードエディタですが、nova を使っています。これはpanicが開発したアプリで、playdate専用というわけではないようですが、playdateのシミュレーターと連携しているので開発しやすそうです。
ただ、有料ですw
初回は ¥11,800で、その後は1年ごとに¥5,900/年だそうです。
今は30日間のお試し期間中なので、終わったらどうするか考えます。VSCode用の機能拡張もあるようなので、後日それも試してみます。
なんとか1個目のマインスイーパー的な(ほぼそれw)ゲームができました。

そして注文しました。
年末に発送の予定ですが、あまり期待せず来年くるつもりで待っています。
それまで何本つくれるかな?
良かったこと
- モノクロのドット絵なので、ポチポチ書くだけでまあそれなりの見た目になる
- 懲りたくても限界があるので、いい意味で割り切って作業できる。つまり内容に集中できる
- 大作を作ろうなんて気負わず、サクッと作れるのでいい気晴らしになる
- Luaにclassはないけどclass風なことはできるので、たいていなんとかなりそう
次は何を作ろうか