世界を動かす技術を、日本語で。

Goでゲームを作る:LLMなしの3ヶ月とLLMありの3日

概要

  • 15年のソフトウェアエンジニア経験 を活かし、初めてカードゲームを公開
  • GoとReact、WASM を使ったサーバーレス構成で実装
  • LLM(大規模言語モデル)前後の開発速度の違い を体験
  • Tic-Tac-Toeリポジトリ を使ったゲーム開発の最小手順を解説
  • WASMとの連携やトラブルシューティング まで具体的に紹介

15年目の初ゲーム公開体験

  • 15年のソフトウェアエンジニア歴 を持つ筆者が、初めてゲーム開発と公開に挑戦

  • アルゼンチンの伝統的カードゲーム「Truco」 を題材に選定

  • Goでバックエンド を構築、 ReactでUI を最小限実装

  • サーバー不要 を目指し、 TinyGoでWASM化 しGitHub Pagesでホスティング

  • LLM普及前 の時代、全て手探りで3ヶ月かけて完成

  • 広告や収益化は一切せず、純粋に完成を目指した作品

  • 公開から1年経過後も プレイヤーが継続的に遊んでいる実績

    • Truco(ゲーム本体)
    • バックエンド(Go)
    • フロントエンド(React、1時間の学習で実装)

LLM時代の開発「Escoba」:3日で実装

  • 翌年、 家族にEscobaを教えるため に新ゲーム開発を決意
  • LLM(Claude)を活用 し、TrucoのバックエンドをクローンしてEscoba用にリファクタリング
  • 初回プロンプトでほぼ完璧に動作 し、バグ修正も最小限
  • フロントエンドは自力で数日かかったが、 Reactスキルの課題WASMによる状態管理の複雑さ が主な要因
  • デバッグもJavaScriptで苦労
  • Escoba(ゲーム本体)、バックエンド(Go)、フロントエンド(React)を公開

最小構成で自作ゲームを作る手順

バックエンド(Go)の基本構成

  • GameState構造体 で状態管理(初期盤面やアクション履歴など)
  • CalculatePossibleActions で有効な手を算出
  • RunAction でGameStateを更新
  • Bot用関数 で自動プレイにも対応
  • ヒューマン対ヒューマンは有料サーバー必須、基本はBot対戦推奨

フロントエンド(React)の基本構成

  • 新規GameState生成 のAPIコール
  • 盤面レンダリング
  • 有効な手から選択し、アクション適用
  • Botのターンを自動で処理
  • シンプルな構成で実装可能

バックエンドとWASM連携手順

WASMへのコンパイル

  • GOARCH=wasm GOOS=js go build でWASMバイナリ生成(ただし巨大化するためTinyGo推奨)
  • TinyGo用のmain.go を別途用意し、エクスポート関数を明示
  • main()の最後でselect {} を呼び、プログラムの即時終了を防止
//go:build tinygo
package main
func main() {
  js.Global().Set("trucoNew", js.FuncOf(trucoNew))
  js.Global().Set("trucoRunAction", js.FuncOf(trucoRunAction))
  js.Global().Set("trucoBotRunAction", js.FuncOf(trucoBotRunAction))
  select {}
}

データの受け渡し(JSON経由)

  • GameState等の構造体はJSONでやり取り
  • TinyGoのドキュメントを参考に、 js.CopyBytesToGo/ToJS でバイト列をやり取り
func trucoRunAction(this js.Value, p []js.Value) interface{} {
  jsonBytes := make([]byte, p[0].Length())
  js.CopyBytesToGo(jsonBytes, p[0])
  newBytes := _runAction(jsonBytes)
  buffer := js.Global().Get("Uint8Array").New(len(newBytes))
  js.CopyBytesToJS(buffer, newBytes)
  return buffer
}

フロントエンドからの呼び出し例

  • WASM関数呼び出し・GameState管理のグローバル変数化
function jsRunAction(data) {
  const encoder = new TextEncoder();
  const encodedData = encoder.encode(JSON.stringify(data));
  const result = trucoRunAction(encodedData);
  const json = new TextDecoder().decode(result);
  return JSON.parse(json);
}
let gameState = jsNewGame();
gameState = jsRunAction(action);
  • 状態管理はWASM側が唯一のソース となり、フロントエンド側で直接変更不可

バックエンド更新時のWASM再ビルド

  • Makefileで自動化推奨
  • wasm_exec.jsのコピーも必須
  • HTMLのHEADにscriptタグでwasm_exec.jsと初期化コードを追加
<script src="wasm/wasm_exec.js"></script>
<script>
  const go = new Go();
  const WASM_URL = 'wasm/wasm.wasm';
  var wasm;
  let wasmReady = false;
  if ('instantiateStreaming' in WebAssembly) {
    WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function (obj) {
      wasm = obj.instance;
      go.run(wasm);
      wasmReady = true;
    })
  } else {
    fetch(WASM_URL).then(resp => resp.arrayBuffer() )
    .then(bytes => WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
      wasm = obj.instance;
      go.run(wasm);
      wasmReady = true;
    }))
  }
</script>

トラブルシューティング

  • WASMファイルが読み込めない場合
    • GitHub Pagesでは自動対応
    • ローカルでは http-server 等でHTTPサーバー起動
      • 例:npx http-server ./public -p 8080
      • ブラウザで http://localhost:8080 にアクセス

まとめ

  • ゲーム開発・公開の楽しさ と、 Go+React+WASMの実践的なノウハウ を共有
  • LLM活用による開発効率化 の実感
  • 自作ゲームに挑戦したい人向けの最小構成テンプレート と具体的手順
  • 質問や相談も歓迎、気軽に連絡可能

Hackerたちの意見

LLMは、前のゲームをテンプレートにしてコード面で3ヶ月の先行スタートを切ったし、もっと重要なのは、手動でのパスでの学びや失敗も活かせたことだね。

それだけじゃなくて、彼らにとって初めてのゲームだったから、未知の未知にたくさん直面してたんだよね。もし今日、LLMなしでカードゲームをプログラムし始めたとしても、前の経験から得た知識や洞察を活かせるから、3ヶ月もかからずにできると思う。

うん、これがクリックベイトだって分かってたけど、見た瞬間ちょっと驚いたよ:Trucoのバックエンドをクローンして、ClaudeにEscobaのルールを説明する長いプロンプトを与えて、コードをリファクタリングさせたんだ。人間の開発者が自分でコードをリファクタリングするのにどれくらいかかると思う?3日以上かかる可能性はあると思うけど、どうかな!

クローンする必要すらないよ。僕はゼロから数ヶ月かかるプロジェクトを再現したことがあるけど、次回は前回の1/3の時間で済む感じだね。

この投稿が好きなのは、多くの開発者が見落としがちなことを強調しているところ。ゲーム開発のコーディング部分は、実際にはボトルネックじゃなかったんだよね。ソロ開発者でも、AIがあろうがなかろうが、メカニクスをすぐに作り出せる。ほんとうに大変なのは、見えないレイヤーの部分。ループのバランス調整、難易度の調整、奇妙に見えないアセットの作成、そして誰かの注意を5分以上引きつけるための磨き上げ。だから、LLMの後に素晴らしいSteamのリリースが急に増えたわけじゃない。技術が一つの壁を低くしたけど、高い壁はまだ残ってる。2010年代のUnityの台頭みたいなもので、エンジンがゲーム制作を民主化したけど、良いゲームの爆発的な増加は見られなかった。ただの試みが増えただけ。LLMはコードに対して同じことをしていて、画像モデルはアートに対してそれを始めているけど、どちらもあなたのゲームが実際に楽しいかどうかは教えてくれない。私にとって興味深いのは、AIが実装だけでなくプレイテストもできるようになったらどうなるかってこと。ループの何千回もの反復を行い、シミュレーションされたプレイヤーを引きつけるメカニクスを浮き彫りにする。そうなった時、私たちは「AIを生産性向上のハック」として使うのを超えて、「デザインのコラボレーターとしてのAI」に進むことができる。まだそこには至っていないけど、この記事はその方向性における初期のデータポイントのように感じる。

私の生成AIのリトマス試験:2Dピクセルアートアクションゲームの完全なスプライトシートを生成してみて。例えば、バトルタンクやメインヒーローの動きだけでも。今のところ成功してないけど。

実際に面白いゲームプレイを持つゲームを作ることが、どの言語でゲームを作るのがベストかを議論するよりもずっと重要な理由だよね。

私たちは良いゲームの比例的な爆発を見なかった 確かに、2010年代の初め頃には良いインディーゲームの爆発があったよね。Unityに関係があったかどうかはまた別の話だけど。

私にとって興味深い質問は、AIが実装するだけでなく、プレイテストもできるようになったらどうなるかということです。ループの何千回もの反復を実行して、どのメカニクスがシミュレーションされたプレイヤーを引きつけるかを明らかにするのです。AIはどうやってプレイヤーをシミュレートするのでしょうか?そして、なぜAIが本物の人間が魅力的だと感じるものを判断できるべきなのでしょうか?

2010年代のUnityの台頭みたいなもんだね。エンジンがゲーム制作を民主化したけど、いいゲームの爆発的な増加は見られなかった。ただの試みが増えただけ。でも、実際には?限られたXBLAのカタログからはずいぶん進歩したよ。これは一晩で起こったわけじゃないけど、Unity、Godot、Gamemaker、Renpy、RPG Makerがなければ、今のゲームの量はなかっただろうね。

この投稿の好きなところは、多くの開発者が見落としがちなことを強調している点だ:ゲーム開発のコーディング部分は、実際にはボトルネックではなかった。ソロ開発者は、AIがあろうとなかろうと、メカニクスをかなり早く作り出せる。これは全然違うよ。ゲームに関わったことがない私には、「基本的な」ゲームを書くのにかなりの時間(数ヶ月かかることもある)を要する。ソフトウェア開発の良いプラクティスについてはたくさん知っているし、FAANGでの10年以上の経験もあるけど、ゲーム開発の細かい部分や基本すら知らない。最近、別の用途でこれを体験した。経験豊富なバックエンド開発者として、いくつかのJavaScript/ブラウザ関連の自動化をしたいと思った。2〜3日自分で試してみたけど、いくつかのプロトタイプはできたものの、実際には何も機能しなかった。AIと2時間過ごしたら、動作するソリューションができたんだ。一緒に素早く反復して、いくつかのランタイムの問題も解決できて、今はスムーズに動いてる。だから、私のような経験豊富な開発者にとっても、AIの価値は確実にあると思う。

AIが実装だけじゃなくて、プレイテストもできるようになったらどうなるの? 何千回もループを回すことができるんだ。アタリのゲームからスタークラフトまで、LLMが登場する前からそういうことはあったよね。 > シミュレーションされたプレイヤーを引きつけるメカニクスを浮き彫りにするのは、どうやって実現するかは不明だね。いろいろあるけど、全てのゲームが全ての人に合うわけじゃないし。

ゲーム開発のコーディング部分は、実際にはボトルネックじゃなかったよ。僕にはそうは見えないな。著者はサーバーを構築して、Reactを学び、フロントエンドを作って、カードゲームを動かしたんだ。で、カードゲームに必要な部分がほとんど整った状態で、既存のコードを変更して別のカードゲームを実装するようにClaudeに頼んだ。理解できるけど、確かにそれは短時間で済んだ。でも、人間のエンジニアが同じことをやっても、もっと短時間でできたはずだよ。

これは他の分野でも当てはまるし、少なくとも僕にとっては、証拠から結論が導かれるよ:エージェントコーディングにはかなりの可能性があるみたい。いくつかのタスクは簡単に解決されてるけど(クイックなウェブアプリのデモや小さなライブラリの統合など)、本格的なソフトウェアにはまだまだ足りない。モデルの調整方法や、それを使うための我々の専門知識がね。これは驚くべきことじゃないよ。Gitスタイルのバージョン管理が登場してからほぼ20年経つけど、どこでも完全に使えるようになるまでに5年かかったし、エリート企業が完全に理解するまでにさらに5年かかった。それ以来、徐々に普及してきて、今ではかなり使えるようになってる。これを正しく使うのはGitよりも難しいと思う。実際、すべての製品リリースやOSSツール、クソみたいなブログ投稿が「もう終わった、解決した、古い方法は終わり、新しい世界が来た」って言い張ってなければ、もっと早く進むと思うよ。私たちはまだ模索している段階で、時間がかかるのはOKだよ。もしもう終わってたら、素晴らしいソフトウェアが溢れてるはずだし、今はそうじゃない。なんとか均衡を保ってる状態で、これは新しい大きなことが1〜2年で出てきたことを考えればすごいことだよ。

ゲームによるけど、確かに何千回もプレイテストするよ。でもそれはバランスのためね。どのメカニクスがプレイヤーを引きつけるか、プレイヤーからフィードバックをもらう必要があるんだ。

情報を求めて - Go/wasmは、React/JavaScriptができないことをやっているの?

フロントエンドはまだReactなんだ。でも、LLMがJavaScriptよりも強く型付けされた言語のコード生成でエラーが少ないのか、ちょっと気になるな。

この場合、いいえ。クライアントゲームだし、パフォーマンスは全然重要じゃないから、状態やループは普通のJS関数で簡単に処理できるよ。もしサーバーアーキテクチャが必要になったら、その関数をNodeでホストするのも簡単だし。俺の意見としては、バックエンド開発者として彼は、何が何でもサーバーを持つことにこだわってたんじゃないかな(「ハンマーしか持ってないと、すべてが釘に見える」ってやつね)。それでGoがメイン言語だったから、そっちを選んだんだろうけど、結局あんまり意味がないことに気づいたんだよね。でも、そのシンプルなロジックをJSに訳す代わりに、全体をオーバーエンジニアリングして、WASMにトランスパイルすることで設計やビルドプロセスを複雑にしちゃった。バグもいくつかあるけど。最近、エスコバでいくつか試合に勝ったんだけど(すごくいいゲームだよ、何十年もやってなかった!)、次の試合のためにゲーム状態がちゃんとリセットされてなかったんだ。それは多分LLMのせいだね…

なんでこんなに未完成で、洗練されてないグラフィック、うるさい音が重なって倍増するようなゲームを見せる意味が分からない。ちょっと失礼かもしれないけど、これと子供がScratchで作ったゲームの違いは何なの?後者は子供が作ったって分かるし、彼の学びの旅を応援したいと思う。シニア開発者として、こんな状態のゲームを見せるのは恥ずかしいよ。しかもReactで作ったなんて、全く意味不明だね。

唯一考えられる理由は、自己アピールみたいなもんかな。でも、それなら6ヶ月くらいかけてゲームを超磨き上げて、ロックスターみたいに見せればいいのに。AIを使ってどこが役立つか学ぶこともできるし…そうじゃないと理解できないな。

あなたが一人で作ったものを見せて、どれくらいのユーザーがいるか教えてくれる?何もないなら、話は安いってことを思い出してほしい。

あなたの他人に対する判断力よりは、確実に洗練されてるね。

せいぜい24時間じゃない? https://nordicgamejam.com/ 俺は今のLLMやGenAI、エンジンが出るずっと前からこういうのに参加してる。昔は、MicrosoftのXNAとC#が一番の選択肢だった。(Unityはまだ発明されてなかった。)それに、ほとんどのアートはペイントで手描きみたいに見えたけど、実際そうだった。とはいえ、毎年楽しいゲームがたくさん出てたし、Baba is YouやBraidみたいに広いオーディエンスに届いたものもあった。コーディングがボトルネックになることはなかった。俺はチームメンバー間のコミュニケーションが一番大事だと思ってる。

私はチームメンバー間のコミュニケーションが一番大事だと思っています。「コミュニケーション」は自分の頭の中でも意外と難しいことが多いです。

LLMが好きなのは、自分のプログラムの考え方に直接合った形でコードを抽象化してくれるからなんだ。コードを読むと、ASTみたいなものに変わって、関数や呼び出しが入力と結果の抽象的な概念になるんだよね。LLMはその逆のプロセスをものすごく簡単にしてくれる。アイデアをコードで表現する方法を探したり、記憶やコードアーカイブを掘り返す必要がなくて、LLMにWiFiの初期化用のボイラープレートを書いてもらうことができる。コードがレゴのピースみたいになって、自分の内部の問題の概念を反映したプログラムに組み立てられるんだ。LLMを使っていろんなプログラミング言語をサクサク進めてるよ。テキストや文法が抽象化できると、プログラムに集中できるんだ。LLMが登場する前にもできたけど、もっと努力が必要だった。確かに、各言語についてあまり学べないけど、それが逆にポイントなんだよね。どの言語のテキストや意味論も、プログラム自体の論理の流れにはほとんど関係ないはず。テキストは、機械が解析できる形で論理を表現するための半標準化された方法に過ぎないし、人間が理解するためのものは二次的なものだよ。最初に機械語をアセンブリに抽象化した。その後、Cのような低レベル言語、さらに高レベルの言語へと進んで、最終的には機械自体が完全に無関係になった。これらの言語をさらに抽象化して、コーディングではなくプログラミングの目標に近づくのは自然な流れだと思う。最終的にどうなるかのヒントすら見えていないけど、書く時間がどんどん減って、プログラミングにもっと時間を使うようになると確信してる。

よく言ったね。昔、Cでコーディングしている人はアセンブリを理解していないと言っていた人たちがいたけど、それがいつか自分に返ってくるかもしれないって。AIはツールで、今はそのツールの最も良い役割が、開発者から面倒な実装の詳細を抽象化することだってことが明確になった。今は、モダンな言語で書いたプログラムでは手動のメモリ管理に悩む必要がないのと同じようにね。セグメンテーションフォルトに対処する必要がなくなったんだから、簡単なコードブロックを実装するためのドキュメントや例を追跡する必要もなくなっていいじゃん。

LLMを使ってこれをやるのに数日かかったよ:https://www.susmel.com/stacky/ 全体で言うと、だいたい2日間くらいかな。最初はグリーンフィールドとその後ブラウンフィールド開発のテストグラウンドとして使ったから、特に真剣なものではなかったけど、調整したい詳細がどんどん増えていって、機能もどんどん追加していった(スーパーローテーションシステムとか、ダスとか、いろいろね)。これをフルゲームとして持っていくには、まだ10〜20%くらいのところだと思う。WebGL版もどこかで機能してるけど、究極のテトリスを作り始める前に止めなきゃいけなかった。訴えられたくないし、フルライセンスを取得するお金も(多分)ないからね。結構な金額を取られるって聞いたけど…でも、今は自信を持って進められるようになったし、その経験を他のソフトウェア開発にも活かしてる。こないだHNでパラメトリック関数について面白いリンクがあったんだ。それを見て、また興味が湧いて、1、2時間で遊び場を作ったよ:https://www.susmel.com/graphy でも、詳細、詳細、詳細だね。自分が見たいものがわかってれば、実際にかなり楽しいよ。

いいテトリスの再実装だね!ちなみに、Firefox + M4 Max MBPでやってみたら、コアが100%使われてファンがすごい回ってるよ。

クライアントサイドだけのゲームが欲しいなら…なんで「バックエンド」があるかのように書くの?「バックエンド」とアプリ全体で異なる技術を使う理由は何?

趣味でゲーム開発を結構やってきて、いくつかのゲームも完成させたよ。ここでのコメントは、実際のゲーム開発の知識がかなり不足してるように思う。コーディングはゲーム開発の難しい部分なんだ。面白い新しいメカニクスや既存のジャンルのアレンジを考えるのは割と簡単だけど、それを実現するのは特にコードが難しい。マルチプレイヤーのバンパイアサバイバーだけど、巨大なバトルテックのメカカスタマイズがあるっていうのは、見ての通り、すごく簡単。これをLLMで作るのは大変だよ。このメカニクスはよく知られたカードゲームを使ってるから、スネークゲームと同じくらい面白くない。これはOPに対する批判ではないけど、ここにいる多くの人がゲーム開発をあまりやったことがないのは明らかだね。

あなたの意見には大いに賛成だけど、面白いことに、個人的にはゲームデザインのアイデアや青空思考の部分が一番難しいと感じてる。今はほぼどんなゲームメカニクスでもコーディングできるけど、ゲーム開発のライティングやクリエイティブな部分には苦労してる。もしそれが簡単だと感じるなら、かなり恵まれてると思うよ。みんなが自然にできるわけじゃないからね。

ゲーム開発のコーディングは難しい部分だよ。面白い新しいメカニクスや既存のジャンルのアレンジを考えるのは簡単だけど、それを実現するのは難しい、特にコードの部分がね。マルチプレイヤーの吸血鬼サバイバーだけど、巨大なバトルテックのメカカスタマイズがあるやつ。僕は違うと思う。確かに難しいけど、新しくて楽しいゲームプレイのアイデアを考える方がずっと難しいよ。楽しいアイデアがあれば、あとは問題を小さく分けて、反復していくだけ。真っ白なページの問題に直面して、ゼロから何かを考え出すときに、明確な方法論はないよね。散歩に行くのがいいかもしれないし、ドラッグを大量に摂取するのもいいかも。いろいろ試してみて、何がうまくいくかを見るのもありだね。これは何千年も前から創造的な取り組みの中で存在している問題だし、コーディングは「ただの」エンジニアリングだよ。僕はこの1ヶ月ゲーム開発を学んでいて、何かをするためにたくさんの数学を学ばなきゃいけなかったけど、それでも「どんなゲームを作りたいのか?」という質問の方がずっと難しかった。今でもその答えは出ていないよ。3Blue1Brownの動画は役に立たないし、ベクトル数学やクォータニオンが何なのかを学ぶのとは違うからね。

このコメントは、このスレッドのほとんどのユーザーがゲーム開発をしたことがないことを示してるね。LLMの有用性を、トレーニングデータに過剰に代表されているプロジェクトで判断してるんだ(プログラミング入門のコースの多くがこういうプロジェクトを要求するし、南ヨーロッパの国々ではブログにあるような似たようなゲームがたくさんある)。大学の1年目に、PythonでMoon Patrolを再実装したけど、ほとんど経験がなかったし、週3日で2〜3ヶ月かかったよ。カードゲームをコーディングするのはそれより簡単だよ。LLMは特定のことには役立つけど、これはゲーム開発のコーディングにおける有用性を評価する良い方法じゃないね。