HoloLens (第 1 世代) と Azure 305: 関数とストレージ
注:
Mixed Reality Academy のチュートリアルは、HoloLens (第 1 世代) と Mixed Realityイマーシブ ヘッドセットを念頭に置いて設計されました。 そのため、これらのデバイスの開発に関するガイダンスをまだ探している開発者には、これらのチュートリアルを配置しておくことが重要であると考えます。 これらのチュートリアルは、HoloLens 2に使用されている最新のツールセットや相互作用では更新されません。 これらは、サポートされているデバイスでの作業を継続するために維持されます。 今後、HoloLens 2向けに開発する方法を示す新しい一連のチュートリアルが掲載されます。 この通知は、投稿時にこれらのチュートリアルへのリンクで更新されます。
このコースでは、mixed Reality アプリケーション内で、Azure Functionsを作成して使用し、Azure Storage リソースにデータを格納する方法について説明します。
Azure Functionsは Microsoft サービスであり、開発者は Azure で小さなコード '関数' を実行できます。 これにより、ローカル アプリケーションではなくクラウドに作業を委任する方法が提供されます。これには多くの利点があります。 Azure Functionsでは、C#、F#、Node.js、Java、PHP など、いくつかの開発言語がサポートされています。 詳細については、Azure Functionsに関する記事を参照してください。
Azure Storage は Microsoft クラウド サービスであり、開発者はデータを格納でき、高可用性、セキュリティ、持続性、スケーラブル、冗長になるという保険が適用されます。 つまり、Microsoft はすべてのメンテナンスと重大な問題を処理します。 詳細については、 Azure Storage に関する記事を参照してください。
このコースを完了すると、Mixed Reality イマーシブ ヘッドセット アプリケーションが用意され、次のことを行うことができます。
- ユーザーがシーンを視線で見つめることを許可します。
- ユーザーが 3D 'button' を視線入力したときに、オブジェクトのスポーンをトリガーします。
- 生成されたオブジェクトは、Azure 関数によって選択されます。
- 各オブジェクトが生成されると、アプリケーションは Azure Storage にある Azure File にオブジェクトの種類を格納 します。
- 2 回目の読み込み時に 、Azure File データが取得され、アプリケーションの前のインスタンスからのスポーン アクションの再生に使用されます。
アプリケーションでは、結果を設計と統合する方法はユーザー次第です。 このコースは、Azure サービスを Unity プロジェクトと統合する方法を説明するように設計されています。 このコースから得た知識を使用して、Mixed Reality アプリケーションを強化するのがあなたの仕事です。
デバイスのサポート
コース | HoloLens | イマーシブ ヘッドセット |
---|---|---|
MR と Azure 305: 関数とストレージ | ✔️ | ✔️ |
注:
このコースでは主に、Windows Mixed Realityイマーシブ (VR) ヘッドセットに焦点を当てていますが、このコースで学習した内容をMicrosoft HoloLensに適用することもできます。 コースに従うと、HoloLens をサポートするために採用する必要がある変更に関するメモが表示されます。
前提条件
注:
このチュートリアルは、Unityと C# に関する基本的な経験を持つ開発者向けに設計されています。 また、このドキュメント内の前提条件と書面による指示は、執筆時点 (2018 年 5 月) にテストおよび検証された内容を表していることにも注意してください。 ツールのインストールに関する記事に記載されている最新のソフトウェアは自由に使用できますが、このコースの情報は、以下に示すソフトウェアよりも新しいソフトウェアで見つかるものと完全に一致するとは想定しないでください。
このコースでは、次のハードウェアとソフトウェアをお勧めします。
- イマーシブ (VR) ヘッドセット開発用のWindows Mixed Realityと互換性のある開発用 PC
- 開発者モードが有効になっているWindows 10 Fall Creators Update (またはそれ以降)
- 最新のWindows 10 SDK
- Unity 2017.4
- Visual Studio 2017
- 開発者モードが有効になっているWindows Mixed Realityイマーシブ (VR) ヘッドセットまたはMicrosoft HoloLens
- Azure リソースを作成するための Azure アカウントのサブスクリプション
- Azure のセットアップとデータ取得のためのインターネット アクセス
始める前に
このプロジェクトのビルドで問題が発生しないように、このチュートリアルで説明するプロジェクトをルート フォルダーまたはほぼルート フォルダーに作成することを強くお勧めします (長いフォルダー パスがビルド時に問題を引き起こす可能性があります)。
第 1 章 - Azure Portal
Azure Storage Service を使用するには、Azure portalでストレージ アカウントを作成して構成する必要があります。
Azure Portal にログインします。
注:
Azure アカウントがまだない場合は、アカウントを作成する必要があります。 教室やラボの状況でこのチュートリアルに従っている場合は、講師またはプロクターの 1 人に新しいアカウントの設定に関するヘルプを依頼してください。
ログインしたら、左上隅の [ 新規 ] をクリックし、[ ストレージ アカウント] を検索し、[ Enter] をクリックします。
注:
新しいポータルでは、New という単語がリソースの作成に置き換えられた可能性があります。
新しいページには、 Azure Storage アカウント サービスの説明が表示されます。 このプロンプトの左下にある [ 作成 ] ボタンを選択して、このサービスとの関連付けを作成します。
[作成] をクリックしたら、次の手順を実行します。
アカウントの [名前] を挿入します。このフィールドは数字と小文字のみを受け入れることに注意してください。
[ デプロイ モデル] で、[ リソース マネージャー] を選択します。
[ アカウントの種類] で、[ ストレージ (汎用 v1)] を選択します。
リソース グループの 場所 を決定します (新しいリソース グループを作成する場合)。 この場所は、アプリケーションが実行されるリージョンに理想的です。 一部の Azure 資産は、特定のリージョンでのみ使用できます。
[ レプリケーション ] で、 読み取りアクセス geo 冗長ストレージ (RA-GRS) を選択します。
[パフォーマンス] で、[Standard] を選択します。
[ 安全な転送が必要] は [無効] のままにします。
サブスクリプションを選択 します。
リソース グループを選択するか、新しい リソース グループ を作成します。 リソース グループを使用すると、Azure 資産のコレクションの監視、アクセスの制御、課金のプロビジョニングと管理を行うことができます。 1 つのプロジェクト (これらのラボなど) に関連付けられているすべての Azure サービスを共通リソース グループの下に保持することをお勧めします。
Azure リソース グループの詳細については、 リソース グループに関する記事を参照してください。
また、本サービスに適用される使用条件を理解していることも確認する必要があります。
[作成] を選択します。
[ 作成] をクリックすると、サービスが作成されるまで待つ必要があります。これには 1 分かかる場合があります。
Service インスタンスが作成されると、ポータルに通知が表示されます。
通知をクリックして、新しいサービス インスタンスを確認します。
通知の [ リソースに移動 ] ボタンをクリックして、新しいサービス インスタンスを探索します。 新しい ストレージ アカウント サービス インスタンスにアクセスします。
[ アクセス キー] をクリックして、このクラウド サービスのエンドポイントを表示します。 メモ帳などを使用して、後で使用するためにいずれかのキーをコピーします。 また、 接続文字列 の値は、後で作成する AzureServices クラスで使用されるため、注意してください。
第 2 章 - Azure 関数の設定
次に、Azure サービスで Azure関数 を記述します。
Azure 関数を使用すると、コード内のクラシック関数で行うほぼすべての操作を実行できます。違いは、この関数は、Azure アカウントにアクセスするための資格情報を持つ任意のアプリケーションからアクセスできることです。
Azure 関数を作成するには:
Azure Portal で、左上隅にある [新規] をクリックし、[関数アプリ] を検索し、[Enter] をクリックします。
注:
新しいポータルでは、New という単語がリソースの作成に置き換えられた可能性があります。
新しいページには、 Azure Function App サービスの説明が表示されます。 このプロンプトの左下にある [ 作成 ] ボタンを選択して、このサービスとの関連付けを作成します。
[作成] をクリックしたら、次の手順を実行します。
アプリ名を指定します。 ここで使用できるのは文字と数字のみです (大文字または小文字を使用できます)。
お好みのサブスクリプションを選択 します。
リソース グループを選択するか、新しい リソース グループ を作成します。 リソース グループを使用すると、Azure 資産のコレクションの監視、アクセスの制御、課金のプロビジョニングと管理を行うことができます。 1 つのプロジェクト (これらのラボなど) に関連付けられているすべての Azure サービスを共通リソース グループの下に保持することをお勧めします。
Azure リソース グループの詳細については、 リソース グループに関する記事を参照してください。
この演習では、選択した OS として [Windows] を選択します。
ホスティング プランの [従量課金プラン] を選択します。
リソース グループの 場所 を決定します (新しいリソース グループを作成する場合)。 この場所は、アプリケーションが実行されるリージョンに理想的です。 一部の Azure 資産は、特定のリージョンでのみ使用できます。 最適なパフォーマンスを得るには、ストレージ アカウントと同じリージョンを選択します。
[ ストレージ] で [ 既存のものを使用] を選択し、ドロップダウン メニューを使用して、以前に作成したストレージを見つけます。
この演習 では、Application Insights をオフのままにします。
[作成] ボタンをクリックします。
[ 作成] をクリックすると、サービスが作成されるまで待つ必要があります。これには 1 分かかる場合があります。
Service インスタンスが作成されると、ポータルに通知が表示されます。
通知をクリックして、新しいサービス インスタンスを確認します。
通知の [ リソースに移動 ] ボタンをクリックして、新しいサービス インスタンスを探索します。 新しい Function App Service インスタンスにアクセスします。
関数アプリ ダッシュボードで、左側のパネル内にある Functions の上にマウス ポインターを置き、+ (プラス) 記号をクリックします。
次のページで、 Webhook + API が選択されていることを確認し、[ 言語の選択] で CSharp を選択します。これは、このチュートリアルで使用される言語になります。 最後に、[ この関数の作成 ] ボタンをクリックします。
ただし、コード ページ (run.csx) に移動する必要があります。そうでない場合は、左側のパネル内の [関数] リストで新しく作成された関数をクリックします。
次のコードを関数にコピーします。 この関数は、呼び出されたときに 0 から 2 の間のランダムな整数を返すだけです。 既存のコードについて心配しないでください。その上に自由に貼り付けてください。
using System.Net; using System.Threading.Tasks; public static int Run(CustomObject req, TraceWriter log) { Random rnd = new Random(); int randomInt = rnd.Next(0, 3); return randomInt; } public class CustomObject { public String name {get; set;} }
[保存] を選択します。
結果は次の図のようになります。
[ 関数 URL の取得] を クリックし、表示された エンドポイント をメモします。 このコースの後半で作成する AzureServices クラスに挿入する必要があります。
第 3 章 - Unity プロジェクトの設定
以下は、Mixed Realityを使用して開発するための一般的な設定であり、他のプロジェクトに適したテンプレートです。
Mixed Reality イマーシブ ヘッドセットを設定してテストします。
注:
このコースではモーション コントローラーは必要 ありません 。 イマーシブ ヘッドセットのセットアップのサポートが必要な場合は、 Mixed Reality のセットアップに関する記事を参照してください。
Unityを開き、[新規] をクリックします。
次に、Unityプロジェクト名を指定する必要があります。 MR_Azure_Functionsを挿入 します。 プロジェクトの種類が 3D に設定されていることを確認します。 [場所] を適切な場所に設定します (ルート ディレクトリに近い方が適しています)。 次に、[ プロジェクトの作成] をクリックします。
Unity開いている場合は、既定の [スクリプト] エディターが Visual Studio に設定されていることを確認する価値があります。 [編集>Preferences] に移動し、新しいウィンドウから [外部ツール] に移動します。 [外部スクリプト] エディターを Visual Studio 2017 に変更します。 [基本設定] ウィンドウを閉じます。
次に、[ファイル>Build 設定] に移動し、[プラットフォームの切り替え] ボタンをクリックして、プラットフォームをユニバーサル Windows プラットフォームに切り替えます。
[ファイル>Build の設定] に移動し、次の点を確認します。
[ターゲット デバイス] が [ 任意のデバイス] に設定されています。
Microsoft HoloLensの場合は、[ターゲット デバイス] を HoloLens に設定します。
ビルドの種類が D3D に設定されている
SDK が [最新インストール済み] に設定されている
Visual Studio のバージョンが [最新インストール済み] に設定されている
ビルドと実行がローカル コンピューターに設定されている
シーンを保存し、ビルドに追加します。
これを行うには、[ 開いているシーンの追加] を選択します。 保存ウィンドウが表示されます。
このために新しいフォルダーと将来のシーンを作成し、[ 新しいフォルダー ] ボタンを選択して新しいフォルダーを作成し、 Scenes という名前を付けます。
新しく作成した Scenes フォルダーを開き、[ ファイル名: テキスト] フィールドに 「FunctionsScene」と入力し、[ 保存] を押します。
[ ビルド設定] の残りの設定は、現時点では既定値のままにする必要があります。
[ ビルド設定] ウィンドウで、[ プレイヤーの設定] ボタンをクリックすると、 インスペクター が配置されている領域に関連するパネルが開きます。
このパネルでは、いくつかの設定を確認する必要があります。
[その他の設定] タブで、次 の手順を実行 します。
- スクリプトランタイムバージョンは試験段階 (.NET 4.6 同等) にする必要があります。これにより、エディターを再起動する必要があります。
- スクリプト バックエンドは .NET にする必要があります
- API 互換性レベルは .NET 4.6 にする必要があります
[発行設定] タブの [機能] で、次をチェックします。
InternetClient
パネルの下の [XR 設定] ([発行設定] の下にあります) で、[Virtual Reality Supported] をオンにして、Windows Mixed Reality SDK が追加されていることを確認します。
[ビルド設定] に戻りますUnity C# プロジェクトは灰色表示されなくなりました。この横にあるチェック ボックスをオンにします。
[ビルド設定] ウィンドウを閉じます。
シーンとプロジェクト (FILE>SAVE SCENE/FILE>SAVE プロジェクト) を保存します。
第 4 章 - メイン カメラのセットアップ
重要
「このコースのコンポーネントを設定する」のUnityをスキップし、そのままコードに進む場合は、この .unitypackage を自由にダウンロードして、カスタム パッケージとしてプロジェクトにインポートしてください。 これには、次の章の DLL も含まれます。 インポート後、 第 7 章から続行します。
階層パネルには、Main Camera という名前のオブジェクトがあります。このオブジェクトは、アプリケーションの "内部" になると"頭" の視点を表します。
Unity ダッシュボードが目の前にある状態で、Main Camera GameObject を選択します。 インスペクター パネル (一般にダッシュボード内の右側にあります) には、その GameObject のさまざまなコンポーネントが表示され、上部に Transform が表示され、次にカメラ、その他のコンポーネントが表示されます。 メイン カメラの変換をリセットして、正しく配置する必要があります。
これを行うには、カメラの変換コンポーネントの横にある歯車アイコンを選択し、[リセット] を選択します。
次に、 Transform コンポーネントを次のように更新します。
変換 - 位置
X | Y | Z |
---|---|---|
0 | 1 | 0 |
変換 - 回転
X | Y | Z |
---|---|---|
0 | 0 | 0 |
変換 - スケール
X | Y | Z |
---|---|---|
1 | 1 | 1 |
第 5 章 - Unity シーンの設定
階層パネルの空の領域を右クリックし、[3D オブジェクト] の下に平面を追加します。
Plane オブジェクトを選択した状態で、インスペクター パネルで次のパラメーターを変更します。
変換 - 位置
X | Y | Z |
---|---|---|
0 | 0 | 4 |
変換 - スケール
X | Y | Z |
---|---|---|
10 | 1 | 10 |
階層パネルの空の領域を右クリックし、[3D オブジェクト] の下にキューブを追加します。
キューブの名前を GazeButton に変更します (キューブが選択されている状態で、'F2' キーを押します)。
インスペクター パネルの [位置の変換] の次のパラメーターを変更します。
X Y Z 0 3 5 [ タグ ] ドロップダウン ボタンをクリックし、[タグの 追加 ] をクリックして、[ タグ] & [レイヤー] ウィンドウを開きます。
[+ (プラス)] ボタンを選択し、[新しいタグ名] フィールドに「GazeButton」と入力し、[保存] を押します。
階層パネルで GazeButton オブジェクトをクリックし、[インスペクター] パネルで新しく作成した GazeButton タグを割り当てます。
階層パネルで GazeButton オブジェクトを右クリックし、空の GameObject (子オブジェクトとして追加されます) を追加します。
新しいオブジェクトを選択し、 ShapeSpawnPoint という名前を変更します。
インスペクター パネルの [位置の変換] の次のパラメーターを変更します。
X Y Z 0 -1 0
次に、Azure サービスの状態に関するフィードバックを提供する 3D Text オブジェクトを作成します。
階層パネルで GazeButton を右クリックし、 3D オブジェクト>3D Text オブジェクトを 子として追加します。
3D Text オブジェクトの名前を AzureStatusText に変更します。
AzureStatusText オブジェクト Transform Position を次のように変更します。
X Y Z 0 0 -0.6 AzureStatusText オブジェクト Transform Scale を次のように変更します。 |X | Y | Z | |:---: |:---: |:---: | |0.1 |0.1 |0.1 |
注:
下のテキスト メッシュ コンポーネントが更新されると修正されるため、中央から外れているように見えても心配しないでください。
テキスト メッシュ コンポーネントを次に一致するように変更します。
ヒント
ここで選択した色は 16 進数の色: 000000FF ですが、自由に選択できますが、読み取り可能であることを確認してください。
階層パネルの構造は次のようになります。
これで、シーンは次のようになります。
第 6 章 - Unity用の Azure Storage のインポート
Unityには Azure Storage を使用します (それ自体が .Net SDK for Azure を利用します)。 詳細については、「Azure Storage for Unity」の記事を参照してください。
現在、Unityには、インポート後にプラグインを再構成する必要がある既知の問題があります。 バグが解決された後、これらの手順 (このセクションの 4 ~ 7) は不要になります。
SDK を独自のプロジェクトにインポートするには、GitHub から最新の '.unitypackage' をダウンロードしていることを確認します。 その後、次の操作を行います。
[Assets>Import Package>Custom Package] メニュー オプションを使用して、.unitypackage ファイルをUnityに追加します。
ポップアップ表示される [Unity パッケージのインポート] ボックスで、[プラグイン>Storage] の下にあるすべてを選択できます。 このコースでは必要ではないため、他のチェック を外します。
[ インポート ] ボタンをクリックして、項目をプロジェクトに追加します。
[プラグイン] の下の [ストレージ] フォルダーに移動し、[プロジェクト] ビューで次のプラグインのみを選択します。
Microsoft.Data.Edm
Microsoft.Data.OData
Microsoft.WindowsAzure.Storage
Newtonsoft.Json
System.Spatial
これらの特定のプラグインを選択した状態で、[任意のプラットフォーム] をオフにし、[WSAPlayer] をオフにして、[適用] をクリックします。
注:
これらの特定のプラグインは、Unity エディターでのみ使用するようにマークされています。 これは、プロジェクトがUnityからエクスポートされた後に使用される同じプラグインのバージョンが WSA フォルダーに存在するためです。
[ ストレージ プラグイン] フォルダーで、次のみを選択します。
Microsoft.Data.Services.Client
[プラットフォーム設定] の [処理しない] ボックスをオンにし、[適用] をクリックします。
注:
Unityアセンブリパッチがこのプラグインを処理するのが難しいので、このプラグインを 「処理しない」とマークしています。 プラグインは、処理されていない場合でも引き続き機能します。
第 7 章 - AzureServices クラスを作成する
最初に作成するクラスは 、AzureServices クラスです。
AzureServices クラスは、次の役割を担います。
Azure アカウント資格情報の格納。
Azure アプリ関数の呼び出し。
Azure Cloud Storage 内のデータ ファイルのアップロードとダウンロード。
このクラスを作成するには:
プロジェクト パネルの [作成>Folder] にある [アセット フォルダー] を右クリックします。 [スクリプト] フォルダーに名前 を付けます。
作成したフォルダーをダブルクリックして開きます。
[ Create>C# Script] フォルダー内を右クリックします。 スクリプト AzureServices を呼び出します。
新しい AzureServices クラスをダブルクリックして 、Visual Studio で開きます。
AzureServices の上部に次の名前空間を追加します。
using System; using System.Threading.Tasks; using UnityEngine; using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage.File; using System.IO; using System.Net;
AzureServices クラス内に次のインスペクター フィールドを追加します。
/// <summary> /// Provides Singleton-like behavior to this class. /// </summary> public static AzureServices instance; /// <summary> /// Reference Target for AzureStatusText Text Mesh object /// </summary> public TextMesh azureStatusText;
次に、 AzureServices クラス内に次のメンバー変数を追加します。
/// <summary> /// Holds the Azure Function endpoint - Insert your Azure Function /// Connection String here. /// </summary> private readonly string azureFunctionEndpoint = "--Insert here you AzureFunction Endpoint--"; /// <summary> /// Holds the Storage Connection String - Insert your Azure Storage /// Connection String here. /// </summary> private readonly string storageConnectionString = "--Insert here you AzureStorage Connection String--"; /// <summary> /// Name of the Cloud Share - Hosts directories. /// </summary> private const string fileShare = "fileshare"; /// <summary> /// Name of a Directory within the Share /// </summary> private const string storageDirectory = "storagedirectory"; /// <summary> /// The Cloud File /// </summary> private CloudFile shapeIndexCloudFile; /// <summary> /// The Linked Storage Account /// </summary> private CloudStorageAccount storageAccount; /// <summary> /// The Cloud Client /// </summary> private CloudFileClient fileClient; /// <summary> /// The Cloud Share - Hosts Directories /// </summary> private CloudFileShare share; /// <summary> /// The Directory in the share that will host the Cloud file /// </summary> private CloudFileDirectory dir;
重要
エンドポイントと接続文字列の値は、Azure Portal にある Azure ストレージの値に置き換えてください
Awake() メソッドと Start() メソッドのコードを追加する必要があります。 クラスが初期化されると、これらのメソッドが呼び出されます。
private void Awake() { instance = this; } // Use this for initialization private void Start() { // Set the Status text to loading, whilst attempting connection to Azure. azureStatusText.text = "Loading..."; } /// <summary> /// Call to the Azure Function App to request a Shape. /// </summary> public async void CallAzureFunctionForNextShape() { }
重要
今後のチャプターで CallAzureFunctionForNextShape() のコードを入力します。
このクラスでは使用されないため、 Update() メソッドを削除します。
変更を Visual Studio に保存し、Unityに戻ります。
[スクリプト] フォルダーから [階層パネル] の Main Camera オブジェクトに AzureServices クラスをクリックしてドラッグします。
メイン カメラを選択し、GazeButton オブジェクトの下から AzureStatusText 子オブジェクトを取得し、AzureStatusText 参照ターゲット フィールド内のインスペクターに配置して、AzureServices スクリプトへの参照を提供します。
第 8 章 - ShapeFactory クラスを作成する
次に作成するスクリプトは、 ShapeFactory クラスです。 このクラスの役割は、要求されたときに新しい図形を作成し、図形履歴リストに作成された図形の履歴を保持 することです。 図形が作成されるたびに、AzureService クラスで [図形の履歴] リストが更新され、Azure Storage に格納されます。 アプリケーションの起動時に、Azure Storage に格納されているファイルが見つかった場合、生成された図形がストレージからのものか新規かを指定する 3D Text オブジェクトを使用して、図形履歴リストが取得されて再生されます。
このクラスを作成するには:
前に作成した Scripts フォルダーに移動します。
[ Create>C# Script] フォルダー内を右クリックします。 スクリプト ShapeFactory を呼び出します。
新しい ShapeFactory スクリプトをダブルクリックして 、Visual Studio で開きます。
ShapeFactory クラスに次の名前空間が含まれていることを確認します。
using System.Collections.Generic; using UnityEngine;
次に示す変数を ShapeFactory クラスに追加し、 Start() 関数と Awake() 関数を次の関数に置き換えます。
/// <summary> /// Provide this class Singleton-like behaviour /// </summary> [HideInInspector] public static ShapeFactory instance; /// <summary> /// Provides an Inspector exposed reference to ShapeSpawnPoint /// </summary> [SerializeField] public Transform spawnPoint; /// <summary> /// Shape History Index /// </summary> [HideInInspector] public List<int> shapeHistoryList; /// <summary> /// Shapes Enum for selecting required shape /// </summary> private enum Shapes { Cube, Sphere, Cylinder } private void Awake() { instance = this; } private void Start() { shapeHistoryList = new List<int>(); }
CreateShape() メソッドは、指定された整数パラメーターに基づいてプリミティブ図形を生成します。 ブール型 (Boolean) パラメーターは、現在作成されている図形がストレージからのものか新しい図形かを指定するために使用されます。 前のメソッドの下にある ShapeFactory クラスに次のコードを配置します。
/// <summary> /// Use the Shape Enum to spawn a new Primitive object in the scene /// </summary> /// <param name="shape">Enumerator Number for Shape</param> /// <param name="storageShape">Provides whether this is new or old</param> internal void CreateShape(int shape, bool storageSpace) { Shapes primitive = (Shapes)shape; GameObject newObject = null; string shapeText = storageSpace == true ? "Storage: " : "New: "; AzureServices.instance.azureStatusText.text = string.Format("{0}{1}", shapeText, primitive.ToString()); switch (primitive) { case Shapes.Cube: newObject = GameObject.CreatePrimitive(PrimitiveType.Cube); break; case Shapes.Sphere: newObject = GameObject.CreatePrimitive(PrimitiveType.Sphere); break; case Shapes.Cylinder: newObject = GameObject.CreatePrimitive(PrimitiveType.Cylinder); break; } if (newObject != null) { newObject.transform.position = spawnPoint.position; newObject.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f); newObject.AddComponent<Rigidbody>().useGravity = true; newObject.GetComponent<Renderer>().material.color = UnityEngine.Random.ColorHSV(0f, 1f, 1f, 1f, 0.5f, 1f); } }
Unityに戻る前に、変更を Visual Studio に保存してください。
Unity エディターに戻り、ShapeFactory クラスを [スクリプト] フォルダーから階層パネルの Main Camera オブジェクトにドラッグします。
[メイン カメラ] を選択すると、 ShapeFactory スクリプト コンポーネントに スポーン ポイント 参照がありません。 修正するには、 ShapeSpawnPoint オブジェクトを 階層パネル から Spawn Point 参照ターゲットにドラッグします。
第 9 章 - 視線入力クラスを作成する
最後に作成する必要があるスクリプトは 、Gaze クラスです。
このクラスは、メイン カメラから前方に投影される レイキャスト を作成して、ユーザーが見ているオブジェクトを検出します。 この場合、レイキャストは、ユーザーがシーン内の GazeButton オブジェクトを見ているかどうかを識別し、動作をトリガーする必要があります。
このクラスを作成するには:
前に作成した Scripts フォルダーに移動します。
[プロジェクト] パネルの [ 作成>C# スクリプト] を右クリックします。 スクリプト Gaze を呼び出します。
新しい視線入力スクリプトをダブルクリックして、Visual Studio で開きます。
スクリプトの先頭に次の名前空間が含まれていることを確認します。
using UnityEngine;
次に、 Gaze クラス内に次の変数を追加します。
/// <summary> /// Provides Singleton-like behavior to this class. /// </summary> public static Gaze instance; /// <summary> /// The Tag which the Gaze will use to interact with objects. Can also be set in editor. /// </summary> public string InteractibleTag = "GazeButton"; /// <summary> /// The layer which will be detected by the Gaze ('~0' equals everything). /// </summary> public LayerMask LayerMask = ~0; /// <summary> /// The Max Distance the gaze should travel, if it has not hit anything. /// </summary> public float GazeMaxDistance = 300; /// <summary> /// The size of the cursor, which will be created. /// </summary> public Vector3 CursorSize = new Vector3(0.05f, 0.05f, 0.05f); /// <summary> /// The color of the cursor - can be set in editor. /// </summary> public Color CursorColour = Color.HSVToRGB(0.0223f, 0.7922f, 1.000f); /// <summary> /// Provides when the gaze is ready to start working (based upon whether /// Azure connects successfully). /// </summary> internal bool GazeEnabled = false; /// <summary> /// The currently focused object. /// </summary> internal GameObject FocusedObject { get; private set; } /// <summary> /// The object which was last focused on. /// </summary> internal GameObject _oldFocusedObject { get; private set; } /// <summary> /// The info taken from the last hit. /// </summary> internal RaycastHit HitInfo { get; private set; } /// <summary> /// The cursor object. /// </summary> internal GameObject Cursor { get; private set; } /// <summary> /// Provides whether the raycast has hit something. /// </summary> internal bool Hit { get; private set; } /// <summary> /// This will store the position which the ray last hit. /// </summary> internal Vector3 Position { get; private set; } /// <summary> /// This will store the normal, of the ray from its last hit. /// </summary> internal Vector3 Normal { get; private set; } /// <summary> /// The start point of the gaze ray cast. /// </summary> private Vector3 _gazeOrigin; /// <summary> /// The direction in which the gaze should be. /// </summary> private Vector3 _gazeDirection;
重要
これらの変数の一部は、エディターで編集できます。
Awake() メソッドと Start() メソッドのコードを追加する必要があります。
/// <summary> /// The method used after initialization of the scene, though before Start(). /// </summary> private void Awake() { // Set this class to behave similar to singleton instance = this; } /// <summary> /// Start method used upon initialization. /// </summary> private void Start() { FocusedObject = null; Cursor = CreateCursor(); }
次のコードを追加します。このコードは、起動時にカーソル オブジェクトを作成し、Raycast メソッドを実行する Update() メソッドと共に、GazeEnabled ブール値が切り替えられる場所を追加します。
/// <summary> /// Method to create a cursor object. /// </summary> /// <returns></returns> private GameObject CreateCursor() { GameObject newCursor = GameObject.CreatePrimitive(PrimitiveType.Sphere); newCursor.SetActive(false); // Remove the collider, so it doesn't block raycast. Destroy(newCursor.GetComponent<SphereCollider>()); newCursor.transform.localScale = CursorSize; newCursor.GetComponent<MeshRenderer>().material = new Material(Shader.Find("Diffuse")) { color = CursorColour }; newCursor.name = "Cursor"; newCursor.SetActive(true); return newCursor; } /// <summary> /// Called every frame /// </summary> private void Update() { if(GazeEnabled == true) { _gazeOrigin = Camera.main.transform.position; _gazeDirection = Camera.main.transform.forward; UpdateRaycast(); } }
次に UpdateRaycast() メソッドを追加します。これにより、Raycast が投影され、ヒット ターゲットが検出されます。
private void UpdateRaycast() { // Set the old focused gameobject. _oldFocusedObject = FocusedObject; RaycastHit hitInfo; // Initialise Raycasting. Hit = Physics.Raycast(_gazeOrigin, _gazeDirection, out hitInfo, GazeMaxDistance, LayerMask); HitInfo = hitInfo; // Check whether raycast has hit. if (Hit == true) { Position = hitInfo.point; Normal = hitInfo.normal; // Check whether the hit has a collider. if (hitInfo.collider != null) { // Set the focused object with what the user just looked at. FocusedObject = hitInfo.collider.gameObject; } else { // Object looked on is not valid, set focused gameobject to null. FocusedObject = null; } } else { // No object looked upon, set focused gameobject to null. FocusedObject = null; // Provide default position for cursor. Position = _gazeOrigin + (_gazeDirection * GazeMaxDistance); // Provide a default normal. Normal = _gazeDirection; } // Lerp the cursor to the given position, which helps to stabilize the gaze. Cursor.transform.position = Vector3.Lerp(Cursor.transform.position, Position, 0.6f); // Check whether the previous focused object is this same // object. If so, reset the focused object. if (FocusedObject != _oldFocusedObject) { ResetFocusedObject(); if (FocusedObject != null) { if (FocusedObject.CompareTag(InteractibleTag.ToString())) { // Set the Focused object to green - success! FocusedObject.GetComponent<Renderer>().material.color = Color.green; // Start the Azure Function, to provide the next shape! AzureServices.instance.CallAzureFunctionForNextShape(); } } } }
最後に、 ResetFocusedObject() メソッドを追加します。これにより、GazeButton オブジェクトの現在の色が切り替わります。これは、新しい図形を作成しているかどうかを示します。
/// <summary> /// Reset the old focused object, stop the gaze timer, and send data if it /// is greater than one. /// </summary> private void ResetFocusedObject() { // Ensure the old focused object is not null. if (_oldFocusedObject != null) { if (_oldFocusedObject.CompareTag(InteractibleTag.ToString())) { // Set the old focused object to red - its original state. _oldFocusedObject.GetComponent<Renderer>().material.color = Color.red; } } }
Unityに戻る前に、Visual Studio に変更を保存します。
[スクリプト] フォルダーの [視線入力] クラスをクリックして、[階層] パネルの [Main Camera] オブジェクトにドラッグします。
第 10 章 - AzureServices クラスの完了
他のスクリプトを配置すると、AzureServices クラスを完了できるようになりました。 これは、次の方法で実現されます。
CreateCloudIdentityAsync()という名前の新しいメソッドを追加して、Azure との通信に必要な認証変数を設定します。
このメソッドは、図形リストを含む以前に格納された File の存在についてもチェックします。
ファイルが見つかった場合、Azure Storage ファイルに格納されている図形のパターンに従って、ユーザーの視線入力が無効になり、図形の作成がトリガーされます。 テキスト メッシュは図形の原点に応じて表示 'Storage' または 'New' を提供するため、ユーザーはこれを確認できます。
ファイルが見つからない場合は、 視線入力が有効になり、ユーザーはシーン内の GazeButton オブジェクトを見るときに図形を作成できます。
/// <summary> /// Create the references necessary to log into Azure /// </summary> private async void CreateCloudIdentityAsync() { // Retrieve storage account information from connection string storageAccount = CloudStorageAccount.Parse(storageConnectionString); // Create a file client for interacting with the file service. fileClient = storageAccount.CreateCloudFileClient(); // Create a share for organizing files and directories within the storage account. share = fileClient.GetShareReference(fileShare); await share.CreateIfNotExistsAsync(); // Get a reference to the root directory of the share. CloudFileDirectory root = share.GetRootDirectoryReference(); // Create a directory under the root directory dir = root.GetDirectoryReference(storageDirectory); await dir.CreateIfNotExistsAsync(); //Check if the there is a stored text file containing the list shapeIndexCloudFile = dir.GetFileReference("TextShapeFile"); if (!await shapeIndexCloudFile.ExistsAsync()) { // File not found, enable gaze for shapes creation Gaze.instance.GazeEnabled = true; azureStatusText.text = "No Shape\nFile!"; } else { // The file has been found, disable gaze and get the list from the file Gaze.instance.GazeEnabled = false; azureStatusText.text = "Shape File\nFound!"; await ReplicateListFromAzureAsync(); } }
次のコード スニペットは 、Start() メソッド内にあります。ここで、 CreateCloudIdentityAsync() メソッドの呼び出しが行われます。 次のように、現在の Start() メソッドを自由にコピーしてください。
private void Start() { // Disable TLS cert checks only while in Unity Editor (until Unity adds support for TLS) #if UNITY_EDITOR ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; #endif // Set the Status text to loading, whilst attempting connection to Azure. azureStatusText.text = "Loading..."; //Creating the references necessary to log into Azure and check if the Storage Directory is empty CreateCloudIdentityAsync(); }
メソッド CallAzureFunctionForNextShape()のコードを入力します。 以前に作成した Azure Function App を 使用して、図形インデックスを要求します。 新しい図形を受け取ると、このメソッドは 図形を ShapeFactory クラスに送信して、シーンに新しい図形を作成します。 CallAzureFunctionForNextShape()の本文を完了するには、次のコードを使用します。
/// <summary> /// Call to the Azure Function App to request a Shape. /// </summary> public async void CallAzureFunctionForNextShape() { int azureRandomInt = 0; // Call Azure function HttpWebRequest webRequest = WebRequest.CreateHttp(azureFunctionEndpoint); WebResponse response = await webRequest.GetResponseAsync(); // Read response as string using (Stream stream = response.GetResponseStream()) { StreamReader reader = new StreamReader(stream); String responseString = reader.ReadToEnd(); //parse result as integer Int32.TryParse(responseString, out azureRandomInt); } //add random int from Azure to the ShapeIndexList ShapeFactory.instance.shapeHistoryList.Add(azureRandomInt); ShapeFactory.instance.CreateShape(azureRandomInt, false); //Save to Azure storage await UploadListToAzureAsync(); }
図形履歴リストに格納されている整数を連結し、 Azure Storage File に保存することで、文字列を作成するメソッドを追加します。
/// <summary> /// Upload the locally stored List to Azure /// </summary> private async Task UploadListToAzureAsync() { // Uploading a local file to the directory created above string listToString = string.Join(",", ShapeFactory.instance.shapeHistoryList.ToArray()); await shapeIndexCloudFile.UploadTextAsync(listToString); }
Azure Storage File にあるファイルに格納されているテキストを取得し、一覧に逆シリアル化するメソッドを追加します。
このプロセスが完了すると、メソッドは視線入力を再度有効にして、ユーザーがシーンにさらに図形を追加できるようにします。
///<summary> /// Get the List stored in Azure and use the data retrieved to replicate /// a Shape creation pattern ///</summary> private async Task ReplicateListFromAzureAsync() { string azureTextFileContent = await shapeIndexCloudFile.DownloadTextAsync(); string[] shapes = azureTextFileContent.Split(new char[] { ',' }); foreach (string shape in shapes) { int i; Int32.TryParse(shape.ToString(), out i); ShapeFactory.instance.shapeHistoryList.Add(i); ShapeFactory.instance.CreateShape(i, true); await Task.Delay(500); } Gaze.instance.GazeEnabled = true; azureStatusText.text = "Load Complete!"; }
Unityに戻る前に、Visual Studio に変更を保存します。
第 11 章 - UWP ソリューションを構築する
ビルド プロセスを開始するには:
[ファイル>ビルド設定] に移動します。
[ ビルド] をクリックします。 Unityはエクスプローラー ウィンドウを起動します。ここで、アプリをビルドするフォルダーを作成して選択する必要があります。 そのフォルダーを今すぐ作成し、App という名前 を付けます。 次に、 App フォルダーが選択された状態で、[ フォルダーの選択] を押します。
Unityは、App フォルダーへのプロジェクトのビルドを開始します。
ビルドUnity完了すると (時間がかかる場合があります)、ビルドの場所にエクスプローラー ウィンドウが開きます (タスク バーチェック、常にウィンドウの上に表示されるとは限りませんが、新しいウィンドウが追加されたことを通知します)。
第 12 章 - アプリケーションのデプロイ
アプリケーションをデプロイするには:
最後のチャプターで作成された App フォルダーに移動します。 ".sln" 拡張子を持つアプリ名のファイルが表示されます。このファイルはダブルクリックして Visual Studio 内で開く必要があります。
ソリューション プラットフォームで、x86、ローカル コンピューターを選択します。
[ソリューション構成] で [デバッグ] を選択します。
Microsoft HoloLensでは、コンピューターにテザリングされないように、これをリモート コンピューターに設定する方が簡単な場合があります。 ただし、次の操作も行う必要があります。
- HoloLens の IP アドレスは、[設定]>Network & インターネット>Wi-Fi>Advanced オプションにあります。IPv4 は使用する必要があるアドレスです。
- [開発者モード] が [オン] になっていることを確認します。「設定>Update & Security>開発者向け」にあります。
[ビルド] メニューに移動し、[ソリューションのデプロイ] をクリックして、アプリケーションをコンピューターにサイドロードします。
アプリがインストールされているアプリの一覧に表示され、起動してテストする準備が整いました。
完成したAzure Functionsとストレージ アプリケーション
これで、Azure Functions サービスと Azure Storage サービスの両方を活用する Mixed Reality アプリが構築されました。 アプリは、保存されたデータを描画し、そのデータに基づいてアクションを提供できます。
ボーナス演習
演習 1
2 つ目のスポーン ポイントを作成し、オブジェクトが作成されたスポーン ポイントを記録します。 データ ファイルを読み込むときに、最初に作成された場所から生成される図形を再生します。
演習 2
アプリを毎回開き直す必要なく、アプリを再起動する方法を作成します。 [シーンの読み込み] は、開始するのに適した場所です。 その後、アプリから簡単にリセットできるように、 Azure Storage に格納されているリストをクリアする方法を作成します。