MRサービス研究開発室の佐々木(竜)と申します。 MRサービス研究開発の一環としてHoloLensのアプリにおいて広告表示を行うSDKを開発しましたのでその紹介をさせていただきます。
前提知識
Windows Mixed Realityとは?
MRは、現在一般的な仮想現実技術のAR、VRを含んだ技術です。 現実空間に仮想的なものを表示する部分から、周りのもの全てを仮想的に表示するところまでを技術範囲としています。
HoloLensとは?
Windows Mixed Realityに準じたAR端末で、端末内にPCが内蔵されており単体で動作します。 視線方向の環境データを認識して現実物体に対してオクルージョンなどの処理をしながら表示することができます。
MixedRealityToolKit-Unityとは?
HoloLensのアプリケーションを作成するときに必要となる、HoloLensデバイスの入力などを容易に制御できるようにするスクリプト群です。 これを用いることにより、Unity上で空間認識などを扱うアプリケーションを容易に開発することができます。
実際の動作動画
SDK設計
動作環境設定
開発環境 Unity 5.6.2f1
Visual Studio 2015
device
HoloLens Developer Edition
Windows10 Microsoft
Plugin
MixedRealityToolKit#Unity 5.6.2f1
Scene 今回はテストのため、広告を表示するのに最低限のオブジェクトのみを配置しています
機能要件
- UnityのScene内に広告を表示します。
- 広告は実際にadstir経由で入札を行い、案件をとってきます。
- HoloLens側の空間認識を利用し、実際に壁にポスターのように表示します。
- 人のタップしたジェスチャーを認識して、Edgeを起動LPを表示します。
動作フロー
①空間を認識する
MixedRealityToolKit内のSpatialMapping.prefabをScene内に配置することで、HoloLensで認識した空間データをもとに、空間をメッシュ化して空間に沿ったメッシュとメッシュコライダーを配置します。作成した空間が、アプリケーションの動作上十分な数に達したとき空間のメッシュへの変換を停止します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
void Start() { // Callbackとして、空間認識が終わったタイミングをハンドリングする SurfaceMeshesToPlanes.Instance.MakePlanesComplete += SurfaceMeshesToPlanes_MakePlanesComplete; } void Update() { if (!meshesProcessed) { // 設定した空間の認識時間が過ぎたかどうか判定 if ((Time.time - SpatialMappingManager.Instance.StartTime) > scanTime) { // 空間認識処理が走っているか if (SpatialMappingManager.Instance.IsObserverRunning()) { // 空間認識処理を止める SpatialMappingManager.Instance.StopObserver(); } // 認識したデータをもとにメッシュを作成する CreatePlanes(); meshesProcessed = true; } } } |
上記のようにコルーチンのStart処理で、マッピングの終了をハンドリングします。 そして、あらかじめ設定した空間認識の時間を過ぎたときMapping処理を止めて、空間メッシュを作成します。 空間認識の処理は重い処理となっているので、アプリケーションの要件に合わせて、空間スキャンの時間を設定します。
参照 : HoloToolkitのSpatialMappingを理解する
②広告データを取得する
①の動作と同時に広告リクエストを非同期で飛ばします。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
OnEnable() { // 広告の読み込みが終わったことを受け取るコールバックを登録 HttpCommon.OnLoadAdDataJson += OnSetAdImage; // 広告リクエストする ShowAd() } private void ShowAd() { AdstirMRAdNative ad = new AdstirMRAdNative("メディアID", 枠ID ); ad.AdLoadStart(); } |
広告をリクエストする前に、処理が終わったイベントを登録します。OnSetAdImage内では広告が読み込まれたフラグを立てる処理と、タップされたときのイベントをセットします。Unityの他のGameObjectから広告の読み込み終了を参照しやするするために、フラグとしてイベントの終了をUnity側で持っておきます。
Managed Pluginを作成するとき
UnityEditor側からDebugで動作させるときと、実際にHoloLensに配置して動作させるときでアプリケーションのrun timeが変わり、参照できるライブラリが変わってきます。 今回のSDKでは、実際にUnityのSceneに配置する前提でした。なので、UnityEditor側からある程度動作を再現したかったので下記のようなプロジェクト構成をしました。
Pluginの実態はSharedの中に持っていて、UWP、UnityのフォルダはSharedファイルからのシンボリックリンクを張っています。また、コード内ではコンパイルを切り替えるマクロを使って、動作環境ごとのコードを書いています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public sealed partial class AdstirMRAdNative { private readonly String mediaId; private readonly int spotNo; public AdstirMRAdNative(String mediaId, int spotNo, Build_Types mode = Build_Types.RELEASE) { this.mediaId = mediaId; this.spotNo = spotNo; ConfigLoader.SetConfigSettings(mode); } public void AdLoadStart() { #if NETFX_CORE UWPAdStirNativeAd Ad = new UWPAdStirNativeAd(); Ad.getAd(mediaId, spotNo); #else UnityAdStirNativeAd Ad = new UnityAdStirNativeAd(); Ad.getAd(mediaId, spotNo); #endif } } |
上記のように、NETFX_CORE
条件内でUWP側のアプリケーションの挙動、もう一方でUnity側の挙動を記述します。 参照 : HoloLens用 Managed Pluginの作り方
③ 広告を配置する
広告のオブジェクトの読み込みが終わり、空間のデータも蓄積されたとき、広告を壁に埋め込みます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
GameObject surface = hitInfo.transform.gameObject; SurfacePlane plane = surface.GetComponent<SurfacePlane>(); // 視線が壁のオブジェクトと一致したとき if (plane != null && plane.PlaneType == PlaneTypes.Wall) { Vector3 position = Camera.main.transform.position + Camera.main.transform.forward * 1.25f; Quaternion rotation = Quaternion.identity; position = surface.transform.position + (plane.PlaneThickness * plane.SurfaceNormal); // 広告オブジェクトを壁表示する位置を取得する position = AdjustPositionWithSpatialMap(position, plane.SurfaceNormal); rotation = Quaternion.LookRotation(surface.transform.forward, Vector3.up); // 広告オブジェクトの生成 GameObject spaceObject = Instantiate(spaceObjectPrefabs, position, rotation) as GameObject; spaceObject.transform.parent = gameObject.transform; } |
ここでは、広告オブジェクトを配置する壁オブジェクトを決定します。 視線方向を取得して、壁のオブジェクトと視線がぶつかる位置に広告を配置します。 壁のオブジェクトが広告を配置することができない幅の壁のオブジェクトが、選択された場合は、壁オブジェクトの再配置を行います。
④LPを表示する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
OnEable() { InteractionManager.OnTapped += ReceivedTapped; } private void ReceivedTapped(GameObject go, InteractionManager.InteractionEventArgs e) { if (CanvasObjCollider(go)) { if (launchedDialog) return; var buttons = SimpleDialog.ButtonTypeEnum.Yes | SimpleDialog.ButtonTypeEnum.No; var title = "alert"; var message = "Open the link in Edge.Is it OK ?"; SimpleDialog simpleDialog = SimpleDialog.Open(dialogObj, buttons, title, message); simpleDialog.OnClosed += OnClosed; StartCoroutine(LaunchDialog(simpleDialog)); launchedDialog = true; } } |
Sceneに配置した。IntractionManagerにTapしたイベントを登録します。IntaractionManagerは人がGestureなどをしたときにイベントをフックしてくれていて、このコンポーネントを使うことでGestureなどの処理を容易にしてくれています。 Tapをされたオブジェクトが、実際の広告のオブジェクトだった時に、ダイアログを表示して遷移するかを問い合わせます。 遷移するが選ばれたとき、アプリケーションをサスペンド状態にして、Edgeを呼び出します。
参照 : MixedRealityToolkit-UnityのInputManagerを紐解く ~Gesture編~
以上が一通りの動作となります。
まとめ
壁の認識、ジェスチャーからのインプットなどを利用したHoloLens用adstirMRSDKを作ってきました。 現状はまだ、動作環境が制限されるなど様々な課題が多く実用的なSDKとはなっていませんが、ARKit/ARCoreなどのモバイルのARフレームワークの発展とともにコンテンツが成熟してきたとき、また、広告を利用していただける機会が生まれたとき、より良いユーザー体験ができるSDKを提供していきたいと思っています。
ユナイテッドでは、エンジニアを絶賛募集しています!このように最新技術にトライできる環境で一緒に働きませんか?