クソアプリ Advent Calendar 2021 (カレンダー2)8日目の記事です。
歴史あるクソアプリアドベントカレンダーに初参加(Qiitaに初投稿)です。
#はじめに
私は気づいた。
日々の食事で使う3つの道具。
箸、スプーン、フォーク。
この3つはある法則で「変貌」する・・・。
「バシ!」「ズブ!」「ブォグ!」
そう、濁点をつけると、完全に 打撃音 なのだ。
#作ったもの
↓Android版はこちらからダウンロードできます(レビューしてほしい)!
紹介画像がなかなかクソですね。
↓こちらからはブラウザで遊べます。PCやiPhoneの方はこちらで遊んでみてください!
↓ブラウザ版のQRコード
モバイルブラウザ版では私の端末だとBGMを流すとガクッとフレームレートが落ちたので、デフォルトでBGMだけオフになっています。中〜高スペックな方は「音設定」からオンにしてください。
iOS版も出したいです。
#技術的なこと
##Go言語+2DゲームライブラリEbiten製
このゲームはGo言語+2DゲームライブラリEbitenで作りました。
EbitenはAPIのシンプルさをとにかく追究して開発されている、とてもクールなライブラリです。
なんと、
- Windows
- macOS
- Linux
- Android
- iOS
- WebAssembly
- Nintendo Switch
で動作するというマルチプラットフォーム対応です。
ゲームの本体部分はGoで記述し、データの保存やSNSシェア機能などのごく一部を各プラットフォームごとに実装することで、Androidネイティブ版とWebアプリ版(WebAssembly)を同時にリリースすることができました。
##ポイント
ネイティブとWebAssemblyの両方で動かすときに使った知識などです。
###出力したWebAssemblyのファイルを圧縮してアップロードする
Go言語では、
GOOS=js GOARCH=wasm go build -o sample.wasm github.com/username/sample
のようにGOOS=jsとGOARCH=wasmを指定するだけで簡単にWebAssemblyのバイナリを出力できるのですが、このゲームをビルドすると.wasmファイルのサイズが9MBくらいと結構大きくなってしまいます(工夫すればもっと小さくできるのかもしれません)。
そのため、gzipコマンドを用いてwasmを圧縮してアップロードし、ユーザーがサイトをロードした時にユーザーの端末上でpako.jsで展開してもらうことにしました。これにより読み込み時間が短縮されるみたいです。
出力したwasmファイルを
gzip sample.wasm
で圧縮し、sample.wasm.gzを
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>golang-wasm-sample</title>
<script src="wasm_exec.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pako/2.0.2/pako.min.js"></script>
</head>
<body>
<script>
(async function loadAndRunGoWasm() {
const go = new Go();
const buffer = pako.ungzip(await (await fetch("sample.wasm.gz")).arrayBuffer());
if (buffer[0] === 0x1f && buffer[1] === 0x8b) {
buffer = pako.ungzip(buffer);
}
const result = await WebAssembly.instantiate(buffer, go.importObject);
go.run(result.instance);
})()
</script>
</body>
</html>
こんな感じで読み込むことができます。
###go:embedでアセット読み込み
画像(png)、効果音とBGM(mp3)、フォントファイル(ttf)をそのままGoのコードでロードするのではなく、Go公式の外部ファイル埋め込み機能であるgo:embedを使用しています。プラットフォームごとにファイルの読み込みの処理を書く必要はありません。
例えば
├── se
│ ├── se_1.mp3
│ └── se_2.mp3
├── image
│ ├── img_1.png
│ └── img_2.png
├── font
│ ├── font_1.ttf
│ └── font_2.ttf
├── resource.go
├── main.go
のような構造の時、
var images []*ebiten.Image
imagesFS, err := imagesDir.ReadDir("image")
if err != nil {
log.Fatal(err)
}
for i, fs := range imagesFS {
if !fs.IsDir() {
file, err := imagesDir.ReadFile("image/" + fs.Name())
if err != nil {
log.Fatal(err)
}
r := bytes.NewReader(b)
p, err := png.Decode(r)
if err != nil {
log.Fatal(err)
}
images = append(images, ebiten.NewImageFromImage(p))
}
}
こんな感じで読み込めます。サウンドやフォントも同様です。作った画像などをそのままフォルダにコピペするだけで読み込めるので便利です。
###効果音のレコーディング
もはやこのゲームの最大のセールスポイントである「バシ!」「ズブ」「ブォグ!」の効果音は作者の私の生声を採用しています。
レコーディング機材として、Windowsのノートパソコンにデフォルトで入っている「ボイスレコーダー」とPC内蔵マイク、そして普通の部屋を用意しました。
レコーディングではいろいろな言い方やトーンを試しました。なかなかいい出来だと思います。
#最後に
コンセプトの軽さ、デザインや実装の未熟さはありますが、やはりゲームはリリースして遊んでもらうまでが大事ということで、今回クソアプリアドベントカレンダーに投稿させていただきました。
記事を出す日までに完成させるという締め切りがあるとモチベーションがあがりやすくていいですね。
GoとEbitenでのゲーム開発についても何か詳しく書きたいと思いました。
ありがとうございました。