CesiumForUnityのDynamicCameraの紹介と詳解


どうも初めまして、K-kunです。
A-kun ひとり Advent Calendar 2024の11日目です。

## はじめに
今日は、Cesium for Unity の Dynamic Camera について紹介します。
Unityにおいて一人称視点のカメラを用意するのは何かと手間ですが、Cesium for Unityでは簡単に導入することができるコンポーネントを用意してくれています。それが、Dynamic Cameraです。

また、Unityで原点から離れた位置にカメラなどがあるとfloat型の誤差から描画が不安定になることがありますが、Dynamic Cameraでは「カメラは動かず、地図を動かす」という方法で解決をしてくれています。

今回はDynamic Cameraの使い方や、各種コンポーネントの紹介を行います。

## この記事を読んで欲しい人
- Cesium for Unityで一人称視点のカメラを導入したい人
- とりあえず何ができるか知りたい人
- 今日のご飯がおにぎりだけだった人（これっきりこれっきり〜も〜う〜〜）

## Cesium for Unityについて
Cesium for Unityは、Unity上で立体地図を表示できるサービスです。同様のMapbox SDKや ArcGIS SDKなどがあります。広く使われていることと、無料で使えること、Googleの3D Tilemapや国土交通省のPLATEAUもストリーミングで読み込めるなど、機能が豊富です。自分のGISデータをCesium ionに登録することで、Unity上でストリーミングで読み込むこともできます。

## Dynamic Cameraの導入方法

インストール方法については公式が詳しく解説してくれていますので、そちらを参照してください。

https://cesium.com/learn/unity/unity-quickstart/

CesiumのタブからCesiumのwindowを開き

[![Image from Gyazo](https://i.gyazo.com/26e4132b19bb97c01d7767291000eb2e.png)](https://gyazo.com/26e4132b19bb97c01d7767291000eb2e)

Dynamic Cameraの右にある+ボタンを押すだけです。

[![Image from Gyazo](https://i.gyazo.com/1be64d16749ebe8598c859d67c964fb8.png)](https://gyazo.com/1be64d16749ebe8598c859d67c964fb8)

すでに開かれているtileがあれば、CesiumGeorefarenceオブジェクト直下に配置されます。

[![Image from Gyazo](https://i.gyazo.com/8dcd266269a0451301c4796b229732f7.png)](https://gyazo.com/8dcd266269a0451301c4796b229732f7)

## Dynamic Cameraに追加されているコンポーネント

Dynamic Cameraに含まれているコンポーネントを機能に分けるとこんな感じです。

- Unity標準のもの
  - Camera
  - Audio Listener
- CesiumForUnityのもの
  - カメラを移動させるコンポーネント
    - Cesium Camera Controller
    - Cesium Fly To Controller
  - カメラの位置を原点に固定するコンポーネント
    - Cesium Globe Anchor
    - Cesium Origin Shift

## Globe Anchor, Origin Shiftの機能

Cesium Globe Anchor, Cesium Origin Shift は、Dynamic Cameraの位置を原点に固定します。試しにUnityを再生しながらDynamic Cameraの位置を変えてみて下さい。Dynamic Cameraの位置は変わらず、地図だけが動いているように見えると思います。

## Cesium Globe Anchor

このクラスはゲームオブジェクトを地球上にアンカー（固定）します。高い精度でオブジェクトを地球上の任意の場所に配置でき、CesiumGeoreferenceの原点が変更されても、正しい位置に維持されます。

1. 依存関係:
このコンポーネントを持つゲームオブジェクトは、CesiumGeoreference 内にネストされている必要があります。そうでない場合、OnEnable メソッド内で例外がスローされます。

2. 移動の許可:
アンカーされたゲームオブジェクトは移動可能です。移動はこのクラスのプロパティを介して、またはゲームオブジェクトのTransformを直接更新することで行えます。
Transform の変更を検知するには、detectTransformChanges プロパティをtrueに設定する必要があります。

3. 地球に基づく回転の調整:
adjustOrientationForGlobeWhenMoving を有効にすると、地球表面の曲率に基づいてオブジェクトの向きを自動的に調整します。

4. Transform の初期値:
通常、このコンポーネントを持つオブジェクトの祖先の Transform は「単位変換」である必要があります（位置: 0、回転: 0、スケール: 1）。ただし、意図的なオフセットを設定する場合は例外です。

### adjustOrientationForGlobeWhenMoving

地球の曲率に基づいてオブジェクトの向きを調整するかどうかを設定します。有効にすると、地球表面を移動する際に、オブジェクトが常に正しい方向を向くようになります。

### detectTransformChanges

ゲームオブジェクトのTransform の変更を自動的に検知し、その変更を地球座標に反映するかどうかを設定します。無効の場合、変更を手動で反映する必要があります。

### localToGlobeFixedMatrix

ゲームオブジェクトのローカル座標系から地球中心座標系（ECEF）への変換を定義する4x4行列を取得または設定します。

### longitudeLatitudeHeight

オブジェクトの経度（X）、緯度（Y）、高さ（Z）を取得または設定します。高さはWGS84楕円体に基づいて測定されます。

### Sync

このコンポーネントのプロパティを同期します。たとえば、Transform の変更を検出し、地球座標に反映させる際に使用します。

### Restart

このコンポーネントの状態を再初期化します。OnEnable メソッドで自動的に呼び出されますが、特定の状況（例えば、Undo/Redo操作後）で手動で呼び出すことも可能です。

## Cesium Origin Shift

このクラスは、指定されたオブジェクトが移動するにつれて、CesiumGeoreference の原点を自動的に変更します。これにより、座標値を小さく保つことで、レンダリング精度が向上します。

1. カメラへの利用:
- 通常、このコンポーネントはカメラにアタッチされ、カメラの位置に基づいてCesiumGeoreferenceの原点を自動的に更新します。
- これにより、カメラ周辺のオブジェクトの座標値を小さく保ち、レンダリングの精度が向上します。

2. 依存関係:
- このコンポーネントを持つゲームオブジェクトは、CesiumGeoreference 内にネストされている必要があります。
- また、ゲームオブジェクトにはCesiumGlobeAnchorも必要です。

3. 他のオブジェクトへの影響:
- シーン内のすべてのオブジェクトにCesiumGlobeAnchorを追加する必要があります。そうしないと、原点をシフトした際にこれらのオブジェクトが移動しているように見える可能性があります。

4. サブシーンの切り替え:
- 距離に基づいてCesiumSubSceneを切り替える機能も持っています。
- サブシーン内にいる場合、原点のシフトは行われません。これにより、地球上の異なる場所で通常のUnityシーンを定義できます。

### distance

Unity座標系の原点と、このコンポーネントがアタッチされたオブジェクトとの最大距離を定義します。この距離を超えると、CesiumGeoreferenceの原点がオブジェクトの近くに移動します。

- 値が0.0の場合: 原点は継続的にシフトされます。

### LateUpdate

1. CesiumGeoreferenceの確認:
現在のゲームオブジェクトがCesiumGeoreference内に含まれているかをチェックします。存在しない場合、警告を表示します。

2. CesiumGlobeAnchorの確認:
ゲームオブジェクトにCesiumGlobeAnchorがアタッチされているか、または有効かを確認します。有効でない場合、警告を表示します。

3. 原点の更新:
現在のECEF（地球中心座標）に基づいて、原点を更新します。

### UpdateFromEcef

1. サブシーンの切り替え:
- 現在の位置がCesiumSubSceneのアクティベーション半径内にある場合、そのサブシーンを有効にします。
- それ以外の場合、すべてのサブシーンを無効にします。

2. 原点の移動:
- 現在のECEF座標が距離制限を超えた場合、CesiumGeoreferenceの原点を移動します。

## Ceisum Camera Controller

地球全体を移動し、見ることができるカメラコントローラー。このコントローラーは、カメラが地平線を移動するときに「上方向」を自動的に調整し、世界が常に正しい向きに見えるようにします。

### enableMovement

このコントローラーで移動を有効にするかどうかを設定します。移動は以下のキーで制御されます:
- W, A, S, D: 水平移動
- Q, E: 垂直移動

### enableRotation

回転を有効にするかどうかを設定します。回転はマウスの動きで制御されます。

### defaultMaximumSpeed

動的速度が無効になっている場合のコントローラーの最大速度を設定します。動的速度が有効の場合、この値は無視されます。

### enableDynamicSpeed

動的速度を有効にするかどうかを設定します。有効にすると、標高や他の要因に基づいて速度が動的に変化します。

### enableDynamicClippingPlanes

カメラのクリッピングプレーンを動的に調整して、遠くにある地球がクリッピングされないようにするかどうかを設定します。ただし、カメラに近いオブジェクトであっても、地球から大きく離れた場合には表示されない可能性があります。

### 注意点とその他の情報

#### Awakeメソッド:
このクラスが有効になると、CesiumGeoreference や CesiumGlobeAnchor など必要なコンポーネントを自動的に確認し、適切に初期化します。

#### 回転機能:
水平回転はY軸、垂直回転はX軸を基準に実行され、視野が自然に調整されます。

#### 動的速度:
カメラの位置や高さに基づいて速度が自動的に調整され、スムーズな移動が可能です。

#### 動的クリッピング:
地球の中心からの距離をもとにクリッピングプレーンを動的に調整し、視界の最適化を実現します。

## Cesium Fly To Controller

地球上の任意の位置に滑らかに移動（フライト）できるコントローラ。フライトの特徴を調整するためのさまざまな制御が可能です。このコントローラは CesiumCameraController と互換性があります。フライト中は必要に応じて CesiumCameraController の入力（例えば、マウスによるカメラ回転）を無効化します。

### flyToAltitudeProfileCurve

コントローラがフライト中に達成すべき最大高度に対する割合を、時刻に応じて指定するためのカーブ。

### flyToProgressCurve

フライト全体の進行状況（％）を決定するために使用されるカーブ。他のカーブの計算基準としても機能します。

### flyToMaximumAltitudeCurve

フライト中の各時点での最大高度を指定するカーブ。

### flyToDuration

フライトの総時間（秒単位）。

### イベント（OnFlightComplete / OnFlightInterrupted）

フライトが完了・中断した際に発生するイベント。

### フライト機能（例: FlyToLocationEarthCenteredEarthFixed）

指定された地球中心座標（ECEF）の目的地に向けて、滑らかなフライトを開始します。フライトの終了時には、指定されたヨー角とピッチ角に設定されます。

このフライトの特性は、以下のプロパティで調整可能です。
- flyToAltitudeProfileCurve
- flyToProgressCurve
- flyToMaximumAltitudeCurve
- flyToDuration
- flyToGranularityDegrees

## まとめ

今回は、Cesium for UnityのDynamic Cameraについて紹介しました。Dynamic Cameraは、Unityで一人称視点のカメラを導入するのに便利なコンポーネントです。また、Cesium for Unityには他にも様々な機能がありますので、興味がある方は公式サイトを参照してみてください。