実機をまだ買ってなくても体験できる!HoloLensアプリ開発入門

2017/03/27

Lars Klint

FREE
18

Articles in this issue reproduced from SitePoint
Copyright © 2017, All rights reserved. SitePoint Pty Ltd. www.sitepoint.com. Translation copyright © 2017, KADOKAWA CorporationJapanese syndication rights arranged with SitePoint Pty Ltd, Collingwood, Victoria,Australia through Tuttle-Mori Agency, Inc., Tokyo

ついに2017年1月から日本でも開発者向けに出荷が始まった、マイクロソフトのHoloLens。30万円以上という高額商品ゆえにまだ購入に踏み切れていない人も多いのでは? そこで実機がなくても体験できる、アプリ開発のチュートリアルです。

※本記事は2016年7月9日に掲載した記事の翻訳を一部更新したものです。執筆時点の情報をベースにしており、最新ではない可能性があります。

最近出た中でもっとも刺激的で画期的なテクノロジー製品の1つが、マイクロソフトのHoloLensです。現実空間にリアルタイムの3Dホログラムを映し出すもので、コンピューター界に無限の新たな可能性をもたらしています。3000米ドルもする開発キットとして売り出されたばかりのHoloLensを私も少しだけ試してみましたが、まだまだこれからが楽しみな製品です。

お手頃価格の製品がリリースされるのを待たずに、すぐHoloLensアプリ開発を始めたい人に朗報があります! HoloLens本体が不要で、無料で入手可能なツールがあるのです。

最初に必要なもの

HoloLensはWindows 10で動作し、アプリはUniversal Windows Platform(UWP)向けなので、モバイルデバイスやデスクトップ、Xbox などのプラットフォーム向けのUWPアプリがHoloLensでも動作します。ホログラム体験に必要な主なツールは、Visual Studio 2015とUnity3Dの2つです。SDKやエミュレーターとともに使用します。必要なものは以下のとおりです。

これらのツールをインストールすれば、初のホログラム体験の準備は完了です。

C#やVisual Studioで書かれたUWPアプリはHoloLensで動作しますが、HoloLensアプリならではの特徴は3Dホログラムです。3Dホログラムは、好みの3Dソフトウェアで制作、モデリングできますが、私のように3Dモデルの作り方が分からない人には、プロに制作してもらえるサービスもあります。Unityにも独自の3Dモデルアセットストアがあります。Unityについて少し紹介しますが、大きなトピックですので、始めるにあたってはSitePointにあるほかのUnity関連記事を参照してください。

Unity3Dは3Dモデルのインポートや操作ができ、各オブジェクトのイベントやスクリプトを管理できます。照明効果や背景(「シーン」と呼ばれています)など、ユーザーに投影する空間のあらゆる側面を調整できます。Unityをマスターするのは容易ではありませんが、HoloLensアプリを制作するには避けて通れません。

Unityでの3Dオブジェクトの制作

Unityを使って基本形を制作します。GameObjectメニューから3D Objectを開き、その中から好きな3Dオブジェクトを選択します。

01

球を選択するとシーンの中に基本の球体が表示され、あとで細工したり、動きを加えたりして調整できます。

02

ホログラム体験で3Dオブジェクトとインタラクトする主な方法は、オブジェクトにスクリプトをつけることです。Unityで新規スクリプトを作成します。

03

Visual Studioで次のスクリプトをダブルクリックすれば編集できます。

using UnityEngine;
using System.Collections;

public class SphereBehaviour : MonoBehaviour {

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }
}

Startメソッドは、アプリが起動したときに呼び出され、イベントや初期化コードを設定します。Updateは各フレームごとに1度呼び出され、衝突やRayCastによるヒットなどのユーザーインタラクションを検出するときに使います。

Unityでオブジェクトにスクリプトをつけるには、オブジェクトをドラッグして、スクリプト上でドロップします。スクリプトのコードはメソッドが呼ばれると実行されます。起動時やユーザーが音声コマンドを使ったときなど、ホログラム体験の中でイベントが起きたときです。

ホログラフィックアプリを実行するには、Visual Studioにプロジェクトをインポートすれば、UnityがUWPアプリのソリューションを全部作成してくれます。Build Settingsメニューの中にある次のビルド設定を使用します。

04

Player Settingsを開き、Virtual Reality Supportedを選択して、Windows Holographic SDKを確認してください。

05

ビルドが実行されると、Unityは選択したフォルダーにUWPアプリのソリューションを作成します。このソリューションはVisual Studioで開けます。

06

デフォルトでは、Unity からエクスポートしたUniversal Windows Platformアプリは、どのWindows 10デバイスでも動作します。HoloLensの場合は、アプリがHoloLensで利用できる機能を有効にするための設定が必要です。次のように、Visual StudioのPackage.appxmanifestファイルで、TargetDeviceFamilyを「Windows.Holographic」にします。

07

これで、HoloLensエミュレーターでソリューションを実行できるようになりました。

08

09

3Dワークスペースを作成するワークフローを理解し、モデルの追加やモデルへのスクリプトの適用が分かったところで、HoloLensアプリでホログラムとインタラクトするための主要な分野について説明します。

視線入力

視線入力は、HoloLensアプリへの最初のインプット形式で、ホログラムへの焦点の当て方を決定します。これが、ユーザーがHoloLensを通して見るときの視界の中心であり、実質的な「マウスカーソル」です。カーソルは好きなようにデザインでき、アプリや企業ロゴあるいは他の3D形状との関係で考えます。

10

イメージ提供:Microsoft

HoloLensでは視線ベクトルを決定するのに、ユーザーの目ではなく頭の位置と向きを利用することに注意してください。HoloLensの中心から直進するレーザーポインターのようなものです。

カーソルもまた3Dオブジェクトであり、カーソルらしく動くようコーディングしなければなりません。Unityで新規スクリプトを作成してモデルに加え、次のようなコードを使用します。

using UnityEngine;

public class WorldCursor : MonoBehaviour
{
  private MeshRenderer meshRenderer;

  // Use this for initialization
  void Start()
  {
    // Grab the mesh renderer that's on the same object as this script.
    meshRenderer = this.gameObject.GetComponentInChildren<MeshRenderer>();
  }

  // Update is called once per frame
  void Update()
  {
    // Do a raycast into the world based on the user's
    // head position and orientation.
    var headPosition = Camera.main.transform.position;
    var gazeDirection = Camera.main.transform.forward;

    RaycastHit hitInfo;

    if (Physics.Raycast(headPosition, gazeDirection, out hitInfo))
    {
      // If the raycast hit a hologram...
      // Display the cursor mesh.
      meshRenderer.enabled = true;

      // Move the cursor to the point where the raycast hit.
      this.transform.position = hitInfo.point;

      // Rotate the cursor to hug the surface of the hologram.
      this.transform.rotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
    }
    else
    {
      // If the raycast did not hit a hologram, hide the cursor mesh.
      meshRenderer.enabled = false;
    }
  }
}

コードで重要な部分はRayCastオブジェクトです。RayCastオブジェクトは先ほど説明した「レーザービーム」に当たり、ユーザーがオブジェクトを見ているかどうかを判断するテストになります。ヒットがあれば、カーソルを表示させて、その表面に「貼り付く」ようにします。

ジェスチャー入力

視線によりインタラクトしたいオブジェクトに標的を定めたら、ジェスチャーを使って実際にインタラクトしてみます。もっとも一般的で簡単にできるのは「タップ」です。これは、マウスの左ボタンをクリックしてオブジェクトのアクションを実行するのに似ています。オブジェクトを3D空間で動かすときの「タップ&ホールド」のような、より複雑なジェスチャーもできます。

Unityで別のスクリプトを作成し、次のようなコードを入れます。すると、スクリプトはプロジェクトのルートにリンクし、ジェスチャーはアプリの中でグローバルに3Dオブジェクトとインタラクトします。

using UnityEngine;
using UnityEngine.VR.WSA.Input;

public class GazeGestureManager : MonoBehaviour
{
  public static GazeGestureManager Instance { get; private set; }

  // Represents the hologram that is currently being gazed at.
  public GameObject FocusedObject { get; private set; }

  GestureRecognizer recognizer;

  // Use this for initialization
  void Start()
  {
    Instance = this;

    // Set up a GestureRecognizer to detect Select gestures.
    recognizer = new GestureRecognizer();
    recognizer.TappedEvent += (source, tapCount, ray) =>
    {
      // Send an OnSelect message to the focused object and its ancestors.
      if (FocusedObject != null)
      {
        FocusedObject.SendMessageUpwards("OnSelect");
      }
    };
    recognizer.StartCapturingGestures();
  }

  // Update is called once per frame
  void Update()
  {
    // Figure out which hologram is focused this frame.
    GameObject oldFocusObject = FocusedObject;

    // Do a raycast into the world based on the user's
    // head position and orientation.
    var headPosition = Camera.main.transform.position;
    var gazeDirection = Camera.main.transform.forward;

    RaycastHit hitInfo;
    if (Physics.Raycast(headPosition, gazeDirection, out hitInfo))
    {
      // If the raycast hit a hologram, use that as the focused object.
      FocusedObject = hitInfo.collider.gameObject;
    }
    else
    {
      // If the raycast did not hit a hologram, clear the focused object.
      FocusedObject = null;
    }

    // If the focused object changed this frame,
    // start detecting fresh gestures again.
    if (FocusedObject != oldFocusObject)
    {
      recognizer.CancelGestures();
      recognizer.StartCapturingGestures();
    }
  }
}

このスニペットで注目すべきメインオブジェクトはGestureRecognizerです。GestureRecognizerは、ユーザーのジェスチャーを認識する役割があります。タップのジャスチャーをするたびに、オブジェクトにOnSelectイベントを渡すためにTappedEventを登録します。

Updateメソッドは、注視されているオブジェクトがあるかを常にチェックし、オブジェクトがタップされたらイベントに送信します。

音声入力

HoloLens(またはせめてエミュレーター)とのインタラクションの方法で気に入っているのは、音声を使うものです。「See it Say it」ルールと同様に、HoloLensが認識できるフレーズを自分で作成できます。アプリがあるフレーズを認識するとイベントが発生し、そのフレーズに対応する動作をします。

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Windows.Speech;

public class SpeechManager : MonoBehaviour
{
  KeywordRecognizer keywordRecognizer = null;
  Dictionary<string, System.Action> keywords = new Dictionary<string, System.Action>();

  // Use this for initialization
  void Start()
  {
    keywords.Add("Reset world", () =>
    {
      // Call the OnReset method on every descendant object.
      this.BroadcastMessage("OnReset");
    });

    keywords.Add("Drop Object", () =>
    {
      var focusObject = GazeGestureManager.Instance.FocusedObject;
      if (focusObject != null)
      {
        // Call the OnDrop method on just the focused object.
        focusObject.SendMessage("OnDrop");
      }
    });

    // Tell the KeywordRecognizer about our keywords.
    keywordRecognizer = new KeywordRecognizer(keywords.Keys.ToArray());

    // Register a callback for the KeywordRecognizer and start recognizing!
    keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized;
    keywordRecognizer.Start();
  }

  private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
  {
    System.Action keywordAction;
    if (keywords.TryGetValue(args.text, out keywordAction))
    {
      keywordAction.Invoke();
    }
  }
}

ここで難しい役割を担っているオブジェクトがKeywordRecognizerで、コンストラクターは配列としてキーワードを取得します。これがフレーズを認識するたび、OnPhraseRecognizedイベントが発生し、キーワードのアクションが実行されます。ユーザーが「Reset World」と言うとOnResetメッセージが送信され、「Drop Object」と言うとOnDropイベントが視線の先にあるオブジェクトに送られます。

オーディオ入力

主要なホログラム体験の最後は臨場感あふれるオーディオです。これはユーザーが入力する方法ではなく、ユーザーに対して体験中に起こっていることや注目すべきものを示す方法です。

オーディオを追加するのは技術的には難しくありませんが、映像に適切で必要なオーディオを追加するのは困難です。SDKは、ユーザーがHoloLensを通して見ている場所に応じて音を配置する役割がありますが、音が多すぎては混乱を招き、少なすぎてはユーザーは次にすべきことが分からなくなってしまいます。

次のスニペットでは、オブジェクトの衝撃音とオブジェクト(球体など)の回転音が追加されています。

using UnityEngine;

public class SphereSounds : MonoBehaviour
{
  AudioSource audioSource = null;
  AudioClip impactClip = null;
  AudioClip rollingClip = null;

  bool rolling = false;

  void Start()
  {
    // Add an AudioSource component and set up some defaults
    audioSource = gameObject.AddComponent<AudioSource>();
    audioSource.playOnAwake = false;
    audioSource.spatialize = true;
    audioSource.spatialBlend = 1.0f;
    audioSource.dopplerLevel = 0.0f;
    audioSource.rolloffMode = AudioRolloffMode.Custom;

    // Load the Sphere sounds from the Resources folder
    impactClip = Resources.Load<AudioClip>("Impact");
    rollingClip = Resources.Load<AudioClip>("Rolling");
  }

  // Occurs when this object starts colliding with another object
  void OnCollisionEnter(Collision collision)
  {
    // Play an impact sound if the sphere impacts strongly enough.
    if (collision.relativeVelocity.magnitude >= 0.1f)
    {
      audioSource.clip = impactClip;
      audioSource.Play();
    }
  }

  // Occurs each frame that this object continues to collide with another object
  void OnCollisionStay(Collision collision)
  {
    Rigidbody rigid = this.gameObject.GetComponent<Rigidbody>();

    // Play a rolling sound if the sphere is rolling fast enough.
    if (!rolling && rigid.velocity.magnitude >= 0.01f)
    {
      rolling = true;
      audioSource.clip = rollingClip;
      audioSource.Play();
    }
    // Stop the rolling sound if rolling slows down.
    else if (rolling && rigid.velocity.magnitude < 0.01f)
    {
      rolling = false;
      audioSource.Stop();
    }
  }

  // Occurs when this object stops colliding with another object
  void OnCollisionExit(Collision collision)
  {
    // Stop the rolling sound if the object falls off and stops colliding.
    if (rolling)
    {
      rolling = false;
      audioSource.Stop();
    } 
  }
}

OnCollisionEnterOnCollisionStayOnCollisionExitというイベントはそれぞれ、オーディオクリップの開始、継続、終了のタイミングを決定します。上のスニペットは衝突イベントによって音を追加する方法の1つですが、体験中に流す音楽や進行中のイベントにより発生した音声クリップなどの環境音の追加もできます。オーディオを追加することでより本物に近い体験が実現でき、ユーザーを魅了できます。

コード参照元:https://developer.microsoft.com/en-us/windows/holographic/holograms_101e

HoloDeckの将来

デスクトップやモバイル、企業環境向けの従来型のソフトウェアを制作する開発者にとって、HoloLensに対応したアプリや体験を制作するのは未知の世界への1歩です。3D空間の体験に関する考え方は、アプリ開発における従来の認識を刺激してくれます。

HoloLens向けアプリの制作で良い点は、始めるのに3000USドルを支払ってHoloLensを購入する必要がないことです。ツールはすべて無料で、開発したいシナリオのほぼすべてがエミュレーターでまかなえ、あとで本物のデバイスでテストできます。

HoloLensはWindows 10で動作するため、既存のUWPアプリが使え、それを拡張してHoloLensで動作させることで洗練されたホログラム体験を実現できます。

デジタルの未来は明るく、早い段階で新しいものを取り入れておくことが消費者に対して先手を取ることにつながります。

(原文:Getting Started with Microsoft HoloLens Development

[翻訳:Noriko O. Romano/編集:Livit

Copyright © 2017, Lars Klint All Rights Reserved.

Lars Klint

Lars Klint

フリーのソリューション構築家でPluralsight講師を務めています。MicrosoftのMVPを獲得し、Microsoftエコシステムに興味を持つポッドキャストのホストとしても活躍しています。今年で7年目を迎える350人以上の参加者を持つDDDメルボルンコミュニティーイベントの共同創設者でもあり、国内外のさまざまな技術イベントに出席しています。

Loading...