FIELD NOTES
NOTE 01 · BUILDING THE FOREST
森を、検証できるものとして作る
地形をどうベイクしたか、遠景をなぜ切り離したか、そして町がなぜ戦場より難しいのか。
以下は開発者個人の記録で、内部マイルストーン、技術判断、失敗の経緯を含みます。現在の状態を述べるものであり、最終形でも、リリースの約束でもありません。
森は「見栄えが良くなった」では完了しない
このプロジェクトでは、ワールドが二つの身元を持っている。
一つは目に見える森。光の筋、苔、水音、遠くの樹冠。 もう一つはオンライン権威から見た森。固定された高さフィールド、セマンティックハッシュ、 整数座標の集合。
この二つは分けておかないとオンラインが壊れる。だから森に手を入れるときは毎回、 まず「いま動かしているのはどちらか」に答えることから始まる。
一、地形はベイクされたもので、エディタで並べたものではない
addons/terrain_pipeline/ は決定論的なオフラインのベイクチェーンだ。出力は固定されている。
- 連続した
20×20 mの地形メッシュ; - 共有のマテリアルウェイトと AO マスク;
- 簡略化されたコリジョン;
- 連結されたナビゲーション;
42枚のスラブ、38の森インスタンス、28の遺跡インスタンス;26/26の Sector MultiMesh がシリアライズ済みバッファを持ち、108のトランスフォームが すべて有効で空間的に重複しない。
なぜこの数字を文書に書くのか。これが「今回の変更が何かを壊していないか」を判断する唯一の根拠だからだ。 地形は手で並べたものではなく、スクリプトが出したもので、同じ入力からは同じ結果が出なければ、 このチェーン全体に意味がない。
v1 のこれらのファイルはハッシュで固定され、不変だ。v2 をやるということは、
新しい *_v2.gd ファイル、新しいランタイムオプション、専用の検証器、専用の証拠ディレクトリを
作るということで、古い方は 1 バイトも触らない。
二、地形セマンティックハッシュと、その防火壁
オンライン権威は地形 v1 のセマンティックハッシュ c1e82575… に固定されている。
これは P1 のコンテンツバンドルに封じられていて、コンテンツバンドルハッシュ 09a0de4a…、
森 54ccd449…、町 411b6aaa… と一緒にオンライン ID を構成する。
つまり地形 v2 は表現レイヤーと P0B のオフラインコリジョン用であって、P1 の権威を変えられない。 本当に差し替えるなら、それは別途承認されたコンテンツバンドルの再構築であって、 「ついでにアップグレード」ではない。
このハッシュは三か所に打ち込まれていて、一致していなければならない。
p0b_combat_slice_v1.gdのバックエンドテーブル;p0b_combat_slice_v1_structure_test.gd;scripts/automation/validate_p0b_combat_slice_v1.py。
三つがずれれば検証器がそのまま落ちる。この制約を入れたのは、自分が将来必ず忘れると確信しているからだ。
三、マップが端で切れてはいけない
森の戦場の合法な境界は 25 m。ゲームプレイ上は問題ないが、画としては良くない。
ある位置で世界が急に終わり、その先に何もないのが見えてしまう。
そこで純表現の遠景レイヤーを足した。戦場側は 192×192 m の不透明な地面の続きで、
固定された 64×64 m の表現地形の下と外に敷かれる。さらに有界の透明な森の地平線アークが
プロダクションカメラの焦点に追従し、地面の続きの方はワールド固定のまま。
町側は曲面の、ミップマップ付き透過の森の幕で、既存の TownPresentation サブツリーに入っている。
--p1-town-presentation=off でレイヤーごと消える。
これらの遠景メッシュはすべて影と GI を切り、異方性ミップマップフィルタを使う。 コリジョン、ナビゲーション、ラベル、権威状態、プロトコル状態、ゲームプレイ効果は 一切含まない。
テクスチャは生成物だ(戦場 1024×1024 不透明、町 2048×768 アルファ付き)。
ここで私が気にしている規律が一つある。唯一の正のスタイル入力は、ハッシュ付きのワールド基準画像だけだ。
ランタイムのキャプチャは、カメラ・スケール・マップ端の診断入力としてバイト保存されるが、
正のスタイルやパレットの入力ではない。細かい区別に聞こえるが、
これがスタイルが少しずつ流れていくかどうかを決める。
町のテクスチャは下端が完全に不透明でなければならない(透明ピクセル 0/2048)。
さらに高彩度マゼンタの滲みチェックを通す必要がある。透過の抜きは端に色縁を残しやすく、
その端がちょうどプレイヤーの視線の高さに来るからだ。
四、町は戦場より難しい
戦場は境界があり、制御でき、一度に一つのことを解けばいい。町はそうではない。
町では同時に、複数の旅人が居合わせ、名前はバイトをそのまま保って表示され (Unicode が飲み込まれてはいけない)、チャットはプレーンテキストとして描画され、 キャラクターが木の裏に回ったら樹冠がフェードし、ライティングは可読性を保ち、 その上で性能が落ちてはいけない。
樹冠のフェードは特に厄介だった。カメラのオクルージョンフェーダーと地形シーンのあいだには
硬い契約がある。Modules サブツリーは MultiMesh、TRANSFORM_3D、カラー有効、
カスタムデータなし、単一サーフェスの StandardMaterial3D でなければならない。
地形 v2 が実装を変えるのは構わないが、このノード名と形は保たなければならない。
さもないとフェードは静かに効かなくなる——エラーは出ず、ある日キャラクターが木に隠れて見えなくなるだけだ。
五、いま「森が壊れていない」をどう判断しているか
スクリーンショットでは判断しない。スクリーンショットは嘘をつく。
静的キャプチャで被写界深度が更新されていなかったり、無関係な既定値の変更で
ライティングが変わっていたりする。判断は検証器を走らせて *_OK が出るかで行う。
- 起動チェック
startup_check.sh; - 森の戦場のエクスポート検証(遠景レイヤー、樹木オクルージョン、遺跡の透明度といった リソースの正確なハッシュを束ねる);
- P1 コンテンツ ID の再計算
build_p1_content_v1.py --check; - 構造テスト。
これらが全部緑になってから画を見る。順序を逆にすると、 きれいなスクリーンショットのために間違った状態を受け入れ続けることになる。
森はまだ育っている。町のディテール、遠景の層、端の可読性はどれも反復中で、 この記事が書いているのは今の姿だ。