MRが楽しい

MRやVRについて学習したことを書き残す

公式チュートリアル「HOLOGRAMS 210 4章」を試してみる

本日も継続して、チュートリアルお試し枠です。
今回は4章 インジケータを試します。
azure-recipe.kc-cloud.jp


アプリをビルドすると、宇宙飛行士が視界外にあるとき、その方向を矢印で示してくれるようになります。
f:id:bluebirdofoz:20170524014816j:plain
参考記事ではキャプチャを撮ると消えてしまうとありましたが、自身の環境ではキャプチャできました。
f:id:bluebirdofoz:20170524014824j:plain
矢印の方向を見ると、宇宙飛行士がいます。宇宙飛行士を画面内に捉えると矢印は消えます。

hololensは視野が狭く、目標の仮想オブジェクトを見失うことが多々あります。
この機能は色々と活用できそうです。おいかけっこアプリにも搭載すると便利かもしれません。
矢印で示すオブジェクトを動的に差し替えるなど、応用も効きそうです。


では早速、コード確認です。

今回追加したスクリプトは以下のDirectionIndicatorクラスです。
まずは起動時処理と、アップデート関数を確認します。
・DirectionIndicator.cs

public void Awake()
{
    if (DirectionIndicatorObject == null)
    {
        return;
    }

    // Instantiate the direction indicator.
    DirectionIndicatorObject = InstantiateDirectionIndicator(DirectionIndicatorObject);
    directionIndicatorDefaultRotation = DirectionIndicatorObject.transform.rotation;

    directionIndicatorRenderer = DirectionIndicatorObject.GetComponent<MeshRenderer>();
}

public void Update()
{
    if (DirectionIndicatorObject == null)
    {
        return;
    }

    // Direction from the Main Camera to this script's parent gameObject.
    Vector3 camToObjectDirection = gameObject.transform.position - Camera.main.transform.position;
    camToObjectDirection.Normalize();

    // The cursor indicator should only be visible if the target is not visible.
    isDirectionIndicatorVisible = !IsTargetVisible();
    directionIndicatorRenderer.enabled = isDirectionIndicatorVisible;

    if (isDirectionIndicatorVisible)
    {
        Vector3 position;
        Quaternion rotation;
        GetDirectionIndicatorPositionAndRotation(
            camToObjectDirection,
            out position,
            out rotation);

        DirectionIndicatorObject.transform.position = position;
        DirectionIndicatorObject.transform.rotation = rotation;
    }
}

途中、DirectionIndicatorObject変数に設定したDirectionalIndicator.prefabが矢印オブジェクトです。
Awake関数では登録された矢印オブジェクトの向きとメッシュを取得しています。
InstantiateDirectionIndicator関数は矢印オブジェクトからクローンを作成し、以下の下準備を行い、返却しています。
・メッシュを設定する
・衝突判定を除去する
・カラーを設定する
・子オブジェクトとして登録する

次にUpdate関数の処理についてです。処理の流れとしては以下のようです。
1.ゲームオブジェクト(宇宙飛行士)とカメラの位置情報から方向と距離を割り出す。
2.ゲームオブジェクト(宇宙飛行士)が非表示の時、矢印オブジェクトを表示する。
3.表示の際は、方向と距離の情報から矢印オブジェクトの位置と向きを決定する。

確認してみると、とてもシンプルな処理です。


因みにゲームオブジェクト(宇宙飛行士)が非表示かの判定関数は以下です。
・DirectionIndicator.cs

private bool IsTargetVisible()
{
    // This will return true if the target's mesh is within the Main Camera's view frustums.
    Vector3 targetViewportPosition = Camera.main.WorldToViewportPoint(gameObject.transform.position);
    return (targetViewportPosition.x > TitleSafeFactor && targetViewportPosition.x < 1 - TitleSafeFactor &&
        targetViewportPosition.y > TitleSafeFactor && targetViewportPosition.y < 1 - TitleSafeFactor &&
        targetViewportPosition.z > 0);
}

Camera.main.WorldToViewportPoint関数は、ワールド空間のpositionをビューポート空間に変換する関数です。
docs.unity3d.com


hololensのアプリではワールド空間とビューポート空間の関係は重要なので覚えておいて損のない関数です。
Z位置はワールド空間のままなので、X/Yと判定が異なっています。


矢印オブジェクトの位置と向きを決定する関数は以下です。
・DirectionIndicator.cs

private void GetDirectionIndicatorPositionAndRotation(
    Vector3 camToObjectDirection,
    out Vector3 position,
    out Quaternion rotation)
{
    // Find position:
    // Use this value to decrease the distance from the cursor center an object is rendered to keep it in view.
    float metersFromCursor = 0.3f;

    // Save the cursor transform position in a variable.
    Vector3 origin = Cursor.transform.position;

    // Project the camera to target direction onto the screen plane.
    Vector3 cursorIndicatorDirection = Vector3.ProjectOnPlane(camToObjectDirection, -1 * Camera.main.transform.forward);
    cursorIndicatorDirection.Normalize();

    // If the direction is 0, set the direction to the right.
    // This will only happen if the camera is facing directly away from the target.
    if (cursorIndicatorDirection == Vector3.zero)
    {
        cursorIndicatorDirection = Camera.main.transform.right;
    }

    // The final position is translated from the center of the screen along this direction vector.
    position = origin + cursorIndicatorDirection * metersFromCursor;

    // Find the rotation from the facing direction to the target object.
    rotation = Quaternion.LookRotation(
        Camera.main.transform.forward,
        cursorIndicatorDirection) * directionIndicatorDefaultRotation;
}

カーソルを中心にオブジェクトを設置していますが、注目すべき点はVector3.ProjectOnPlaneでしょうか。
こちらもワールド空間の方向値を平面上のベクトルへと座標変換するコードです。
docs.unity3d.com