UnityでMagic Leapの環境構築からImage Trackingするまでをさくっと行う

Magic Leapを使用したARの案件を受注したので、まずはMagic LeapのImage Trackingを試してみようと思います。タイトルの「さくっと」は、「出来るだけ早く手間をかけずに」の意です。実際、ImageTrackingのサンプルを動かすだけならすんなり終わりました。

## 目標
Image Tracking機能を使用して、画像を認識したところに3Dオブジェクトを表示する

## 環境
- Unity : 2019.3.7f1
- Lumin OS : 0.98.10
- Lumin SDK : 0.24.1

## 環境構築
[こちらの記事](https://medium.com/kadinche-engineering/%E6%9C%80%E7%9F%AD%E3%81%A7-magic-leap-1-%E3%81%AE%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83%E3%82%92%E6%A7%8B%E7%AF%89%E3%81%99%E3%82%8B-f1cf41864dde)が参考になりました。基本はテンプレートプロジェクトが用意されているので、それを使うのが一番スマートかと思います。

## Image Tracking
ここからは、Magic Leapのテンプレートプロジェクト導入済みという前提で進みます。

###シーンに置くオブジェクト
![hierarchy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626158/82f70812-4529-7bcd-35f2-095785243039.png)

- Main Cameraは、MagicLeap > Core > Assets > Prefabsにあります。それをそのままhierarchyに持ってきます。Inspectorは特に弄りません。

- ImageTrackingManagerは空のオブジェクトを作り、MLPriviMLPrivilegeRequesterBehaviorクラスをアタッチします。Privilegesプルダウンを開いて、Sizeに1を指定します。するとElement0というプルダウンが表示されるので、Camera Captureを選択します。
![ImageTrackingManager.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626158/5cc77d73-26c0-9e8d-7468-d9e8544f8c98.png)


- ImageTrackedObjectは空のオブジェクトを作った後、ARした時に表示させたいオブジェクトを子オブジェクトとして持たせます。MLImageTrackerBehaviorクラスをアタッチしてImageに認識したいマーカー画像を登録します。今回はテンプレートプロジェクトに入っているImageTracker_DeepSeaExplorationという画像を使用しました。IsStationaryはTrueに、Auto UpdateはFalseに設定しておきます。ImageTrackedObjectとその子オブジェクトは、全て非アクティブにします。
![MLImageTrackerBehaviour.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626158/a143f4db-8a4a-7fa2-6cca-7d26d70fb21f.png)

### コードを準備
ImageTrackingManagerクラスを作成し、先ほど作成したImageTrackingManagerオブジェクトにアタッチします。
アタッチした後、TrackedImageに、ImageTrackedObjectを登録します。
中身は以下です。これはオフィシャルのサンプルのまんまです。カメラキャプチャーが正常に開始されたら、シーンの読み込み後にTrackedImageに登録したオブジェクトをアクティブにします。

```c#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.MagicLeap;

[RequireComponent(typeof(MLPrivilegeRequesterBehavior))]
public class ImageTrackingManager : MonoBehaviour
{
    public GameObject TrackedImage;
    private MLPrivilegeRequesterBehavior _privRequester = null;
    
    void Awake()
    {
        _privRequester = GetComponent<MLPrivilegeRequesterBehavior>();
        _privRequester.OnPrivilegesDone += HandlePrivilegesDone;
    }
    
    void HandlePrivilegesDone(MLResult result)
    {
        if(!result.IsOk)
        {
            Debug.Log("Error: Priv Request Failed");
        }
        else
        {
            Debug.Log("Success: Priv granted");
            StartCapture();
        }
    }
    
    void StartCapture()
    {
        TrackedImage.SetActive(true);
    }
}
```

次にTrackingVisualsクラスを作成して、ImageTrackedObjectにアタッチします。
アタッチした後、isStationaryをTrueに指定し、Longer Dimension In Scene Unitsパラメーターに、マーカー画像の長辺の長さを指定します。0.1=10cmです。
このクラスにはマーカー画像が認識された時の処理を書きます。RefreshViewMode()の中身を変更すれば、マーカー画像を認識した際の挙動を変更できます。これもオフィシャルのサンプルのまんま。

```c#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.MagicLeap;
using MagicLeap.Core;


[RequireComponent(typeof(MLImageTrackerBehavior))]
public class TrackingVisuals : MonoBehaviour
{

    private bool _targetFound = false;
    private MLImageTrackerBehavior _trackingBehaviour = null;
    public GameObject TrackedObject; 
    void Start()
    {
        _trackingBehaviour = GetComponent<MLImageTrackerBehavior>();
        _trackingBehaviour.OnTargetFound += OnTargetFound;
        _trackingBehaviour.OnTargetLost += OnTargetLost;
        _trackingBehaviour.OnTargetUpdated += OnTargetUpdated;

        RefreshViewMode();
    }

    void OnTargetFound(MLImageTracker.Target target, MLImageTracker.Target.Result result)
    {
        _targetFound = true;
        RefreshViewMode();
    }

    void OnTargetLost(MLImageTracker.Target target, MLImageTracker.Target.Result result)
    {
        _targetFound = false;
        RefreshViewMode();
    }

    void OnTargetUpdated(MLImageTracker.Target target, MLImageTracker.Target.Result result)
    {
        transform.position = result.Position;
        transform.rotation = result.Rotation;
    }

    void OnDestroy()
    {
        _trackingBehaviour.OnTargetLost -= OnTargetLost;
        _trackingBehaviour.OnTargetFound -= OnTargetFound;
        _trackingBehaviour.OnTargetUpdated -= OnTargetUpdated;
    }

    void RefreshViewMode()
    {
        if(_targetFound)
        {
            TrackedObject.SetActive(true);
        }
        else
        {
            TrackedObject.SetActive(false);
        }
    }
}

```
ここまでの手順が終わればImage Trackingができるようになっているはずです。ビルドするか、実機をPCに繋いでZeroIterationを使用してシーンをPlayしてみましょう。Magic Leapがマーカー画像を認識した時に3Dオブジェクトが表示されればOKです。