はじめに
この記事は、自分がHoloLens2とAndroidとでマルチプラットフォーム化する際に困った部分を記載している記事になります。
※バージョンが更新された場合修正されているかもしれないので気を付けてください。
前提条件
公式がだしているAzure Spatial Anchorsのチュートリアルドキュメント
イワケンさんが出されている記事を参考にし
これらを一通り実践してから自分のつまづいたところを確認していただけると
理解しやすくなると思います。
また記事後半は、Azure Spacial anchors
とphoton
を使った
多人数で体験させるためのコンテンツをつくる際のつまづいた点もご紹介いたします。
開発する上で参考になるドキュメントですのでこれも一通り試していただけますと幸いです。
※HoLoLens2二台とAndroidを使ったものとなります。
iOSはMACが自分の環境にないので検証できていないです。
開発環境
機材やソフト | バージョン |
---|---|
HoLoLens2 | 20348.1450 |
Unity | 2020.3.24f1 |
Windows10 | |
Visual Studio 2019 | |
Mixed Reality Feature Tool | v1.0.2111.0 Preview |
Xperia XZ1 | Android 9 |
Mixed Reality Feature Tool
Mixed Reality Toolkit | バージョン |
---|---|
Mixed Reality Toolkit Foundation | 2.7.3 |
Mixed Reality Toolkit Examples | 2.7.3 |
Mixed Reality Toolkit Standard Assets | 2.7.3 |
Mixed Reality Toolkit Test Utilities | 2.7.3 |
Mixed Reality Toolkit Tools | 2.7.3 |
Mixed Reality Toolkit Extensions | 2.7.3 |
Azure Mixed Reality Services | バージョン |
---|---|
Azure Spatial Anchors SDK Core | 2.12.0 |
Azure Spatial Anchors SDK Windows | 2.12.0 |
Azure Spatial Anchors SDK iOS | 2.12.0 |
Azure Spatial Anchors SDK Android | 2.12.0 |
Platform Support | バージョン |
---|---|
Mixed Reality OpenXR Plugin | 1.4.0 |
Unity Plugin & Asset
Unity Plugin | バージョン |
---|---|
ARCore XR Plugin | 4.1.9 |
ARKit XR Plugin | 4.1.9 |
Windows XR Plugin | 4.6.2 |
ARFoundation | 4.1.7 |
導入してつまずいた点
-
AndroidとiOSでは正常にAnchorへの移動が機能するのになぜか
HoloLens2
では機能しなかった。 -
先の問題を修正してマルチユーザー機能のチュートリアル通りに行ったが
自分の開発環境ではなぜかホストのアンカー位置へ移動してくれなかった。
1. HoloLens2でなぜAnchorへの移動が機能しなかったか
マイクロソフト公式が出されている
このリポジトリのReleaseで配布しているAzure Spatial Anchors v2.7.2
これに入っているAnchorModuleScript.cs
のCloudManager_AnchorLocated
に原因がありました。
private void CloudManager_AnchorLocated(object sender, AnchorLocatedEventArgs args)
{
QueueOnUpdate(new Action(() => Debug.Log($"Anchor recognized as a possible Azure anchor")));
if (args.Status == LocateAnchorStatus.Located || args.Status == LocateAnchorStatus.AlreadyTracked)
{
currentCloudAnchor = args.Anchor;
QueueOnUpdate(() =>
{
Debug.Log($"Azure anchor located successfully");
// Notify AnchorFeedbackScript
OnASAAnchorLocated?.Invoke();
#if WINDOWS_UWP || UNITY_WSA
// HoloLens: The position will be set based on the unityARUserAnchor thatwas located.
// Create a local anchor at the location of the object in question
gameObject.CreateNativeAnchor();
// Notify AnchorFeedbackScript
OnCreateLocalAnchor?.Invoke();
// On HoloLens, if we do not have a cloudAnchor already, we will have already positioned the
// object based on the passed in worldPos/worldRot and attached a new world anchor,
// so we are ready to commit the anchor to the cloud if requested.
// If we do have a cloudAnchor, we will use it's pointer to setup the world anchor,
// which will position the object automatically.
if (currentCloudAnchor != null)
{
Debug.Log("Local anchor position successfully set to Azure anchor position");
//gameObject.GetComponent<UnityEngine.XR.WSA.WorldAnchor>().SetNativeSpatialAnchorPtr(currentCloudAnchor.LocalAnchor);
}
#elif UNITY_ANDROID || UNITY_IOS
Pose anchorPose = Pose.identity;
anchorPose = currentCloudAnchor.GetPose();
Debug.Log($"Setting object to anchor pose with position '{anchorPose.position}' and rotation '{anchorPose.rotation}'");
transform.position = anchorPose.position;
transform.rotation = anchorPose.rotation;
// Create a native anchor at the location of the object in question
gameObject.CreateNativeAnchor();
// Notify AnchorFeedbackScript
OnCreateLocalAnchor?.Invoke();
#endif
});
}
else
{
QueueOnUpdate(new Action(() => Debug.Log($"Attempt to locate Anchor with ID '{args.Identifier}' failed, locate anchor status was not 'Located' but '{args.Status}'")));
}
}
通常のAnchorModuleScriptはこうなっているが
実際はHoloLensの対象コードでアンカーへ移動する処理がごっそりなくなってる!?
プラットフォーム依存コンパイルの
UNITY_ANDROID || UNITY_IOS のプラットフォーム達に入っている内容が
WINDOWS_UWP || UNITY_WSA に一部コードが入っていないのが原因
下記のコードが抜けている部分です。
Pose anchorPose = Pose.identity;
anchorPose = currentCloudAnchor.GetPose();
Debug.Log($"Setting object to anchor pose with position '{anchorPose.position}' and rotation '{anchorPose.rotation}'");
transform.position = anchorPose.position;
transform.rotation = anchorPose.rotation;
//removeAnchor = "Inactive";
//Create a native anchor at the location of the object in question
gameObject.CreateNativeAnchor();
//Notify AnchorFeedbackScript
OnCreateLocalAnchor?.Invoke();
下記のAnchorModuleScriptが今のをふまえて修正したコード
private void CloudManager_AnchorLocated(object sender, AnchorLocatedEventArgs args)
{
QueueOnUpdate(new Action(() => Debug.Log($"Anchor recognized as a possible Azure anchor")));
if (args.Status == LocateAnchorStatus.Located || args.Status == LocateAnchorStatus.AlreadyTracked)
{
currentCloudAnchor = args.Anchor;
QueueOnUpdate(() =>
{
Debug.Log($"Azure anchor located successfully");
// Notify AnchorFeedbackScript
OnASAAnchorLocated?.Invoke();
#if WINDOWS_UWP || UNITY_WSA
// HoloLens: The position will be set based on the unityARUserAnchor thatwas located.
// Create a local anchor at the location of the object in question
gameObject.CreateNativeAnchor();
// Notify AnchorFeedbackScript
OnCreateLocalAnchor?.Invoke();
// On HoloLens, if we do not have a cloudAnchor already, we will have already positioned the
// object based on the passed in worldPos/worldRot and attached a new world anchor,
// so we are ready to commit the anchor to the cloud if requested.
// If we do have a cloudAnchor, we will use it's pointer to setup the world anchor,
// which will position the object automatically.
if (currentCloudAnchor != null)
{
Debug.Log("Local anchor position successfully set to Azure anchor position");
//gameObject.GetComponent<UnityEngine.XR.WSA.WorldAnchor>().SetNativeSpatialAnchorPtr(currentCloudAnchor.LocalAnchor);
///////////// ここからOnCreateLocalAnchor?.Invoke();までの文がごっそりなくなっている!!!! /////////////
Pose anchorPose = Pose.identity;
anchorPose = currentCloudAnchor.GetPose();
Debug.Log($"Setting object to anchor pose with position '{anchorPose.position}' and rotation '{anchorPose.rotation}'");
transform.position = anchorPose.position;
transform.rotation = anchorPose.rotation;
//removeAnchor = "Inactive";
//Create a native anchor at the location of the object in question
gameObject.CreateNativeAnchor();
//Notify AnchorFeedbackScript
OnCreateLocalAnchor?.Invoke();
/////////////////////////////////////////////////////////////////////////////////////////////////////
}
#elif UNITY_ANDROID || UNITY_IOS
Pose anchorPose = Pose.identity;
anchorPose = currentCloudAnchor.GetPose();
Debug.Log($"Setting object to anchor pose with position '{anchorPose.position}' and rotation '{anchorPose.rotation}'");
transform.position = anchorPose.position;
transform.rotation = anchorPose.rotation;
// Create a native anchor at the location of the object in question
gameObject.CreateNativeAnchor();
// Notify AnchorFeedbackScript
OnCreateLocalAnchor?.Invoke();
#endif
});
}
else
{
QueueOnUpdate(new Action(() => Debug.Log($"Attempt to locate Anchor with ID '{args.Identifier}' failed, locate anchor status was not 'Located' but '{args.Status}'")));
}
}
これで一度HoloLensにデプロイすると
アンカーの場所に移動してることが確認できます。
この問題についてissueで触れられていました。
2.マルチユーザー機能でなぜホストのアンカー位置に移動してくれないか
自分の開発した環境ではなぜかアンカーの位置共有が行われませんでした。
先ほどの公式のサンプルでMulti-User Capabilities v2.7.2
に専用Unityパッケージがあるのでこれをimportしてください。
詳しい実装方法は、マルチユーザー機能のチュートリアルの概要のドキュメントを確認してください。
自分は、その中でもHoloLens2同士とAndroidとでアンカーの共有ができなかった問題の修正方法について説明します。
1.の問題を解決した後に、上記のマルチユーザー機能のチュートリアルを一通り終わらせてください。
ドキュメントではTableAnchor
と言うオブジェクトをHierarchy
に追加したと思いますがその際に
- AR Session
- AR Session Origin
- AR Anchor Maneger
この三つのコンポーネントをactive
にしてあると思います。
このコンポーネントたちを非active
にしてください。
そしてMixed Reality Toolkit
に下記を追加してください。
- AR Anchor Maneger
- AR Session Origin
- DisableDiagnosticsSystem → AndroidやiOSを使う場合だけ必要。
これでそれぞれのデバイスにデプロイすれば、
自分が作ったアンカーに他デバイスが、位置共有できていることを確認できます。
終わりに
これで誰でもHoloLens2同士や他デバイスと位置ずれを気にせずにコンテンツ作れます!!!!
参考になったサイト
今回、公式のドキュメントでうまくいかなかった方は下記の方々も同様に
Azure Spatial anchorsの使い方を記事にされているので
つまってしまった場合は彼らの記事を参考にすると良いと思います!!!
夜風のMixed Reality ホロ元さん
MRが楽しい ホロモンさん
Synamon’s Engineer blog Synamonさん