TechNeko
ノート一覧へ
LAB NOTE / 2026-07-03

TBA 開発ログ #1:ヘッドレスシミュレーションから動く荒野の小隊へ

タスクバーに住むポストアポカリプス放置 RPG。Godot 4 + GDScript、コードは AI エージェントがコマンドラインで書いています。立ち上げから 2 週間の全工程を記録:まず画面なしで戦闘の数学と経済ループを動かし、その上にドット絵の表現層を重ねました。途中で自作のレイヤー分割キャラアニメを棚上げし、AI 生成の「なんちゃってドット絵」も捨てました。

これは何か

TBA(TaskBarApocalypse)は荒廃した世界を舞台にしたタスクバー常駐型の放置 RPG です。画面最下部の 760×200 の透明な細長いウィンドウの中で、3 人の小隊が自動で行軍し、敵と遭遇し、戦い、装備を拾います。あなたが仕事をしている間に彼らは成長します。設計は三つの輪で構成されています:主力部隊のリアルタイム自動戦闘、隠れ家の育成、そして中盤以降に解放される遠征システム(隊員を 8〜12 時間送り出し、特定種類の上位装備を持ち帰らせる)。

方針は小さく美しく、口コミと継続率を最優先、ローカル優先、中央サーバーなし、リアルマネー市場なし。同種のタスクバー放置ゲームに着想を得ており、数値の桁感も公開資料を参考にしましたが、絶対値はすべて自分たちのペースに合わせて再設計します。

市街地の廃墟:小隊が小型ゾンビとゴブリン射手と対峙

まずルールを決め、それからコードを書く

着手前に技術設計・引き継ぎ文書を書き、アーキテクチャといくつかの鉄則を固定しました。以降の開発タスクはすべてこの文書を参照します。

4 層の一方向依存:表現層 → シミュレーション層 ←→ データ層 / 永続化層。表現層はシミュレーション層を読むだけで、シミュレーション層は視覚ノードを一切知りません。

4 つの鉄則

  • シミュレーション層は固定タイムステップ(0.1 秒 = 1 tick)のアキュムレータで駆動し、描画フレームレートから切り離す。ウィンドウを極小にしても隠してもロジックは動き続け、オフライン精算は「N tick 早送り」に過ぎない。
  • シミュレーション層は決定的:同じシードと同じ入力なら結果は完全に同一。乱数はシリアライズ可能なシードを使い、状態ごとセーブに入れる。
  • セーブデータが唯一の真実。サーバーとの照合がないので、ローカルとサーバーの不整合という問題自体が存在しない。
  • チート対策はしない。セーブの軽い検証は壊れて落ちるのを防ぐためだけ。時計をいじっても損をするのは自分。

そして開発を、端から端まで動かせる縦割りスライスに分けました。まずヘッドレスシミュレーション+仮アート、表現層は後から重ねる

スライス内容状態
0 骨組みGodot プロジェクト、ディレクトリ、固定 tick ループ、デバッグパネル完了
1 ヘッドレス戦闘3 層ステータス、ダメージ式、軽減パイプライン、ドロップ→インベントリ完了
2 経済ループ物資 / 装備の二重エンジン、隠れ家と作業台、地域深度ブレーキ完了
3 キャラ合成器5 レイヤー rig + 5 ステート + フレーム単位ピボット動作確認後に棚上げ
表現層ドット絵の戦闘ステージ、実数値と経済ループを接続遊べる
4 遠征派遣、配備ロック、実時間タイムスタンプ、段階別精算未着手
5 永続化ローカルセーブ、オフライン精算、タスクバーウィンドウモード未着手

スライス 0 + 1:画面のない戦闘

最初の戦闘は完全にテキストでした。隊員 3 人対雑魚 5 体、tick ごとに攻撃タイマーを進め、コンソールに「近戦 が D1兵3 を攻撃 -18 (HP 72/90)」と一行ずつ流れます。目的はただ一つ、数学が正しくループが面白いことを先に証明し、アートは後回し

ステータスは Diablo 2 式の 3 層重ね:

最終値 = (base + Σflat) × (1 + Σadditive) × Π(1 + 各 multiplicative 層)

乗算層は「複数の独立した層を一つずつ掛ける」のであって、合計してから一回掛けるのではありません。これは意図的で、フライホイールも「フル装備で戦力が跳ね上がる」感覚もここから生まれます。起動時にセルフテストが走り、base 10、flat 5、additive 50%、乗算層 20% と 10% で期待値 29.7、違えば FAIL と出ます。

ダメージ軽減パイプラインは厳密に 7 段階の順序:回避 → ブロック(固定値削減)→ 属性耐性 → 物理アーマー逓減 → 汎用ダメージ軽減 → 吸収シールド → 床(1 撃最低 1 ダメージ)。アーマーの段は地域深度と結合しています:

軽減率 = アーマー / (アーマー + 閾値)、閾値 = 14 × 深度 + 12

アーマーが閾値と等しいときちょうど半減。この式が「深度」を戦力の門にする数学的フックで、同じ鎧は深く行くほど効かなくなり、自然と装備更新を迫ります。

乱数は DetRNG にラップし、シードも内部状態も読み書きできるようにして、スライス 5 のオフライン精算のインターフェースを確保しました。

スライス 2:経済を勝手に回す

経済は二重エンジンです:

  • 規模エンジン:地域クリアで物資 → 隠れ家アップグレード → 全員に割合ボーナス、3 レベルごとに遠征スロット +1。
  • 装備エンジン:地域クリアで素材 → 作業台アップグレード → クラフト → 装備 → 戦力。

二つは遠征スロットで結合し、ブレーキの連鎖は「地域深度 → 要求戦力 → クラフト深度 / 作業台レベルで制限」。装備の数で速度を制限するのではありません。

放置中の自動消費は決定的な貪欲戦略で順序固定:上げられるなら隠れ家、上げられるなら作業台、最後に武器を一本作ってみる。クラフトには重要なルールがあります。作ったものが現役より確実に強い場合だけ素材を消費して残し、そうでなければ捨てて素材は消費しない。するとクラフトが頭打ちになると素材が自然に貯まって作業台が上がり、作業台が上がればより強いものが作れる。二つのエンジンが交互に前進し、追加のスケジューリングコードは不要です。

検証はヘッドレスで 12000 tick(ゲーム内 20 分)回してレポートを出す方式:勝敗数、最深到達、資源残高、隠れ家と作業台のレベル、在庫数。同じシードで 2 回回して結果が一字一句同じ、決定性は合格。

スライス 3:自作レイヤーキャラ、動いた、そして置いた

設計文書のキャラ方式はかなり凝っていました。5 層のスプライト(右腕 / 胴体 / 右武器 / 左腕 / 左手アイテム)を z 順に重ね、ボーン補間は使わず(高さ 40px のキャラを補間するとガタつく)、フレーム駆動+フレーム単位ピボットに。毎フレーム右肩・右手・左肩・左手の座標を読み、武器とアイテムを対応するピボットに貼り付けます。右手の武器クラスが唯一のドライバーで両腕のアニメセットを決め、左手アイテムは左手に受動追従、胴体は共通で両手近接攻撃時のみ前傾胴体に切り替え。

色付き矩形をプレースホルダーにして一式を動かしました:5 ステート、4 武器クラス切り替え、ミラー、ピボット可視化、すべて正しく動きます。

プレースホルダー rig 検証台:片手剣+盾(左)と両手武器の前傾攻撃(右)、色付き点が 4 つのピボット

その上で現実的な判断をしました:棚上げ。ロジックを動かすのは簡単ですが、武器クラスごとに 5 層 × 5 ステートのドット絵連番を描くのは別次元の作業量で、今の段階で時間を使うべき場所ではありません。設計図もコードも残し、プレイヤーキャラは「トラック B」——既製アセットの一枚絵連番——に切り替え。二つのトラックは同じ AnimState 列挙で駆動されるので、将来戻したくなっても戦闘コードには触りません。

アートの回り道

最初は AI にドット絵キャラを描かせようとしました。出力は速いのですが、手元に来るのはなんちゃってドット絵:見た目は格子でも実際のグリッド倍率が不均一、エッジにアンチエイリアス、1「ピクセル」の中に複数の色。元解像度に戻すとぼやけます。

AI 生成のなんちゃってドット絵小隊:遠目にはそれらしいが、拡大するとグリッドが揃わずエッジが甘い

そのためにローカルの小さなツールを書きました。画像の勾配プロファイルを FFT にかけて主空間周期を探し、実グリッド倍率と位相を得る。セルごとに主要色を取って実解像度にダウンサンプル。オプションでメディアンカットによるパレット縮減と透明背景の二値化。合成した偽ドット絵でのセルフテストは正しく復元できました。

ツールは使えましたが、結局この道は取りませんでした——修正に費やす時間より買った方が早い。現在のアートは二つのアセットパックから:ポストアポカリプステーマのパック(手なし胴体+武器レイヤーを組み合わせる主人公、3 種のゾンビ、大量の背景オブジェクト)と、ダークシリーズのモンスターパック(90×90 の横一列連番モンスター 20 種以上、6 層パララックス背景 4 セット)。どちらも有料の商用ライセンスで再配布禁止のため、art/ ディレクトリ全体を git に入れていません。

アセットパックはファイル名の大文字小文字が不統一でフレーム数がファイル名に埋め込まれているので、全小文字インデックスの ArtDB を書いて一括解決。現在 1338 クリップをインデックスしています。

表現層:今動くとこうなる

メインシーンは 760×200 の透明な細長いウィンドウで、Godot のピクセル単位透過を有効化。中身は行軍 → 遭遇 → 戦闘 → 勝利 / 敗北のループです:

  • 行軍:6 層のパララックス背景が係数に従ってスクロール、遠景 0.02、近景 1.0、空レイヤーはオンオフ可。
  • 遭遇:敵が左から歩いてきて、近接は突っ込み、ピストルとライフルはそれぞれの射程で止まる。
  • 戦闘:命中のたびにシミュレーション層の Damage.resolve_hit を呼び、ヘッドレスと同じ数学を使う。違うのは編成がフレームレート駆動になっただけ。弾は本当に飛び、薬莢は本当に飛び出して落ち、命中で白フラッシュ+1px ノックバック、ダメージ数字、ブロック印、MISS も揃っている。
  • カメラ:敵がいなければ隊の中心、いれば隊と敵群の中点。デッドゾーンと速度上限付きで、敵が一体死んだだけで揺れない。
  • 座標リベース:ウェーブ終了ごとに世界全体を標準スロットに平行移動し、カメラを同じ距離だけ逆に動かす。画面は微動だにしないが、隊員が「後ろに歩いて戻る」必要がなくなる。

経済ループは統合済み:ウェーブクリアでドロップがインベントリへ、物資と素材が入金、自動アップグレードとクラフトのイベントがトーストで流れる。戦力が届けば次の地域に進んで背景が切り替わり、敗北すれば一段浅い地域に退いてファーミング。地域は 8 段、各 10 ウェーブ、10 ウェーブ目は 1.5 倍サイズのボス。隊員は倒されるとその場に倒れて 12 秒チャージで復活し、右上に泣き顔の小アイコンが出る。非戦闘時は毎秒 5% 回復、戦闘中は回復しない。

深部:スケルトン射手が射程外で対峙、右上はステージ進行カーソル

主要な決定

決定選択理由
描画二重トラック:CharacterRig(棚上げ)/ ActorSprite(現用)、同じ AnimState設計図を残しつつ既製アセットで先に絵を出す
主力隊バット / ピストル / ライフル、同じ胴体を色調で区別アセットパックに主人公の胴体が一つしかない
死亡倒れて救助待ち、チャージ後その場で復活放置のリズムを止めない
数値表現層とヘッドレスで Combatant / Damage を共有二系統の数学はいずれ食い違う
アート有料アセットパック、git に入れないライセンスが再配布禁止

ツールチェーン

エンジンは Godot 4.6、言語は GDScript、コードは Claude Code がコマンドラインで書いています。エディタには MCP プラグインを入れ、エージェントが直接エディタを操作してシーンを実行しスクリーンショットを撮れるので、人がウィンドウを行き来する必要がありません。ヘッドレス検証は godot --headless で、完走後にレポートを出力して自動終了します。

次にやること

  • 数値テンポの調整:敵の成長、ドロップ率、地域の門はまだ仮の桁。
  • UI シェル:右側パネル、拠点への入口。プレイヤーが隊とバッグを見られるように。
  • スライス 4 遠征、スライス 5 セーブ+オフライン精算+本当のタスクバー化(枠なし、最前面、画面下端に吸着)。

この記事は Claude Code がプロジェクトのソースコードと設計文書から整理して書きました。スクリーンショットは現行ビルドのものです。

ノート一覧へ
TOP