ランドマーク
ランドマークとは、点、辺、ポリゴンなどの空間データで、現実世界のデータを仮想コンテンツのアンカー設定や位置調整の際に役立つパーツに分解したものです。例えば、顔の場合は目、口、耳がランドマークとなり、検出された AR 平面の場合はポリゴンサーフェスと境界矩形がランドマークとなります。どちらの場合も、検出された顔や平面の中心点ではなくランドマークに仮想コンテンツを接続する必要があります。
このセクションでは、Unity MARS で利用できるさまざまな ランドマーク と、スクリプトを使用してランドマークにアクセスする方法、独自のカスタムランドマークを定義する方法について説明します。
Unity MARS オブジェクトへのランドマークの追加
ランドマークを追加するには、アクションを使用します。ランドマークは、顔、平面、その他さまざまなタイプのデータに追加できます。
また、Rule (ルール) のワークフロー を使用してランドマークを作成し、設定することもできます。
平面のランドマーク
平面のランドマークは平面の条件 (IsPlaneCondition
や PlaneSizeCondition
など) が指定されたプロキシに追加できます。これを行うには、プロキシの Inspector ウィンドウで Add MARS Component ボタンをクリックし、Action > Plane Landmarks の順に選択します (1)。
そのアクションの Inspector で、Add Landmark ボタンをクリックし (2)、ドロップダウンメニューから 1 つのオプションを選択します。これで、選択したオプションに基づいて、現在選択しているプロキシの子として新しいゲームオブジェクトが作成されます。新しく作成されたランドマークを選択し、Inspector で編集します。
顔のランドマーク
フェイストラッキングランドマークの場合は、ランドマークがすでに設定されている Face Prefab から開始できます (MARS/Runtime/Prefabs/FaceMask.prefab
にあります)。このプレハブを右クリックして展開します (コンテキストメニューから、Unpack prefab completely を選択)。顔の各特徴を表す子トランスフォームにコンテンツをアタッチします。次に、アプリケーションで使用しないランドマークを削除したり、既存のランドマークを複製し、Inspector で設定を変更したりできます。
このプレハブを使用する代わりに、プロキシに顔のランドマークを追加することもできます。下のスクリーンショットに示すように、まずプロキシに Is Face トレイトを追加し (1)、次に Face Landmarks アクションを追加して (2)、Add Landmark ボタンをクリックします (3)。
体のランドマーク
ランドマーク は ボディトラッキング でも使用できます (Create Menu > MARS > Presets > Body を選択)。顔のランドマークと同様に、Hierarchy で ランドマーク の親にするか、シーンビュー でランドマークのビジュアライザーにドラッグアンドドロップすることによって、体のランドマークにゲームオブジェクトをアタッチできます。体のランドマークの詳細については、体に関するドキュメントのランドマークのセクション を参照してください。
他のタイプのランドマーク
他のシナリオでもランドマークを使用できます。他のタイプのランドマークを追加するには、Landmark Controller コンポーネントを (新規または既存の) ゲームオブジェクトに追加し、Source プロパティを、PlaneLandmarksAction
、FaceLandmarksAction
、LandmarkOutputPolygon
などの ICalculateLandmarks
を実装したコンポーネントに設定します。
ノート: Plane Landmarks アクションと Face Landmarks アクションは、プロキシオブジェクトまたはプロキシの子オブジェクトのどれかに直接配置されている場合にのみデータを取得します。
複数のランドマークで単一のアクションを参照し、ソースデータを共有することができます。この場合、Landmark Controller がアクションを参照している限り、階層内のどこにでも配置できます。
ランドマークのスクリプト
ランドマークの操作には、LandmarkController
、LandmarkOutput
、LandmarkSettings
、LandmarkSource
といったスクリプトを使用します。必要に応じて、これらのスクリプトの全部または一部をゲームオブジェクトにアタッチできます。
LandmarkController
LandmarkController
スクリプトは、他のコンポーネント (Source、Settings、Output) を参照して、ランドマークが更新されるタイミング (方法ではありません) を制御します。
LandmarkOutput
LandmarkOutput
スクリプトには結果となるランドマークデータが含まれ、これを使用してシーンに影響を与えます。標準的なランドマーク出力タイプ (点、辺など) は、Transform コンポーネントを変更し、ギズモを描画します。また、スクリプトの記述を介してアクセスできるパブリックプロパティが用意されています。
各タイプのランドマーク出力は ILandmarkOutput
を実装しており、必要とする固有のデータを追加します。ユーザーは LandmarkController
から .output
プロパティにアクセスし、必要な特定の出力タイプにこのプロパティをキャストできます。プロパティが不適切なタイプであった場合、キャストは失敗し、null が返されます。
public class MyScript : MonoBehaviour {
public LandmarkController closestEdgeLandmark; // ランドマークを参照します
void Update() {
var edgeOutput = closestEdgeLandmark.output as LandmarkOutputEdge;
if (edgeOutput == null)
{
// 割り当てられたランドマークは "辺" タイプに設定されていません
}
else
{
// 以下のように、edgeOutput のデータに何かの処理を実行します
Debug.Log(edgeOutput.startPoint + “ -> “ + edgeOutput.endPoint);
}
}
}
LandmarkSettings
ランドマークのタイプによっては、ソースだけでは計算できないため、追加の入力データが必要になる場合があります。例えば、平面に最も近い辺を MARS で計算するためには、もう 1 つ別の位置が必要です。設定が不要な場合は、LandmarkController
の .settings
フィールドを null にできます。それ以外の場合は、必要な追加プロパティを保持しているコンポーネントを参照します。各設定コンポーネントは ILandmarkSettings
インターフェースを実装しており、必要に応じてランドマークの再計算を発動できます。
LandmarkSource
LandmarkSource
スクリプトは、ランドマークの計算方法を定義します。各ランドマークソースには、名前、Settings コンポーネント (必要な場合)、サポートする出力タイプが含まれています。また、ランドマークを計算するためのメソッドも提供します。ソースには ICalculateLandmarks
インターフェースを実装した任意のクラスを指定できます。
Plane Landmarks アクションと Face Landmarks アクションは、特定の MARS ゲームオブジェクトに対して機能する Landmark Source の例です。これらは、(同じゲームオブジェクトまたは親ゲームオブジェクトの) プロキシの MatchAcquire
イベントを使用して MARS からワールドデータを取得し、ランドマークを計算する Action コンポーネントです。詳細については、用語集 のプロキシおよび Match イベントのエントリーを参照してください。
これらのコンポーネントが (1 つではなく) すべて用意されているのは、ランドマークシステムがコードの再利用や拡張を容易に行うことができるように設計されているためです。動作を別々のコンポーネントに分割することにより、すべてのコードを複製しなくても一部変更するだけで済みます。
LandmarkController
スクリプトは、計算が必要となる可能性のあるさまざまな設定や出力を実現するための再利用可能なフレームワークです。カスタムインスペクターは、まったく異なるタイプの設定や出力を使用している場合でも、すべてのランドマーク間で共有できます。
技術的な観点から見ると、フィールドをシリアル化する際、コンポーネントはフィールドがどのタイプのデータになるかを正確に把握する必要があるために、このことが必要になります。また、ランドマークごとにその設定や出力に異なるタイプのデータを指定できるようにするために、異なるコンポーネントタイプとしてシリアル化する必要もあります。
カスタムランドマークの定義
カスタムランドマークを定義するには、以下の手順に従います。
ランドマークソースを作成する
カスタムランドマークを作成するには、それを定義するランドマークソースを作成する必要があります。これを行うには、ICalculateLandmarks
インターフェースを実装するコンポーネントを作成します。
このインターフェースには、AvailableLandmarkDefinitions
プロパティが必要です。このプロパティでは、計算可能なすべての定義がソースから返されます。定義には、名前、出力タイプ、および設定タイプ (オプション) が含まれます。
static readonly List<LandmarkDefinition> k_Definitions = new List<LandmarkDefinition>
{
new LandmarkDefinition(“Center”, new []{typeof(LandmarkOutputPoint)}),
new LandmarkDefinition(“Closest”,
new []{typeof(LandmarkOutputPoint), typeof(LandmarkOutputEdge)},
typeof(ClosestLandmarkSettings))
};
public override List<LandmarkDefinition> AvailableLandmarkDefinitions { get { return k_Definitions; } }
上記の例では、このクラスは 2 つの定義を含むリストを返します。
- 1 つ目は "Center" と呼ばれるもので、設定を必要としない "点" です。
- 2 つ目は "Closest" と呼ばれるもので、点または辺を指定できます。これにはターゲットを指定するための Settings コンポーネントが必要です。
GetLandmarkCalculation()
メソッドでは、ソースは指定されたランドマークの定義に対して Action<ILandmarkController>
(void を返し、その唯一のパラメーターとしてランドマークを受け取るメソッド) を提供する必要があります。このメソッドは、ランドマークの .settings
と .output
を受け取り、定義として想定されるタイプにそれらをキャストすることで、適切に出力を変更できます。
public override Action<ILandmarkController> GetLandmarkCalculation(LandmarkDefinition definition)
{
if (definition.name == “Closest”)
return CalculateClosestLandmark;
// この条件に該当しない場合は
// ... 他の定義を確認します
else
Debug.LogError("Invalid landmark definition");
return null;
}
void CalculateClosestLandmark(ILandmarkController landmark)
{
var settings = landmark.settings as ClosestLandmarkSettings;
if (settings == null)
{
//平面に最も近いランドマークには有効な設定がありません
return;
}
// ここで計算を実行します
var landmarkPoint = landmark.output as LandmarkOutputPoint;
if (landmarkPoint != null)
{
landmarkPoint.position = closestPoint;
}
var landmarkEdge = landmark.output as LandmarkOutputEdge;
if (landmarkEdge != null)
{
landmarkEdge.startPoint = edgeVertA;
landmarkEdge.endPoint = edgeVertB;
}
}
このインターフェースには、ランドマークコントローラーがリッスンし、更新するかどうかを選択する dataChanged
イベントがあります。MARS プロキシでは、MatchUpdate
イベントがこのイベントを発生させます。
ランドマーク出力を作成する
定義するランドマークが、提供されているタイプ (出力、点、ポーズ、辺) のどれにも出力できない場合、カスタムタイプを作成できます。例えば、ランドマークでその出力タイプを回転として定義する場合は、以下のようなスクリプトを追加します。
public class LandmarkOutputRotation : MonoBehaviour, ILandmarkOutput
{
// これはランドマークソースの計算メソッドによって変更されます
public Quaternion rotation { get; set; }
public override void UpdateOutput()
{
// 回転で何かを実行します
}
void OnDrawGizmosSelected()
{
// ギズモを描画して回転を可視化します
}
}
ランドマーク設定を作成する
計算対象のランドマークに追加のカスタム入力データが必要な場合は、設定のコンポーネントフィールドを使用できます。例えば、平面に最も近い点または辺には、ターゲットとする参照があります。そのための設定コンポーネントは以下のようになります。
public class ClosestLandmarkSettings : MonoBehaviour, ILandmarkSettings
{
[SerializeField]
Component m_Target;
[SerializeField]
bool m_UpdateIfTargetMoves = true;
[SerializeField]
float m_UpdateInterval = 0.03f;
Vector3 m_PreviousPosition;
Quaternion m_PreviousRotation;
float m_TimeSinceLastCheck;
public Component target { get { return m_Target; } set { m_Target = value; } }
public event Action<ILandmarkSettings> dataChanged;
void Update()
{
CheckIfMoved();
}
void CheckIfMoved()
{
if (!m_UpdateIfTargetMoves || dataChanged == null || m_Target == null)
return;
m_TimeSinceLastCheck += Time.unscaledDeltaTime;
if (m_TimeSinceLastCheck < m_UpdateInterval)
return;
m_TimeSinceLastCheck = 0f;
var targetTransform = m_Target.transform;
if (targetTransform.position != m_PreviousPosition ||
targetTransform.rotation != m_PreviousRotation)
{
dataChanged(this);
m_PreviousPosition = targetTransform.position;
m_PreviousRotation = targetTransform.rotation;
}
}
}
ソースの場合と同様に、Settings コンポーネントにも dataChanged
イベントがあります。このイベントは、何かが変更されてランドマークがその変更に反応する必要があることを示すために呼び出すことができます。上記の例では、ターゲットが移動した場合、Settings はこのイベントを呼び出して、ランドマークが最も近い点を再計算できるようにします。