ポイント、トライポイント、および座標システム

この文書では、CBN の座標系と、進行中の tripoint → 型付き座標への移行で座標関連コードを変更する際に貢献者が使うべきパターンを説明します。目的はゲームロジックから生の tripoint/point をなくし、従来の変換関数を統合し、コンパイル時に座標の意図を明確にすることです。

軸

本ゲームは三次元であり、軸は以下のように設定されています:

  • x軸 ディスプレイ上を左から右へ進む方向(非アイソメトリックビューの場合)。
  • y軸 ディスプレイ上を上から下へ進む方向。
  • z軸 垂直方向で、負のzは地下を指し、正のzは上空を指します。

座標システム

CBNでは、様々な目的のために多様な座標システムが使用されています。これらはスケールと原点が異なります。

最も精度の高い座標はマップマス (ms) 座標です。これらは、通常ゲームをプレイする際に見るタイルを指します。

マップマス座標には3種類の一般的な原点があります:

  • 絶対座標(グローバルとも呼ばれます)は、固定された原点を基準とするゲーム全体のシステムです。
  • バブル座標は、現在の「リアリティバブル」、つまりアバターをおおよその中心とする map の隅を基準とします。ローカルマップマス座標では、x と y の値はどちらも [0, MAPSIZE_X) の範囲になります。
  • 車両座標は現在の車両のピボットを基準とし、車両とともに回転します。この原点は特殊なので、正しく動作させるには mount_to_* および *_to_mount 関数を使用する必要があります。

車両スケール (veh) もありますが、マウント関数だけで使われ、現在はマップマス座標と同じです。車両座標でのミスを難しくするためだけに存在します。

次のスケールはサブマップ submap (sm) 座標です。1つのサブマップは 12x12 (SEEXxSEEY)のマップマスです。サブマップは、現実バブルに出入りする際に、マップのチャンクがロードまたはセーブされる際のスケールです。

その次はオーバーマップ地形 overmap terrain (omt) 座標です。1つのオーバーマップ地形は 2x2 のサブマップです。オーバーマップ地形は、ゲーム内のマップビューで単一のタイルに対応し、マップ生成処理のチャンクのスケールです。

次に メモリーマップ領域 (mmr) 座標があります。現在はサブマップの保存と読み込みにのみ使われるため、遭遇することはほとんどありません。

セグメント (seg) 座標はメモリーマップ領域と同様に使われますが、より大きなスケールです。

最も大きいのはオーバーマップ overmap (om) 座標です。1つのオーバーマップは180x180 (OMAPXxOMAPY) のオーバーマップ地形です。大規模なマップ生成処理(例:都市のレイアウト)は、オーバーマップ単位で行われます。

絶対座標、バブル座標、車両座標に加えて、より大きなスケールに対する相対座標を使用する必要がある場合もあります。例えば、単一のオーバーマップに対してマップ生成処理を行う場合、そのオーバーマップ内の座標で作業したいと考えます。これは、それを含むオーバーマップの角を基準としたオーバーマップ地形スケールのポイントとなり、通常 [0,180)の範囲の x 値と y 値を持ちます。

垂直座標

x 座標と y 座標はこれら全てのスケールで機能しますが、z 座標は全てのコンテキストで一貫しています。これらは範囲 [-OVERMAP_DEPTH,OVERMAP_HEIGHT]内に存在します。

乗り物座標

各乗り物 には独自の原点があり、それは乗り物の特定の部位に配置されます(例:運転席にあるかもしれません)。乗り物が損傷し、その位置にある全ての車両パーツが破壊された場合、原点は移動する可能性があります。

乗り物は、その原点に対する相対的な2つの座標システムを使用します:

  • 車両 / マウント座標は、乗り物が移動しても変化しない車両パーツの位置を提供します。これは、乗り物が真東を向いているとき、そのパーツが車両原点に対して持つマップマスです。
  • マップマスは原点に対するマップマスですが、乗り物の現在の向きを考慮に入れます。

乗り物の向きは、四分の一回転とシアリングを組み合わせ、四分の一回転の間を補間して実装されます。乗り物のマウント座標とマップマス座標間の変換は複雑で、vehicle::abs_to_mount() および vehicle::mount_to_bubble() 系の関数で処理されます。

現在、乗り物のマウント座標には z レベル成分がありませんが、乗り物のマップマス座標にはあります。z 座標は車両原点を基準とします。これは型付き tripoint への移行に伴って変更される可能性があります。

ポイント型

これらの座標システムを扱うために、様々な型が用意されています。これらは coordinates.hで定義されています。例えば、絶対マップマス座標には point_abs_ms があります。この型名は、次元 次元 _ 原点 _ _スケール_の三つの部分から構成されています。

  • dimension 2次元の場合は point 、3次元の場合は tripoint
  • origin 値が何に対する相対値かを示し、以下が可能です:
    • rel 任意の点に対する相対値。これは共通の原点を持つ2つの点を減算した結果です。例えば、アバターと、アバターが射撃しているモンスターとの間のオフセットを表すために使用されます。
    • abs グローバルな絶対座標。
    • bub リアリティバブルの角に対する相対値。
    • mnt 車両の基準点に対する相対値で、回転済みの座標。
    • sm サブマップの角に対する相対値。
    • omt オーバーマップ地形の角に対する相対値。
    • om オーバーマップの角に対する相対値。
  • scale 上記で説明されたスケールを意味します。
    • ms マップマス。
    • veh 車両マウント座標 (mnt 原点にのみ関連)。
    • sm サブマップ。
    • omt オーバーマップ地形。
    • seg セグメント。
    • om オーバーマップ。

生のポイント型

原点とスケールが型にエンコードされたこれらの型に加えて、単に point および tripointと呼ばれる単純な生のポイント型が存在します。これらは特定のゲームスケールを意図しない場合に使用できます。

執筆時点では、コードベースをこれらの生のポイント型をあらゆる場所で使用する慣行から移行させるプロセスが進行中です。そのため、より型安全なポイント型が適切と思われる箇所で、レガシーコードがこれらの生のポイント型を使用しているのを目にする可能性があります。

コードでは、可能な限り座標システムを含む型を優先して使用すべきです。

基本原則は、tripoint や point が世界内の位置(またはそこから派生した基準座標系)を表すなら、型付き座標を使うことです。

ポイント型間の変換

スケールの変更

原点を変更せずにポイントのスケールを変更するには、project_toを使用します。例:

point_abs_ms pos_ms = get_avatar()->abs_pos().xy();
point_abs_omt pos_omt = project_to<coords::omt>( pos_ms );
assert( pos_omt == get_avatar()->abs_omt_pos().xy() );

同じ関数 project_to は、スケールを大きくする場合(粗くする)または小さくする場合(細かくする)の両方で使用できます。より粗い座標システムに変換する場合、当然ながら精度は失われます。剰余 が必要な場合は、代わりに project_remainを使用する必要があります。

project_remain を使用すると、より粗い座標システムに変換すると同時に、その粗いポイントに対する剰余をキャプチャできます。これは、結果の2つの部分をキャプチャするために std::tie と組み合わせて使用​​することを意図したヘルパ構造体を返します。例えば、アバターがどのオーバーマップ内にいるか、そしてそのオーバーマップ内でどのオーバーマップ地形内にいるかを知りたい場合を想定します。

point_abs_omt abs_pos = get_avatar()->abs_omt_pos().xy();
point_abs_om overmap;
point_om_omt omt_within_overmap;
std::tie( overmap, omt_within_overmap ) = project_remain<coords::om>( abs_pos );

これは二次元の point 型では意味を成しますが、 tripointはどのように扱われるでしょうか? z座標は水平方向の次元に合わせてスケーリングされないため、z値は project_to および project_remainによって不変であることを思い出してください。しかし、 project_remain では、結果の両方の部分でz座標を複製したくありません。そのため、結果のちょうど一方を tripointになるように選択する必要があります。上記の例では、z座標はオーバーマップスケールではあまり意味がないため、おそらく omt_within_overmapにz座標を持たせたいでしょう。これは次のように行えます:

tripoint_abs_omt abs_pos = get_avatar()->abs_omt_pos();
point_abs_om overmap;
tripoint_om_omt omt_within_overmap;
std::tie( overmap, omt_within_overmap ) = project_remain<coords::om>( abs_pos );

ポイントのリスケールで利用できる最後の操作は project_combineです。これは project_remainとは逆の操作を実行します。2つのポイントが与えられ、2番目のポイントの原点が1番目のポイントのスケールと一致する場合、それらを単一の値に結合できます。上記の議論から予想されるように、これら2つのうち一方は tripointにすることができますが、両方はできません。

tripoint_abs_omt abs_pos = get_avatar()->abs_omt_pos();
point_abs_om overmap;
tripoint_om_omt omt_within_overmap;
std::tie( overmap, omt_within_overmap ) = project_remain<coords::om>( abs_pos );
tripoint_abs_omt abs_pos_again = project_combine( overmap, omt_within_overmap );
assert( abs_pos == abs_pos_again );

原点の変更

project_remain と project_combine は、リスケーリングに特に関連する原点の変更を容易にしますが、バブル座標や車両座標への、またはそれらからの変換には、特定の map オブジェクトまたは vehicle オブジェクトが必要です。

バブル変換 - abs_to_bub / bub_to_abs を使う

バブル座標を手計算してはいけません。マップの変換関数を使用します:

// 悪い例 - 手動計算は間違いやすい
tripoint local = p - tripoint( abs_sub.x() * SEEX, abs_sub.y() * SEEY, 0 );

// 良い例
tripoint_bub_ms bub = here.abs_to_bub( abs_ms_pos );
tripoint_abs_ms abs = here.bub_to_abs( bub_ms_pos );

呼び出し側で get_map(). を必要としない自由関数のオーバーロードも、一般的な型付きバリアントすべてに存在します:

abs_to_bub( tripoint_abs_ms )  →  tripoint_bub_ms
bub_to_abs( tripoint_bub_ms )  →  tripoint_abs_ms
abs_to_bub( tripoint_abs_sm )  →  tripoint_bub_sm
bub_to_abs( tripoint_bub_sm )  →  tripoint_abs_sm
// …および2D point_ バリアント

これらの関数はマップオブジェクトを取得する必要があるため、性能が重要な場所では使用しないでください。照明、影、その他のキャッシュ計算が特に該当します。可能であれば、大きな反復処理の前にマップをローカル変数として保持してください。

バブル空間の z は常に絶対 z レベルです。

「バブル空間」とは、tripoint_sm_ms が「このサブマップの左上からのマップマス」を意味するのと同じく、リアリティバブルの左上隅から測った座標です。x と y はバブル基準ですが、バブルは垂直には移動しないため z は絶対値です (vertical_shift は abs_sub.z() だけを更新し、グリッドを再配置しません)。したがって:

bub.z() == abs.z()    // 常に真

これは XY だけを変換する abs_to_bub と bub_to_abs によって保証されます。z に abs_sub.z() を手動で足したり引いたりしないでください。常に無意味で、バグの原因になります。

ポイント操作

我々は標準的な算術演算を演算子オーバーロードとして提供していますが、バグを防ぐためにそれらを制限しています。例えば、ほとんどのポイント型は定数を掛けることができませんが、rel 原点を持つ型は可能です(「同じ方向に半分の距離」と言うのは理にかなっているため)。

同様に、一般に2つのポイントを足し合わせることはできませんが、一方のポイントが rel 原点を持っている場合、または一方のポイントが生のポイント型である場合は可能です。

距離を計算するために、要件に応じて様々な関数が利用可能です: square_dist、trig_dist、rl_dist、manhattan_dist。その他の関連ユーティリティ関数には、 direction_from および line_toが含まれます。

同じ型の近隣のポイントを反復処理するには、closest_points_firstを使用できます。

非推奨 → 推奨される参照

非推奨 推奨
here.bub_to_abs( tripoint ) here.bub_to_abs( tripoint_bub_ms( p ) )
here.abs_to_bub( tripoint ) here.abs_to_bub( tripoint_abs_ms( p ) )
ms_to_sm_copy( p ) project_to<coords::sm>( p )
sm_to_ms_copy( p ) project_to<coords::ms>( p )
sm_to_omt_copy + sm_to_omt_remain project_remain<coords::omt>( p )
omt_to_ms_copy( omt ) + offset project_combine( omt, offset )

利用可能なテンプレートヘルパー

以下はすべて実装済みで利用できます:

ヘルパー 場所 目的
project_to<S>(p) coordinates.h 原点を保持したスケール変換
project_remain<S>(p) coordinates.h 商と剰余への分解
project_combine(coarse, fine) coordinates.h 商と剰余の再結合
abs_to_bub(p) / bub_to_abs(p) map/map.h(自由関数) バブル ↔ 絶対
p.reinterpret_as<T>() coordinates.h 移行用足場でのみ使う明示的な型変換
IsCoordPoint<T> 概念 coordinates.h 型付き座標にテンプレートを制限
rl_dist(a, b) 型付きオーバーロード line.h 同じ型の coord_point ペアを受け付ける
ch.bub_pos() / ch.abs_pos() creature.h 型付きクリーチャー位置アクセサ

reinterpret_as<T>() は安全でない原点変換を明示的かつ検索可能にする移行用足場です。この呼び出しが残るコードは、まだ完全には移行されていません。

絶対空間とバブル空間

変換が簡単な場合、ゲームロジックでは 絶対空間 を優先してください。次の場合はバブル空間が適しています:

  • レンダリングと表示コード
  • リアリティバブルと本質的に結び付いた関数(例: map::shift)
  • 変換が容易ではなく、変更範囲外であるコード

レガシーコードは、便利なローカルフレームとしてリアリティバブルに依存していました。単純な置換で直せる場合は修正し、より広範な手術が必要なら次へ進んでください。