ここ数年のサバイバーズライク、バレットヘブン、タワーディフェンスが共有してきた制作の既定が、敵ごと・弾ごと・拾得物ごとにスクリプトとライトとコリジョンをぶら下げることだとするなら、2026年10月15日にインディースタジオ Naio Games が公開した約18分の長文は、その請求書を同業者の前に広げたに等しい。題材は Godot 4 で作っているスペルクラフト型ローグライト『Hexwright』の実測である。目標場面は敵約1000、投射物778、ノード約10000に届きうる。敵が約120を超えるとフレームレートは48から32へ落ちた。理由は素朴で、その地点を越えると毎フレームが物理 tick を二つ走らせ、敵単位の仕事がすべて倍になる。
公開ガイドを通読して釘付けできる事実は単純だ。技法一式は普通の GDScript と Godot 付属のシェーダ言語に書かれ、C# も C++ モジュールも GDExtension もない。制約の一つは web ビルドを出すことであり、当時 C# の web 書き出しは使えなかった。ログで本当に遅かったのは、���くの場合 GDScript そのものではない。エンジンにオブジェクト単位の小さな仕事を、毎フレーム何百回も頼むことだった。数字を動かしたのは、その仕事の形を変えることである。
本稿を貫く意象は、種類単位で払う、の四字である。会計用語のように聞こえるが、フレーム予算に落とせば検証可能な帳面の付け替えになる。

一、業界の既定、一件ずつ個別に払う
サバイバーズライクとバレットヘブンの形は固定に近い。種類は少なく、各種が何百回も繰り返される。オブジェクト上に増えたライト1ミリ秒、コールバック一回、テクスチャ切り替え一回は、群れの規模でフレーム全体の災害に増幅される。書き方の既定も固定だ。弾は `_physics_process` を持ち、敵は `Area2D`、拾得は `PointLight2D`、軌跡は `Line2D`。エンジンが物理・描画・シグナルに繋いでくれる。書き心地は良い。請求は件数単位のままである。
Naio は痛い局所コストを並べている。拾得ライト880個が単体で約5 ms/frame を占めたことがある。敵対プレイヤーの衝突検査は敵コストの約54%だった。弾300が敵240を貫くとき、ヒット有りは7.9 ms、無しは4.9 ms。弾軌跡300は、draw call が316から28へ落ちたあとで、13.7 ms から5.0 ms へ落ちた。数字は自前のプレイテストログとベンチシーン由来だ。機種で変わるが方向は変わらない。高くつくのは、しばしば賢い AI の断片より先に、一件ずつ払うことそのものだ。
既定との分岐点はここにある。既定は各オブジェクトの挙動をどう書き切るかを問う。彼らは、同種のものが管理費を一度だけ払い、実体は自分のデータ分だけ払えないか、と問い直す。

二、帳面の付け替え、プール、種類ごとの管家、偽ライト、格子数学
最初の刃は生成と破棄だ。戦闘中の `instantiate()` はノード木を組み、`_ready()` を走らせ、物理と描画に登録する。`queue_free()` はまた解体する。ショットガン一発や一波の同時死亡のように、同一フレームに五十回集まれば目に見えるヒッチになる。対処は読み込み時に設計どおりプールを予熱し、戦闘中は acquire と release だけにすること。シーンは惰性初期で隠し、コリジョンオフ、ツリーに付けてから `activate()`。再利用のたびに見た目設定を既定値まで全部書き直す。さもないと再利用火球が前世の発光を残す。隠れたノードの物理補間は無視されるので、表示前にリセットしないと、弾は前回死んだ位置から滑り込む。プール miss はログに残し、スキルを黙って削らない。
第二の刃はコールバックの形だ。弾300がそれぞれ `_physics_process` を走らせれば、エンジンコールバックは300回になる。弾は `step(delta)` だけを持ち、`bullet_manager` が一覧を後ろから進める。解放は swap-remove で定数時間。一時停止が `time_scale = 0` なら、マネージャは即 return すべきだ。彼らが踏んだ穴は、弾と敵は止まったのに範囲術とビームが速度ゼロのまま掃き続け、レベルアップ画面で6〜14 ms を溶かしたことだ。描画も同じ。敵200の状態アイコンは、単一 `_draw()` を回す一つの描画役にまとめられる。
第三の刃はライトだ。本物の `PointLight2D` は触れた各スプライトに描画コストを足し、スプライト側にも受信ライト上限がある。発光弾50が群衆を飛べば、群衆コストがもう一度掛け算される。真ライトはプレイヤー、ボス、焚き火など少数の源だけに残す。弾と拾得の床光は加算ブレンドの柔らかい円スプライトにし、同じテクスチャと同じマテリアルでバッチさせる。夜間の `CanvasModulate` で暗くなるときは、グローバルシェーダパラメータで色を戻す。半分暗ければ×2上げ、可読性を保つ。拾得ライト880が単体で約5 ms、という数字がこの付け替えの領収書である。
第四の刃は衝突だ。必要なのが「円の中に誰がいるか」「線分が何を掃いたか」だけなら、空間格子と距離判定のほうが物理エンジンの配对より安いことが多い。毎 tick 敵を64 px セルに入れ、問い合わせは覆ったセルだけを見て、少数候補に正確距離を取る。敵は物理エンジンを離れてもよく、ヒット処理は従来の `on_hit` のまま。安定IDは instance id を使い、リスト添字は使わない。swap-remove で添字の指先が別人になるからだ。ボス半径は専用の罠だ。取りこぼしを避けるため問い合わせは最大半径ぶん外へ広がる。半径約45 px のボスが入ると、全弾の問い合わせが余分なセルを掃く。半径約32 px 超の大物は別リストに置き、格子のあとで少数だけ補完する。弾600・敵60・ボス1の場面で、physics は17.4〜18.3 ms から15.5〜15.7 ms へ、約2 ms 減った。文中でも、これは一部にすぎず、残りはヒット処理側だと明記している。
四本の刃は同じ原則を共有する。プールは戦闘中の生成税を消す。種類ごとの管家は件数コールバック税を消す。偽ライトは件数ライティング税を消す。格子数学は件数物理配对税を消す。遊び側のヒット規則はできるだけ残す。変わるのは、誰が払うかである。

三、数字はさらに下がる、アトラス、MultiMesh、変化だけ再描画、GPU 直線移動
テクスチャ合批は2Dで最も安い刃の一つで、ほぼ遊びコードを触らない。Godot は隣接スプライトのテクスチャとマテリアルが同じ間だけ自動バッチする。歩行敵が8方向×8歩を別画像だと、240体で draw call 241になりうる。余白付き atlas にまとめると2になる。計測ではフレーム時間はなお約2.2 ms で、速い GPU では余裕側の数字であり、直ちに見える fps 跳躍とは限らない。輪郭シェーダが隣フレームを拾わないよう余白を残す。マテリアルもバッチを割る。敵200のうち3体が特殊マテリアルなら余分に3バッチ。一体ずつなら200バッチだ。
さらに下へ行くと、拾得・火花・軌跡セグメントのように前後関係を他と取らなくてよい同一の小物は、ノードを一つずつ持つ必要がない。MultiMesh を RenderingServer に直接掛け、着地や回収など変化時だけインスタンスデータを書く。静止拾得の上下揺れは GPU に任せ、スクリプトコストはゼロ。軌跡も同じ。弾ごとの `Line2D` は弾ごとの draw call だ。一つの MultiMesh に環状のセグメント枠を持ち、毎 tick 時刻付きの枠を押し、シェーダが年齢で細らせ消す。300矢ベンチではフレーム全体が13.7→5.0 ms、draw call は316→28。
スキルツリーは別種の高い画面だ。六角と糸と目盛りとアイコンと価格と名前の大半は静止で、液体アニメだけが動く。全体に毎フレーム `queue_redraw()` すると、全強化後は49.9 ms/frame、うち約36 ms は同じ描画命令を再発行する GDScript で、ノード4倍では116 ms。静部を tile に切り、各 canvas item の命令リストを汚れたときだけ録り直す。パンとズームは tile の変換だけ動かす。薄い live layer が本当に動くものだけ毎フレーム描く。今日の87ノードは49.9→10.7 ms。4倍ノードは116→15.2 ms。パンは毎フレーム全再描画からゼロ再描画へ。同じ考えを一段上げると描画解像度だ。`canvas_items` 伸縮は画面画素で描き、4K は1280×720設計の約9倍画素で、全画面グローもシェーダも請求される。設定にし、`viewport` モードで設計解像度描画後に拡大、性能720と鋭利ネイティブの二択を渡す。
GPU 変位はやりすぎやすい刃だ。直線弾は始点・速度・発射時刻があれば、シェーダが毎フレーム正しい位置に置け、スクリプトは変換を毎フレーム変えなくてよい。発光弾約800、本体+暈+床光で、draw call は約1150から約480へ。フレーム時間はベンチのノイズ内で、素朴な矢300ではほぼ変わらない。同マテリアル同スプライトはもともとバッチするからだ。本番が噛みついた。弾の多くは誘導・周回・ブーメランで毎 tick 曲がり、アンカーも毎 tick 書き直し、4〜6 ms 増え、普通のノードより悪くなった。ゆえに直線弾だけが GPU 経路を行く。文末も明記する。直線弾の GPU 描画、拾得 MultiMesh、30 Hz 交互思考は当時まだ完全なプレイテストを経ておらず、他は自機実測なので読者環境では数字が違う。

四、予熱、交互思考、遊びを変えないという硬い境界
読み込み幕がまだ掛かっているあいだ、エフェクト実体を視野内に数フレーム置き、シェーダパイプラインを先にコンパイルさせる。画面外はカリングされ、永遠にコンパイルされない。プールは設計ピーク、キャストあたり投射物×毎秒キャスト×生存秒で予熱する。初回火球のヒッチは、多くの場合マテリアル初描画時のコンパイルだ。Windows ビルドは既定 Direct3D 12 で、コードも描画も暇な100〜600 ms の稀な停滞は GPU パイプラインコンパイルのことが多い。起動で Vulkan に切り替えられ、Steam 起動項目で第二の Play も作れる。
思考は移動より稀でよい。移動は毎 tick 必要で、さもなくば群れが震える。行き先決定はそうではない。群れの半分が交互 tick で考え、約30 Hz、前回思考からの累積時間を渡してクールダウンは実時間を保つ。ボスと溜め中の敵は毎 tick 思考する。文中の規模感では、思考は敵1体8.7 µs のうち1.85 µs、1000体で約0.9 ms/tick の節約。交互は死亡に耐える必要がある。分離が毎 tick 半分だけを見て、死亡で全量再走査すると、毎 tick 何かが死ぬため得が消える。計算結果を敵に付き添わせると、分離は5.4→2.9 ms。ブラウザは約1.4〜1.7倍遅く、`.web` 上書きで web に小さい群れ上限を渡す。
文章は同じ硬い境界に何度も戻る。最適化は実装を差し替えてよい。手触りを黙って変えてはならない。格子ヒットは進入時に一度。一時停止で拾得は凍る。見た目側の再建は旧ヒット処理を呼び続ける。ボス入場は単独で測る。物理コールバック内で物理オブジェクトを増減しない。追いつきステップ上限は2にし、遅い1フレームが雪だるまにならないようにする。エディタ F5 デバッグは GDScript を2〜3倍遅くし、31〜35 ms 対11〜19 ms。正式数字はプレイヤーが受け取る release 書き出しで測り、任意で `-- --perflog`。忙しいマシンは嘘をつき、同じベンチが9.4対3.0 ms、3倍差になりうるので A、B、再A。ヘッドレスは draw call・ライト・バッチを測れない。平均はカクつきを隠し、最悪フレームも必ず記す。
ゆえに種類単位で払う、という独立チーム向けの実行意味は具体的だ。何百もあるものは何かを先に分ける。ライト、軌跡、コールバック、物理配对、テクスチャ切り替え、全画面再描画。ログで最大の項目から請求の形を変える。ベンチシーンにスイッチを付け、ヒット・軌跡・見た目を一つずつ切り、一問だけ答える。最初から C++ モジュールで救われる幻想を持たない。彼らのログは、遅いのはしばしば「オブジェクトごとの小さな一歩」が何百倍にされたことだと書く。
結び、独立性は帳面の付け替えのなかで値付けし直される
このガイドを2026年秋の Godot 制作現場に戻すと、まず打たれるのは静かな制作慣性だ。最適化はより速い言語だと既定し、性能の痛みは後でネイティブプラグインが消すと既定し、サバイバーズのカクつきはジャンル税で数を減らすしかないと既定する。Naio は別の道を開く。言語は GDScript のままでよい。高いのは仕事の形だ。件数請求を種類請求に変えれば、フレーム予算はついて動く。
彼ら自身のチェック表は作業指示書に近い。弾や拾得に真ライトがあるなら、加算グローと夜の持ち上げパラメータを試す。弾ごとの `Line2D` 軌跡なら MultiMesh セグメント。戦闘中の `instantiate` と `queue_free` なら予熱プール。プール化オブジェクトごとの `_process` なら種類ごとの管家。敵と弾ごとの `Area2D` なら格子と円/カプセル数学。アニメフレームごとの一枚絵なら余白付き atlas。同一の小スプライト何百なら RenderingServer 上の MultiMesh。毎フレーム全弾をスクリプト移動ならインスタンスデータのアンカー、変化時だけ書き換え。初回エフェクトのヒッチなら読み込み時シェーダ予熱。大きくてほとんど静止の画面の毎フレーム `queue_redraw` なら保持 tile と薄い live layer。4K が720美術を描いているなら描画品質オプション。敵が毎 tick 意思決定するなら30 Hz 交互、移動は毎 tick。web がデスクトップ群れ上限のままなら `.web` 上書き。感覚的に重いだけで始めるなら、まず本番対局の性能ログを引き、最大の数を直す。
実行可能な指示は短い。
- 件数で増幅される一覧を書く。ライト、コールバック、物理、draw call、全画面再描画。何百倍で死ぬ三項目を先に形から変える。
- 再現可能なベンチ戦をスイッチ付きで作る。ヒット・軌跡・見た目を別々に切る。一問だけ答える。同機で A、B、再A。
- 衝突や描画を差し替えるとき、進入時一回ヒット、一時停止凍結、ボス半径を回帰する。見た目と判定は同じ提出で受け入れる。
- 直線弾は GPU に載せてよい。誘導とブーメランはまず CPU。プールとシェーダは予熱するが、miss をスキル削減で隠さない。遊び規則は感想よりテストに書く。
10月15日の長文はバレットヘブンを簡単にはしない。繰り返すものに何の代金を払っているかを、値付けし直すだけだ。帳面を付け替える気があるチームには、前後数字付きの公開宿題と、文末にすでに書かれた注意がある。まず最大の数を測る。それから件数払いをやめる。

出典と延伸読書
Naio Games(2026-10-15)Godot 4 Performance in Pure GDScript, Many Things on Screen, No Lag
Hexwright 製品頁(spell-crafting roguelite、開発中)https://naiogames.com/hexwright