作ったもの: https://ebiten-icecream-game.vercel.app/
スクショ↓
デプロイ編ってあるけど今のところこれだけ。あとでコード編も書くかも?
背景/お気持ち
Golang さわりたかったんだけど、TODO アプリ作るにはモチベが足りなかったので、とりあえず ebitengine 触ってみた。1個目は ChatGPT と Cursor に土台ほぼ書いてもらってこまかいところだけ修正してたんだけど、さすがに学んだ感がゼロだったので、ChatGPT の壁打ちだけで書いてみた。
画像系は ChatGPT (というか DALLE) と Figma でつくって、
iPad を開くのすら面倒だったので Figma で画像つくってみたけど、意外と行けた。
サウンドはフリーのやつをとってきた。
今回やれなかった系でいくと、ヒット時のエフェクトがあるとかっこよかったかも。あとクリア条件がなくて永遠に数字が上がっていくので、これは後で何か考える...予定。
言語面だと、 ChatGPT が「今後これ学んでいけばいいんじゃない」って言ってた。
(とりあえず) デプロイ
↓の記事を見ると Vercel にデプロイできるとあったので Vercel にした
ただ、とりあえずさっさと上げたくて express サーバを立てることすら面倒だったので、以下の手順でやった。
事前作業: 画像とかを embed
する
まず、ファイルを os.OpenFile("/path/to/file")
でやっている場合、ブラウザで動かないので embed
パッケージを使ったやり方に書き換える。 embed
を使うと、ファイルではなくデータが返ってくるので、周りの関数もちょっと書き換えが必要。
audio, image, font の書き方はだいたいこんな感じ。(既存コードを整形して動作確認してないのでガバあるかも)
package main import ( "embed" _ "embed" "log" "bytes" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/audio" "github.com/hajimehoshi/ebiten/v2/audio/mp3" "github.com/hajimehoshi/ebiten/v2/ebitenutil" "github.com/hajimehoshi/ebiten/v2/text/v2" ) var ( audioContext *audio.Context //go:embed assets/fonts fontsDir embed.FS //go:embed assets/images imagesDir embed.FS //go:embed assets/sounds soundsDir embed.FS ) func loadImage(filepath string) *ebiten.Image { data, err := imagesDir.ReadFile(filepath) if err != nil { log.Fatal(err) } img, _, err := ebitenutil.NewImageFromReader(bytes.NewReader(data)) if err != nil { log.Fatal(err) } return img } func loadAudio(filepath string) *audio.Player { data, err := soundsDir.ReadFile(filepath) if err != nil { log.Fatal(err) } // Decode the MP3 file stream, err := mp3.DecodeWithSampleRate(audioContext.SampleRate(), bytes.NewReader(data)) if err != nil { log.Fatal(err) } // Create an audio player player, err := audioContext.NewPlayer(stream) if err != nil { log.Fatal(err) } return player } func loadFontSource(filepath string) *text.GoTextFaceSource { data, err := fontsDir.ReadFile(filepath) if err != nil { log.Fatal(err) } source, err := text.NewGoTextFaceSource(bytes.NewReader(data)) if err != nil { log.Fatal(err) } return source }
ローカルでビルド
(1) public/
ディレクトリをつくる
. ├── README.md ├── main.go ├── ... (other go files) └── public
(2) Go ファイルのビルド
↓を見ながらやりつつ、 public/
フォルダに作られるようにする
# build the wasm file env GOOS=js GOARCH=wasm go build -o public/icecream-game.wasm # copy wasm_exec.js to execute the WebAssembly binary cp $(go env GOROOT)/misc/wasm/wasm_exec.js public/
(3) HTML ファイルの作成
main.html
と index.html
の2つをつくる。ファイル名とか iFrame のサイズは適宜変えてね。
main.html
<!DOCTYPE html> <style> canvas { width: 480px; height: 640px; } </style> <script src="wasm_exec.js"></script> <script> const go = new Go(); WebAssembly.instantiateStreaming(fetch("icecream-game.wasm"), go.importObject).then(result => { go.run(result.instance); }); </script>
index.html
(フォントとかは完全に趣味なのでよしなに変えてね)
<!DOCTYPE html> <html> <head> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Cherry+Bomb+One&display=swap" rel="stylesheet"> <style> h1 { font-weight: normal; color: #174359; } h1, p { font-family: 'Cherry Bomb One'; } .container { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; flex-direction: column; } </style> </head> <body> <div class="container"> <h1>Ice Cream Game</h1> <p>tip: use <- and -> to move</p> <iframe src="main.html" width="480" height="640"></iframe> </div> </body>
いちおう main.html
を index.html
として iframe に入れずにサーブしても動くんだが、ゲーム画面がブラウザの最大幅になっちゃって変え方がすぐわからなかったのと、docs に ↓のようにあったので iframe に入れた。
If you want to embed your game into your web page, using iframe is strongly recommended. The screen scale is automatically adjusted. If the above HTML's name is main.html, the host HTML will be like this: ...
(4) 動作確認
# run server cd public python3 -m http.server
(別に python のサーバじゃなくても OK)
(5) Git リポジトリに push
注意点として、通常の frontend 開発とは違って public はコミットするのと、ソースコードを変更したら毎回ビルドを走らせる必要がある。
git add public git push origin main
Vercel の設定
(1) あたらしい project を作成 & Git リポジトリに紐づけ
(2) コマンドの設定
↓のスクショの通りだけど、
- build command も install command もなし (
true
で設定) - output ディレクトリを
public/
二設定
(3) デプロイ
これで動けば OK
改善点
上でもちょろっと書いたけど、
あたりが課題。Vercel で go のコマンド動かせられるのか不明だったのでこのやり方にした。