MRが楽しい

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

おいかけっこアプリの追跡ロジックにNavMeshを適用する

本日はおいかけっこアプリの改修枠です。
おいかけっこアプリにNavMeshを適用し、カメラに向けて経路選択を行って追跡するように修正しました。
f:id:bluebirdofoz:20170903165135j:plain

技術的にはこれまで学んだことをおいかけっこアプリの追跡ロジックに適用しただけです。
bluebirdofoz.hatenablog.com

ただ、キャラクタの向きが固定されてスライド移動しているように見える問題がありました。
以下の通り、Agentの設定処理に合わせて、キャラクタの方向設定の処理を追加して対応しています。
・NavMeshLogic.cs

void Update()
{
    // ロジック有効時のみ、定期処理を実行する
    if (p_Enable == false)
    {
        return;
    }

    // NavMeshロジックの実行
    // Agentの現在位置(root)と目的位置(maincamera)を設定
    p_Agent.transform.position = this.gameObject.transform.root.position;
    p_Agent.destination = Camera.main.transform.position;

    // パスの計算
    NavMeshPath path = new NavMeshPath();
    NavMesh.CalculatePath(p_Agent.transform.position, p_Agent.destination, NavMesh.AllAreas, path);
    Vector3[] positions = path.corners;

    // 移動位置差分を取得
    Vector3 diffPos = this.gameObject.transform.root.position - p_PrevPosition;
    Vector3 diffPos_horizontal = new Vector3(
        diffPos.x * 1.0f,
        diffPos.y * 0.0f,
        diffPos.z * 1.0f
    );

    // 移動差分からキャラクタの方向を決定
    if (diffPos.magnitude > 0.01)
    {
        this.gameObject.transform.root.rotation = Quaternion.LookRotation(diffPos_horizontal);
    }

    // 位置を保存
    p_PrevPosition = this.gameObject.transform.root.position;
}

おいかけっこアプリはここで一旦完成としてしまいたいと思います。
細部を詰めたら Windows ストアにアップしてみようかな。

完成とする理由の一つとして、コードが随分と複雑化してきたことがあります。
初期のMVP設計ですが、今では形が保てなくなってきました。
bluebirdofoz.hatenablog.com

今回の NavMesh もですが、HoloToolKit を初めとする利用ライブラリそれぞれが独自の伝達手段を持っているためです。
無理やりにMVP設計に落とし込むのも、無駄が多いですし。
もっと違う方法で全体の繋がりを分かりやすくする手段が必要ですね。この経験を持って一から見直してみたいと思います。

NavMeshを使ってキャラクタとおいかけっこをする

本日は Unity の技術調査枠です。
以前 NavMesh を使って特定のポイントから特定のポイントまでキャラクタを移動させました。
・SpatialMappingのデータにNavMeshを適用する その2
 http://bluebirdofoz.hatenablog.com/entry/2017/08/10/023942

今回はこの技術をおいかけっこアプリに取り込むべく、カメラを追跡し続けるようにカスタマイズしてみます。
その2で利用したプロジェクトを開きます。
f:id:bluebirdofoz:20170902230403j:plain

修正方法としては、以前、一度目にクリックした個所から二度目にクリックした個所へ移動していたのを。
二度目のクリック箇所をカメラ位置にロジックを差し替えます。
更に、カメラは移動し続けるので、Update で常に追跡位置を変更します。
MoveToClickPoint.cs スクリプトのOnInputClicked関数とUpdate関数を以下の通り修正しました。
・MoveToClickPoint.cs

public class MoveToClickPoint : MonoBehaviour, IInputClickHandler
{
    public NavMeshAgent Agent;
    LineRenderer lineRenderer;

    bool traceFlag = false;

    void Start()
    {
        InputManager.Instance.PushFallbackInputHandler(gameObject);
        Agent.gameObject.SetActive(false);
        lineRenderer = gameObject.GetComponent<LineRenderer>();
        lineRenderer.positionCount = 0;
    }

    public void OnInputClicked(InputClickedEventData eventData)
    {
        if (GazeManager.Instance.IsGazingAtObject)
        {
            var hitInfo = GazeManager.Instance.HitInfo;
            if (!Agent.gameObject.activeSelf)
            {
                Agent.gameObject.SetActive(true);
                Agent.transform.position = hitInfo.point;

                Agent.destination = Camera.main.transform.position;

                // パスの計算
                var path = new NavMeshPath();
                NavMesh.CalculatePath(Agent.transform.position, Agent.destination, NavMesh.AllAreas, path);
                var positions = path.corners;

                // ルートの描画
                lineRenderer.widthMultiplier = 0.1f;
                lineRenderer.positionCount = positions.Length;
                for (int i = 0; i < positions.Length; i++)
                {
                    Debug.Log("point " + i + "=" + positions[i]);
                    lineRenderer.SetPosition(i, positions[i]);
                }

                // 追跡フラグON
                traceFlag = true;
            }
            else
            {
            }
        }
    }

    void Update()
    {
        if (Input.anyKeyDown)
        {
            Debug.Log("click LOG");
            if (GazeManager.Instance.IsGazingAtObject)
            {
                var hitInfo = GazeManager.Instance.HitInfo;
                if (!Agent.gameObject.activeSelf)
                {
                    Agent.gameObject.SetActive(true);
                    Agent.transform.position = hitInfo.point;

                    Agent.destination = Camera.main.transform.position;

                    // パスの計算
                    var path = new NavMeshPath();
                    NavMesh.CalculatePath(Agent.transform.position, Agent.destination, NavMesh.AllAreas, path);
                    var positions = path.corners;

                    // ルートの描画
                    lineRenderer.widthMultiplier = 0.1f;
                    lineRenderer.positionCount = positions.Length;
                    for (int i = 0; i < positions.Length; i++)
                    {
                        Debug.Log("point " + i + "=" + positions[i]);
                        lineRenderer.SetPosition(i, positions[i]);
                    }

                    // 追跡フラグON
                    traceFlag = true;
                }
            }
        }
        if (traceFlag == true)
        {
            Agent.destination = Camera.main.transform.position;

            // パスの計算
            var path = new NavMeshPath();
            NavMesh.CalculatePath(Agent.transform.position, Agent.destination, NavMesh.AllAreas, path);
            var positions = path.corners;

            // ルートの描画
            lineRenderer.widthMultiplier = 0.1f;
            lineRenderer.positionCount = positions.Length;
            for (int i = 0; i < positions.Length; i++)
            {
                Debug.Log("point " + i + "=" + positions[i]);
                lineRenderer.SetPosition(i, positions[i]);
            }
        }
    }
}

結果……。
f:id:bluebirdofoz:20170902230416j:plain
ちゃんと動きました。

カメラを移動しても、経路探索をやりなおして追跡し続けます。
f:id:bluebirdofoz:20170902230425j:plain

後はこれを(何とか上手く設計を見直して)おいかけっこアプリに反映するだけです。

アプリパッケージでhololensにアプリをインストールする

本日は hololens の技術調査枠です。
前回、hololensのアプリパッケージの作成方法を学びました。
bluebirdofoz.hatenablog.com

今回はそのアプリパッケージを用いて hololens にアプリをインストールする手順をまとめます。

アプリパッケージを出力したフォルダは以下のようなファイル構成になっています。
f:id:bluebirdofoz:20170901021550j:plain
Add-AppDevPackage.resources:インストール用パワーシェルの文言ファイル
Dependencies:各プロセッサアーキテクチャで必要なパッケージファイル群(X86、X64、ARM)
Add-AppDevPackage.ps1:インストール用パワーシェル
HandPositionTest_1.0.0.0_x86.appxbundle:アプリパッケージファイル
HandPositionTest_1.0.0.0_x86.cer:セキュリティ証明書

hololens へのローカルインストールではパワーシェルを利用しないため、必要なのは以下の2つです。
Dependencies/x86X86プロセッサアーキテクチャで必要なパッケージファイル群
HandPositionTest_1.0.0.0_x86.appxbundle:アプリパッケージファイル

例えば Dependencies/x86 配下には以下のファイルが存在します。
f:id:bluebirdofoz:20170901021611j:plain

appxbundle と合わせて、以下のようにまとめればインストールに必要なファイルは全てです。
f:id:bluebirdofoz:20170901021622j:plain

少し余談ですが。
前回のパッケージ作成のウィンドウで「はい」を選び、ストアへの提出パッケージを作成した場合
これらのファイルは全て appxupload パッケージという一つのファイルにまとまられるそうです。
Windows 10 のユニバーサル Windows アプリをパッケージ化する
 https://msdn.microsoft.com/ja-jp/library/windows/apps/hh454036.aspx
ローカル配布の場合でもまとめてくれればいいのに。何か意図があるのでしょうか。


ともあれ。先ほどのファイルを用いてインストールを行います。
アプリのインストールを行う hololens の DevicePortal を開きます。
f:id:bluebirdofoz:20170901021635j:plain

メニューから「App」をクリックして AppManager を開きます。
f:id:bluebirdofoz:20170901021646j:plain

「App package」に .appxbundle ファイルのアプリパッケージを指定します。
Dependency」に .appx ファイルのプロセッサアーキテクチャで必要なパッケージを指定します。
全て選択したら「Go」ボタンをクリックします。
f:id:bluebirdofoz:20170901021654j:plain

実行したら「Failed to start deployment. Failure text: Install failed. Please contact your software vendor.
(0x80073cf9)」というエラーが出て失敗しました。
f:id:bluebirdofoz:20170901021703j:plain

何故?……と、少し調べたら原因が分かりました。
・Packaging and Sideload - Windows Mixed Reality Developer Forum
 https://forums.hololens.com/discussion/694/packaging-and-sideload

要は同じアプリが既にインストールされていると失敗します。
今回、アプリパッケージの作成に既存の HandDraggble プロジェクトを流用したのが原因でした。

hololens にインストールしていたアプリを一旦アンインストールしてもう一度試します。
f:id:bluebirdofoz:20170901021724j:plain
今度は「Done!」が表示されました。成功です。

hololens 内のアプリ一覧からアプリを起動すると、以下の通り、動作を確認できました。
f:id:bluebirdofoz:20170901021732j:plain

hololensのインストール用アプリパッケージを作成する

本日は hololens の技術調査枠です。
hololensのアプリパッケージの作成方法を学びます。
m2wasabi.hatenablog.com

これを利用すれば VisualStudio をインストールしていないPCからでも hololens にアプリのインストールが可能です。
前回作成した HandDraggble プロジェクトで試してみます。
f:id:bluebirdofoz:20170831011914j:plain

Unity 上はまず通常通りビルドを行い、UWPプロジェクトを作成します。
f:id:bluebirdofoz:20170831011922j:plain

VisualStudio でUWPプロジェクトを起動したらビルドが通ることを確認しておきます。
f:id:bluebirdofoz:20170831012019j:plain

メニューから プロジェクト -> ストア -> アプリパッケージの作成 をクリックします。
f:id:bluebirdofoz:20170831012031j:plain

パッケージ作成のウィンドウが表示されます。
Windowsストアにアップロードするパッケージを作成しますか?」の質問に「いいえ」を選択して「次へ」をクリックします。
f:id:bluebirdofoz:20170831012040j:plain
ストアへアップロードする場合はサインインした上で署名等を行う必要があるようです。
こちらはいずれ実施してみたいと思います。

パッケージの選択と構成ウィンドウが表示されます。以下の通り設定すれば問題ないようです。
出力場所:任意の出力フォルダ
バージョン:任意のバージョン番号
アプリケーションバンドルの生成:常に行う
ソリューション構成マッピングx86 Release(x86)にのみチェック
アプリのクラッシュ分析:チェック無し
f:id:bluebirdofoz:20170831012047j:plain
以上で「作成」をクリックすると、パッケージが出力フォルダに作成されます。

バージョンの扱いについては以下に説明があります。ローカル配布ではほとんど意識する事はないです。
・パッケージ バージョンの番号付け
 https://docs.microsoft.com/ja-jp/windows/uwp/publish/package-version-numbering
アプリケーションバンドルの説明は以下です。
・アプリ パッケージの要件
 https://docs.microsoft.com/ja-jp/windows/uwp/publish/app-package-requirements#app-bundles

パッケージの作成が完了すると、以下のようなウィンドウが表示されます。
そのまま Windows ストアのアプリ認定チェックを行うことが可能です。これもローカル配布では不要です。
f:id:bluebirdofoz:20170831012054j:plain

HoloToolKitのHandDraggableを利用してオブジェクトを移動する

本日は Unity(hololens) の技術調査枠です。
前回、InteractionManager を利用して手の位置検出すると共にオブジェクトのドラッグを試しました。
bluebirdofoz.hatenablog.com

……が、改めて調べてみると「オブジェクトを掴んでドラッグ」は既に HoloToolKit で提供されているという情報がありました。
matatabi-ux.hateblo.jp
izmiz.hateblo.jp

折角なので試してみます。前回利用したプロジェクトを再利用します。
f:id:bluebirdofoz:20170830012843j:plain
前回作成した HandPosition.cs は Sphere オブジェクトから外してしまいます。

利用するのは HoloToolKit/Input/Script/Interactions 配下にある HandDraggable.cs です。
これを動かしたいオブジェクト、今回は Sphere オブジェクトに適用します。
f:id:bluebirdofoz:20170830012853j:plain
HostTransform 変数にオブジェクト自身の Transform を適用します。

そしてビルドして hololens で動かしてみると……あれ?ドラッグできません。

HandDraggble.cs のコードをよく見てみます。すると GazeManager.cs を参照していました。
試しに HoloToolKit/Input/Script/Gaze 配下にある GazeManager.cs も適用してみます。
f:id:bluebirdofoz:20170830012903j:plain

そしてビルドして hololens で動かしてみるが……やっぱり動きません。
そもそも GazeManager.cs は単なる Singleton クラスなので適用しているかどうかは問題ではないですね。
HandDraggble.cs を適用する としか書いていないため、何か根本的な設定がおかしい?
色々やってみましたが、動かないので一旦諦めます。


追記

さらに調べてみたら情報ありました。
zuq9nn.blogspot.jp
noshipu.hateblo.jp

HoloToolKit/Input/Prefabs 配下の InputManger.prefab を Hierarchy に追加。これが前提条件となります。
f:id:bluebirdofoz:20170830012935j:plain
そもそも最初に参照した記事も InputManager に関する記事なので prefab は適用している前提でした。

Hololens 上で正常に動作することを確認できました。
f:id:bluebirdofoz:20170830013000j:plain

因みに InputManager を適用していると Unity での実行時、Shift を押すと青い指マークが現れます。
このとき、HandDraggble.cs を適用したオブジェクトをドラッグできます。
f:id:bluebirdofoz:20170830013015j:plain

hololensで指の位置を検知する

本日は Unity(hololens) の技術調査枠です。
下記の記事を参考に、hololens での手の位置検出について試してみます。
qiita.com

まずは新規プロジェクトを作成します。
f:id:bluebirdofoz:20170829001554j:plain

今回のプロジェクトは hololens 上での確認が必須になるので、HoloToolKit をインポートして
hololens 向けプロジェクトに変更しておきます。
f:id:bluebirdofoz:20170829001606j:plain

動かすオブジェクトのサンプルとして Sphere オブジェクトを配置します。
f:id:bluebirdofoz:20170829001621j:plain

参考記事の HandPosition.cs を元に Sphere オブジェクトを掴んで運ぶスクリプトを作成します。
・HandPosition.cs(改二)

using UnityEngine;
using System.Collections;
using UnityEngine.VR.WSA.Input;

public class HandPosition : MonoBehaviour
{
    public GameObject HandObject;
    private bool HandFlg;

    void Start()
    {
        HandFlg = false;
        InteractionManager.SourceUpdated += InteractionManager_SourceUpdated;
        InteractionManager.SourceLost += InteractionManager_SourceLost;
        InteractionManager.SourcePressed += InteractionManager_SourcePressed;
        InteractionManager.SourceReleased += InteractionManager_SourceReleased;
    }

    void InteractionManager_SourceUpdated(InteractionSourceState state)
    {
        Vector3 handPosition;
        if (HandFlg == true)
        {
            if (state.source.kind == InteractionSourceKind.Hand && state.properties.location.TryGetPosition(out handPosition))
            {
                HandObject.transform.position = handPosition;
            }
        }
    }

    void InteractionManager_SourceLost(InteractionSourceState state)
    {
        HandFlg = false;
    }

    void InteractionManager_SourcePressed(InteractionSourceState state)
    {
        HandFlg = true;
    }

    void InteractionManager_SourceReleased(InteractionSourceState state)
    {
        HandFlg = false;
    }
}

ゲーム内のオブジェクトに作成したスクリプトを適用します。
その後、Sphere オブジェクトをドラック対象のオブジェクトとして設定します。
f:id:bluebirdofoz:20170829001641j:plain

アプリをビルドして hololens 上で動作してみます。
指刺す動作でドラッグ開始、離す動作でドラッグが終了します。
f:id:bluebirdofoz:20170829001652g:plain

視線ではなく手の動きを検知しているため、例えば、オブジェクトを掴んで移動して、好きなところに置くといった動作が自然にできます。
色々と活用できそうですね。

Unityでライティング(Lighting)の設定を行う

本日は Unity の技術調査枠です。
前回、別のプロジェクトから別のプロジェクトへゲームオブジェクトをコピーする術を学びました。
bluebirdofoz.hatenablog.com

しかし、実際に動かしてみると微妙に3Dモデルの色の見え方が違いました。
原因を調べてみると二つのプロジェクトで Standard シェーダの色が違うことが分かりました。
・元プロジェクトの Standard シェーダ
f:id:bluebirdofoz:20170828012126j:plain
・コピー先プロジェクトの Standard シェーダ
f:id:bluebirdofoz:20170828012136j:plain

Standard シェーダは Unity の共通シェーダなのでコピーに関わらず、同じ発色になるはずです。
それにも関わらず、同じ色になっていないということはマテリアルやシェーダ以外に色を決定する要素があるということです。
(シェーダを直接見ているのでこの場合シーン内のライト設定は関係ありません)

調べてみた結果、Unity にはライティングの設定があるらしいことが分かりました。
noshipu.hateblo.jp

プロジェクト間で調べてみたところ、この設定が異なりました。早速コピー先のライティング設定を修正します。
メニューから Window -> Lighting -> Setting で Lighting ウィンドウが開きます。
f:id:bluebirdofoz:20170828012157j:plain

コピー元のライティング設定と見比べて、設定を合わせてみます。
GrobalMaps の AutoGenerate にチェックをしました。
f:id:bluebirdofoz:20170828012210j:plain
Standard シェーダが元プロジェクトの Standard シェーダと同じ色になりました。

グローバルマップの設定の正体は軽く調べただけではよく分からず……。
以下のグローバルイルミネーションがその正体でしょうか。
docs.unity3d.com

またライティングに拘るときが来たら調べます。