.NET Lab
Godot .NET で
3DのiPhoneゲームを作る
C# NativeAOT・AIエージェント並行開発
高丘 知央
株式会社ジェイテックジャパン CTO / J-Tech Creations, Inc. VPoE / Sekiban Framework メンテナ / Microsoft MVP
▶ 初代(2009)メインテーマ
自己紹介
- 高丘 知央 — 株式会社ジェイテックジャパン CTO(日本)/ J-Tech Creations, Inc. VPoE(米国)
- カリフォルニア州・ロサンゼルス近郊在住
- イベントソーシング / CQRS のコンサル、AI活用開発の支援・セミナー
- OSS: Sekiban(Event Sourcing)/ intent-cli(意図駆動開発)
- Microsoft MVP(Developer Technologies, 2024.11〜)
- そして——学生時代に作り始めたゲームが、30年越しで完成!
「Zero4」の30年 — 原点と心残り
30年越しの「Zero4」を、C#とAIと一緒に完成させた話
2026年の Zero4Racer 両アプリ(Pro復刻版・Drive)は J-Tech Creations, Inc. の社内プロジェクトとしてリリース(企画・開発: 高丘)。製品の所有は同社です
Zero4Racer Drive
3Dレーシング × 運転免許練習 × カスタムペイント × 車で鬼ごっこ
- 原点は直線だけのゼロヨン(1996-97 C++テキスト版 → 2009 iPhone版 Zero4Racer)— 実車データで「クルマらしさ」
- 3D化で街へ: 公道・教習所・日米の運転免許試験まで遊びにする
- 運転練習機能は、カリフォルニアで17歳の娘の免許練習に合わせて追加 — 家族が最初のユーザー
- 自分の車にカスタムペイント — 描いて、審査されて、走る
- カスタムペイントした車で、平地・アメリカの街並み・日本の街並みを舞台に鬼ごっこ
「運転が上手くなる気がするレースゲーム」を、ひとりで完成させる
App Store のプレビュー動画(実物)
再始動から5か月 — Pro再現と、31日のDrive
Pro(2D)再現 2026.5.16〜9.11 ・ 途中で何度も止まりながら
0作業日(119日中)
0commits
0C# 行(うちテスト 67,773)
Drive(3D)作成 2026.9.12〜10.12 1.0.0公開
0作業日(31日中)
0commits
0C# 行(うちテスト 44,247)
Godot 4.7.2 .NET / .NET SDK 10.0.401(global.jsonで固定)/ App Store id6812499414
- ローカルWiFi対戦(レース・鬼ごっこ)に加え、Epic Online Servicesでインターネット・マッチング対戦まで実装
- 少人数の開発なら後回しになりがちな機能も、この5か月に同梱できた
AIエージェント2系統と並行開発した結果の数字
02
GODOT .NET × C#
.NETエンジニアにどう見えるか
構成の要 — Godotに触れない「Core」を純C#で
同じCoreをGodotは毎フレーム呼び、テストは実時間より速く回す — シミュレーションは.NETの中で完結
この分離は旧Proの物理モデル(TTCar)から — 理由は次の「テスタビリティ」
業務アプリの .NET と、Godot の .NET
業務・エンタープライズの .NET
Godot の .NET
① ループを回すのは自分のコード(Main / Host)。リクエストが来たら動く
エンジン。毎フレーム _Process(delta)、物理は一定間隔の _PhysicsProcess が呼ばれる
② 部品の組み立てDIコンテナとインターフェース
シーンツリー。Node を継承して子ノードを並べ、GetNode<T>() で参照
③ 設定の渡し方appsettings.json / Options
[Export] をエディタで設定 → シーン(.tscn)に保存。partial 必須はこのソースジェネレーターのため
④ イベントevent / メディエーター / メッセージング
Signal([Signal] で宣言、エディタからもつなげる)
⑤ 時間の感覚1リクエスト数十〜数百ms。GCはほぼ気にしない
1フレーム 16.6ms。毎フレームの割り当てはGCのカクつきにGC
⑥ オブジェクトの寿命GCに任せる
Node はエンジン側の寿命(QueueFree)で、GCとは別 → 次のページの問題へ
エンジンが主で、C#は呼ばれる側 — だから計算はCoreに出してテストする
Godot × iOS で直面した問題
1GCとノード寿命は別物QueueFree してもマネージド側の大配列は残る
2ヒープ140MBで OutOfMemoryiPhoneでは「大きな連続配列」が先に壁 → メッシュ頂点は自前リスト(NativeMemory.Realloc)で持つ
3終了時クラッシュ(FATAL !rc_owner)C#にバインドされたGodotオブジェクト → Quit前にGC
ネイティブの世界(Godot / C++).NETの世界(C# / GC)
どれも「エンジンのネイティブ世界と.NETの世界が隣り合う」から起きる
テスタビリティが、ゲーム作りで一番大事
旧Proにテストは無かった。復刻も最初はレース進行がGodotのシーンに埋まってuntestable — そこを純C#の Step(input, dt) に切り出した(2026-06)。3D版のCoreはこの形を最初から引き継いでいる
0倍速 — 免許試験コース5,967m(交通・採点込み)
ゲーム内16.5分 → 実時間7.3秒
0倍速 — 400m×4台のフル物理
ゲーム内55秒 → 1.2ms
0テスト全合格 / 3分16秒
(dotnet test・Godot起動なし)
0ステップ — 2D↔3Dの物理一致
(旧Pro→2D→3Dの継承をテストで証明)
実時間より速く「遊び」を再現できる形にしておく — 速さの感覚の調整も、AIの修正の検証も、全部ここに乗る
AIへの依頼: 入力をすべてシミュレーターに渡せる形に
Godotタッチ・ペダル・ハンドルUI
→ 入力データを作るだけ
アクセルブレーキステア
Core シミュレーター
Step(input, dt)
物理・判定・交通 = 純C#
Godotを知らない
状態状態
Godot 描画車・カメラ・音に反映
テストxUnit・Godot起動なし
入力列をスクリプトで生成
アクセルブレーキステア
状態状態
Assert実時間の最大4.7万倍速で検証
- アクセル・ブレーキ・ステアリングなどGodotで取れる入力を全部、シミュレーターへの入力データとして渡す
- 境界は入力データだけ → 「入力を作る側」と「計算するCore」が完全に切り離せる。差し替えるだけでテストが運転する
03
NativeAOT → iPhone
実機に載せるまで
iOS では NativeAOT が必須(JIT不可)
なぜ?iOSは実行時に機械語を作って動かすこと(JIT)を許さない → C#は事前に全部ネイティブ化(AOT)しておく
Macでは普通の.NET(JIT)で動くため、問題はiOS向けpublishで初めて現れる
「AOTで動く」は最初から設計の制約として効いてくる
NativeAOT で問題が出やすいもの
① リフレクション前提のコード
例: テスト用コードで匿名型 new { … } を JsonSerializer に渡していた → iOS向けpublishが失敗
error IL3050: … requires dynamic code generationEXPORT FAILED: Export lacks managed AOT payload
対策: source-generated の [JsonSerializable] ×92 / Utf8JsonWriter直書き。dotnet build は通るので publishをゲートに
② ネイティブ連携 — [DllImport("__Internal")] ×73
[DllImport("__Internal", EntryPoint = "z4r_video_append_frame")]
static extern int AppendFrame(ref byte rgba, int length);
extern "C" __attribute__((visibility("default")))
int z4r_video_append_frame(const uint8_t *rgba, int length);
StoreKit課金 / Game Center / EOS / AVAssetWriter / オーディオ / アクセシビリティ — 全部この橋を渡る
z4r_video_beginz4r_video_append_framez4r_video_append_audioz4r_storekit_purchasez4r_gamecenter_authz4r_eos_session_joinz4r_audio_sessionz4r_accessibility_voiceoverz4r_video_share_beginz4r_video_save_begin
z4r_video_beginz4r_video_append_framez4r_video_append_audioz4r_storekit_purchasez4r_gamecenter_authz4r_eos_session_joinz4r_audio_sessionz4r_accessibility_voiceoverz4r_video_share_beginz4r_video_save_begin
04
VEHICLE DYNAMICS
物理はGodot、クルマらしさは実車データ
HOLLYWOOD MOTOR GALLERY
ZERO4RACER DRIVE ショールーム
回転台の12台は、ゲームで実際に走っている実アセット(Godotから書き出したGLB)
01 / 12
STAMPEDE SS
アメリカンマッスル · アメリカ
▶ エンジン音(実機と同じ合成式)
▶ シフト音
剛体は RigidBody3D、駆動とタイヤは自前の8ステップ
次のキーで1ステップずつ見ていきます
① トルク補間エンジンのトルクを回転数で補間し、アクセル開度を掛けて、エンジン摩擦を引く
② クラッチクラッチのつながり具合と総減速比で、タイヤに届く力に変換する
③ ブレーキブレーキの力と、速度に比例する空気抵抗を差し引く
④ 荷重移動加減速で前後の荷重が移り、速度が上がるとダウンフォースが増える
⑤ 駆動配分FF / FR / 4WD に応じて、駆動力を前輪と後輪に配る
⑥ 摩擦で制限タイヤの静/動摩擦の上限で駆動力を制限する — 超えると滑る
⑦ 積分車速・位置・RPM を dt で積分して、次の状態にする
⑧ エンスト回転が落ちすぎたらエンスト、上がりすぎたらレブリミット
①の実データ2007/2008年のSQLにあるトルク曲線(6車種)を、そのまま補間に使っている
12車種のトルク曲線・ギア比は19年前のSQLから移植 — 3D版は操舵・横力・4輪サスを追加
05
MULTIPLE AI AGENTS × HUMAN
意図駆動開発(intent-cli)と、人が持つ承認
intent-cli — 意図を管理して、意図で開発する
株式会社ジェイテックジャパンが開発する、C#製の意図駆動開発(IDD)ツール。会話で決めた「なぜ・何を」をリポジトリに残す
intent-cli は AI を起動しない — 意図と契約を残し、次の手順を決定論的に案内。人が残すのは意図、プロンプトは生成物
intent-cli 自身も intent-cli で開発 — merge済みPR 916件中、意図ユニット(G番号)659件(2026-04-10〜10-21)・Release 62件(最新 v0.32.1)・Apache-2.0 / dotnet tool install -g JTechJapan.IntentSystem.Cli(2026-10-23時点)
orchestrator mode(複数席)— 「意図を渡して回す」
1席+独立したレビュー用subagentで回す solo-conductor もあり、チームの形は対等(ADR 0015)。Zero4Racer Drive は Claude Code と Codex を実装に使い、各作業は git worktree(スレッド間連携の agmsg は現在非推奨、新しく組むなら herdr-only)
自走させる所と、人が持ち続けた承認
AIに任せた(自走)
- 実機記録(.z4rrec)の吸い出し・デコード・原因特定
- テスト再現 → 修正 → PR → マージ
- 開発ビルドを両端末にインストール
人が承認を持ち続けた
- Cloudflare Worker のデプロイ(変更ごと)
- TestFlight / App Store への提出
- 課金設定・マルチプレイのプロトコル番号
- リリースに何を入れるかの判断
「まだ live でない間はマージは任せる」— 権限は段階で委譲する
06
SERVER SIDE
Sekiban-dcb-ts on Cloudflare
カスタムペイント審査 — イベントを積んで状態を導く
カスタムペイントを解放した結果、不適切な画像を他のプレイヤーに見せない仕組みが必要になった
審査の状態が変わるたびに、誰に見せてよいか(権限)とゲームでの見え方(動作)が変わる。その変化を状態の上書きではなくイベントとして積むので、いつ・なぜ見えるようになり、なぜ見えなくなったかを後から辿れる — 取り消しも消すのではなく追記
Sekiban-dcb-ts 0.2.0(SekibanのTS版。株式会社ジェイテックジャパンの開発プロジェクト)on Cloudflare — 構成は次のページ
書き込みの一貫性は「タグごとのDurable Object」が持つ
タグ = DCBの整合性境界。DOはキーごとに1インスタンス+トランザクション付きSQLiteなので、分散ロックなしでタグ内の一貫性を書ける
正本はタグDOのSQLite。D1へは派生コピーが数秒で流れ、毎分のcronが全件を確認する
一番の設計論点 — 「不変ログ」と「削除義務」の両立
ゲームでも業務でも同じ — 何を不変にし、何を消せるようにするかが設計
07
SHIP IT
AIが仕上げてくれる今、完成に何が要るか
実機で触って、指摘を返す — 正しさは現実から来る
「後続車が内側から突っ込んできた」→ .z4rrec を解析: 車線中央から2.2mずれ、判定幅の10cm外だった
「問題ないのに FAILURE TO YIELD」→ 横切り終えた対向車を「交差点占有中」と数えていた
航空写真と見比べて「停止線が奥すぎる」→ 道路生成の構造ごと変更
現実がそのままルールになる
本人が実際の路上試験で「右折専用の青矢印で完全停止」して不合格になった
→ その日のうちに「青矢印で止まると不合格」のルールと信号をゲームに追加(2026-10-07)
- iPhoneでしか出ないOOM — Macでは再現せず、端末内ログに例外とメモリ状況を残す仕組みで特定
- 合成テストでは再現できないバグが、実機のリプレイ記録で初めて見える
実機と同じ生成コードの街(HARBOR)
AIが仕上げてくれる今、完成に要るもの
- 気持ちよさ・面白さ・ゲームとしてのバランスを、妥協点を探しながら考え抜き、諦めずに改善要求を出し続ける気合いとこだわり
- 幸いこれはビジネスアプリを作ってリリースするときのこだわりと同じ方向性 — 30年の開発経験が判断の土台になった
アプリを実際に使い、細部まで確かめて、折れずに指摘を出し続ける
それが完成の原動力
08
AFTER LAUNCH
Godotはゲームを出した後も武器になる
リプレイ → MP4 書き出しも C#(アプリ内蔵)
リプレイ記録.z4rrec
→
SubViewport 再描画番組時計で決定論的に
→
GetImage() 読み戻し7.9MB/フレーム
→
AVAssetWriterDllImport経由でMP4
using var image = _director.Viewport.GetTexture().GetImage(); // GPU→マネージド
if (!_encoder.AppendFrame(image.GetData())) { EncoderStopped("append-frame"); return; }
_audio.Render(_audioCursor, chunk); // 効果音もその場でミックス
Mac側は同一インターフェースでハッシュだけ取る別実装 → 書き出し全体をテストで検証できる
このプレゼンも同じ素材から: 章扉の背景はゲームを撮った動画、車や交差点は実GLBをThree.jsでリアルタイム描画
これが書き出された実物 — iPhone実機のアプリがAVAssetWriterで書き出したMP4、無加工。CPU対戦(6台中P2) SHORT CIRCUIT。共有テキストもアプリが生成
リール / ショートで露出を作る
交通クイズを「見られる・語られる」ものにする7つの工夫
1問いは「あなたならどうする?」の二択
悪役(クラクション・あおり・割り込み)× 誰でも経験する場面 × 意見が割れる答え → 体験談とツッコミが集まる「後ろからクラクション」8,700回
2日本語版と英語版で狙いを分ける
日本語: 日本人が驚く米国ルール「赤でも右折OK」8時間で2,058回英語: 米国の「あるある違反」「出口直前の割り込み」8時間で1,943回
3主役を最優先にした画面
文字はスマホの安全枠内 / 主役の車は隠さず大きく / 注釈は画面の25%以内 / 話している方だけ小さく表示
4場面の作り込み
地域・施設・道路標示を「場所の設定書」で決めてから3Dに。車はゲーム内の車種モデル
5正確さ
法令は一次資料で確認。画面に「日本では/カリフォルニアでは」と出すときはセリフでも同時に言う
6品質の自動チェック
全コマ検査(文字位置・主役の見え方・走る向き・衝突・信号)/ 音声認識で読み間違い検出 / AIの画面レビュー ▶ 読み上げを聴く
7アプリへの導線
最後に実際のゲーム画面で「日本と米国の標識で練習できる」と具体的な価値を伝える。「いいね・コメントして」とは頼まない
方針: 量より質
1回 10本(5テーマ × 日英)を作り、オーナーが見て確認してから公開
O05「後ろからクラクション。右折する?」(公開済み)
3D運転クイズをSNSへ — 約1か月で23万回超の表示・再生
0表示・再生(複数SNS合算 ※)
↓ 約51%
0視聴者(Instagram 30日)
↓ 約0.6%
0プロフィール訪問(Instagram)
↓ 約14%
0リンクタップ(アプリへの関心)
帯の長さは対数目盛
23万回の内訳
Instagram 207,604YouTube 16.8KFacebook 8.2K
Instagram フォロワー 0開設1週間
線は 0 → 103 の推移のイメージ(日ごとの実数ではない)
- 日本語・英語のショート動画を7日間で100本 — 独自3Dエンジン(Godot+C#)でシーンを量産できるのが展開の強み
- 交通判断クイズ → 参加 → アプリへの関心(リアクション約1,400。リンクタップはインストール数ではない)
※ Instagram 207,604 + YouTube 16.8K + Facebook 8.2K の参考合算(集計期間・指標定義は媒体ごとに異なる)。2026-10-23 時点のスナップショット
まとめ
- 01
30年越しの「Zero4」を完成3Dレーシング × 運転免許練習 × カスタムペイント × 車で鬼ごっこ — 2026.10.12 Drive 1.0.0
- 02
Godot .NET入力をすべてシミュレーター(純C#)に渡す形にして、Godotの表示と分離して開発・テスト
- 03
NativeAOTiOSはAOTが必須。publishをゲートに、ネイティブとは73本の橋
- 04
複数のAIエージェント × 意図駆動開発直す速度はAI、デプロイ・提出・リリースの承認は人
- 05
出した後も Godot+C# が武器3Dエンジンで交通クイズを作り、量より質で約1か月 23万回超
アプリを実際に使い、細部まで確かめて、折れずに指摘を出し続ける
それが完成の原動力