Unity の Relay と Lobby について調べたメモ
How to 記事ではありません。自分が Unity の Relay と Lobby で調べたことをメモ代わりに。
Relay
Unity Documentation (Unity Relay)
プレイヤー間で安全性の高いピアツーピアの、リッスンサーバーを使用した UDP 通信を簡単に実現(原文ママ)
- Relay servers という Unity が提供してくれている(のかな?)専用サーバを proxy のように介すことでプレイヤー間での接続を実現
- 全プレイヤーが接続可能なエンドポイントとして Relay server は振る舞い、全プレイヤーは同じ IP アドレス、ポートに接続する
- つまり実際にプレイヤー同士が直接コネクションを張る訳ではない
- NAT 越えやファイヤーウォールなど一般的にネットワーク接続において課題となる問題を気にせずに済む
最大プレイヤー数は allocation を作成するときに指定可能
プレイヤーが タイムアウト したら Relay server はそのプレイヤーを切断する
- デフォルト TTL は 10秒
- ただしホストが一人の場合は TTL は 60秒
- タイムアウトを防ぐために PING メッセージ を Relay server に送信できる
- もし Relay with NGO を使っているなら network manager が自動でコネクションを維持する
- もし Relay with UTP を使っているなら手動でコネクションを維持する必要がある
- CLOSE message を送信しても接続解除が可能
Allocations
ゲームセッションのためにゲームサーバーを予約する機能
リクエストを受信したらそれに最適なサーバーを探し割り当てる
- サーバーが見つかったらそれを利用可能なサーバープールから削除し、その情報を呼び出し元へ送信する
- deallocates するまで残り続ける
- ゲームサーバーの検索は主に以下の情報でもって行われる
- サーバーのリージョン
- サーバーとゲームクライアントの通信品質
- サーバーのビルドとビルド設定
-
- Game Server Hosting API を使用
- Game Server Hosting SDK を使用
- Unity Cloud Dashboard を使用
Relay with Netcode for GameObjects (NGO)
Relay with Netcode for GameObjects (NGO)
- NGO 側のホスト、サーバー、クライアント間での Rpc 呼び出し方法などのサンプルがまとまっている
これらを読めば Relay と NGO を使ったマルチプレイの簡単な実装ができる
ただし別ネットワーク間で通信する場合は Network Manager の Unity Transport の Allow remote connection
を有効にしないといけなさそうだった
Lobby
プレイヤーがマルチプレイゲームにおいて他のプレイヤーを探して接続するための機能を提供するサービス
サービス例
- 利用可能なゲームセッションのリストを表示し、プレイヤーがセッションを選択して参加
- フレンドと join code を共有して自分のゲームセッションにフレンドが直接接続
- Quick Join を使って利用可能なマッチを探して参加
- プライベートロビーを作りゲーム内フレンドリストのフレンドを招待
- ゲームサーバからロビーをホストし、サーバーセッションへのアクセスを管理及び制限
- ゲームモードやタイプなどといった要求をもとににロビーをクエリ
プレイヤーの既存のゲームセッションへの再ジョインを可能に
予期しない切断後のホストの移行を容易に
ロビーの作成
いくつかのプロパティをセットして create コールを行うことでロビーが作成できる
プロパティ
ロビーサンプル
Unity Documentation (Game lobby sample)
まだ試せてないが、規模の大きいサンプルのようで既存のプロジェクトに入れて試すというよりはリファレンスとする方が良いみたい。
Relay vs Lobby
Relay
- サーバー/クライアント環境を使用してプレイヤーを P2P 方式で接続するネットワーキングソリューション
- 専用ゲームサーバーや P2P 接続の複雑さを無視してマルチプレイヤーゲームのセッションを容易に実現
Lobby
Unity の Netcode for GameObjects について調べたメモ
Netcode for GameObjects で自分が気になって調べたことをメモ代わりにまとめておく。
Netcode for GameObjects が何かとか、How to Use 系の記事は他にたくさんあるのでそのあたりは触れません。
参考にしたサイト
About Netcode for GameObjects | Unity Multiplayer Networking
Unity Netcode for GameObjectsを使ったオンラインマルチプレイゲーム開発Tips【Advent Calendar 12/23】|Colorful Palette
ClientRPC
ClientRpc | Unity Multiplayer Networking
サーバーは ClientRPC を実行することで(デフォルトで)全クライアントに対して命令を行える
- ClientRpcSendParameters を使えば特定のクライアントでのみ ClientRPC を実行可能
Host から ClientRPC を実行する場合は Host 自身もクライアントとして振る舞うため自身の ClientRPC も実行される
- 同じスタックで呼ばれるため無限ループにならないよう注意
宣言するには
[ClientRPC]
アトリビュートをつけるのと、メソッド名にClientRpc
プレフィックスをつけないといけない- それと前提として
NetworkBehavior
継承のクラスにする必要がある
- それと前提として
特定のクライアントにのみ send したい場合は ClientRpcSendParameters を使う。以下公式からコピペ。
private void DoSomethingServerSide(int clientId) { // If isn't the Server/Host then we should early return here! if (!IsServer) return; // NOTE! In case you know a list of ClientId's ahead of time, that does not need change, // Then please consider caching this (as a member variable), to avoid Allocating Memory every time you run this function ClientRpcParams clientRpcParams = new ClientRpcParams { Send = new ClientRpcSendParams { TargetClientIds = new ulong[]{clientId} } }; // Let's imagine that you need to compute a Random integer and want to send that to a client const int maxValue = 4; int randomInteger = Random.Range(0, maxValue); DoSomethingClientRpc(randomInteger, clientRpcParams); } [ClientRpc] private void DoSomethingClientRpc(int randomInteger, ClientRpcParams clientRpcParams = default) { if (IsOwner) return; // Run your client-side logic here!! Debug.LogFormat("GameObject: {0} has received a randomInteger with value: {1}", gameObject.name, randomInteger); }
ServerRPC
ServerRpc | Unity Multiplayer Networking
クライアントからサーバに情報を送信する際実行する RPC
- クライアントからしか送信できない&サーバーかホストしか受信できない
宣言には
[ServerRpc]
アトリビュートと ServerRpc プレフィックスをメソッドにつける必要がある- NetworkBehavior 継承のクラスである必要がある
自身が NetworkObject のオーナーではない場合は ServerRpc の実行が許可されない
- しかし
[ServerRpc(RequireOwnership = false)]
を宣言すれば任意のクライアントが ServerRpc を実行できるっぽい(以下公式をコピペ)
- しかし
[ServerRpc(RequireOwnership = false)] public void MyGlobalServerRpc(ServerRpcParams serverRpcParams = default) { var clientId = serverRpcParams.Receive.SenderClientId; if (NetworkManager.ConnectedClients.ContainsKey(clientId)) { var client = NetworkManager.ConnectedClients[clientId]; // Do things for this client } } public override void OnNetworkSpawn() { MyGlobalServerRpc(); // serverRpcParams will be filled in automatically }
ServerRpcParams.Receive.SenderClientId
で Rpc を実行したクライアントの特定が可能- 他の引数がある場合は引数の最後に
ServerRpcParams
を宣言する必要がある - 以下の例ではクライアントが銃を打った時にクライアントの PlayerObject から lookWorldPosition に向けて Ray を打つサンプル(以下公式をコピペ)
- 他の引数がある場合は引数の最後に
[ServerRpc(RequireOwnership = false)] public void PlayerShootGunServerRpc(Vector3 lookWorldPosition, ServerRpcParams serverRpcParams = default) { var clientId = serverRpcParams.Receive.SenderClientId; if (NetworkManager.ConnectedClients.ContainsKey(clientId)) { var client = NetworkManager.ConnectedClients[clientId]; var castRay = new Ray(client.PlayerObject.transform.position, lookWorldPosition); RaycastHit rayCastHit; if (Physics.Raycast(castRay, out rayCastHit, 100.0f)) { // Handle shooting something } } }
なお、RequireOwnership を false にしない場合は上の例で Vector3 引数だけ設定すれば良いらしい。
[ServerRpc] public void PlayerOwnerShootGunServerRpc(Vector3 lookWorldPosition) { if (NetworkManager.ConnectedClients.ContainsKey(OwnerClientId)) { var client = NetworkManager.ConnectedClients[OwnerClientId]; var castRay = new Ray(client.PlayerObject.transform.position, lookWorldPosition); RaycastHit rayCastHit; if (Physics.Raycast(castRay, out rayCastHit, 100.0f)) { // Handle shooting something } } }
- ホストで ServerRpc を実行したら自身の ServerRpc が実行されるので無限ループにならないよう注意が必要
NetworkVariable
NetworkVariable | Unity Multiplayer Networking
サーバとクライアント間で RPC など使わずに値の同期ができる仕組み
テンプレート
<T>
の型の値のコンテナのラッパーなので、その値はNetworkVariable.Value
で取得するRpc との使い分け
- RPC vs NetworkVariable
- Rpc はその瞬間にのみ有用な一時的なイベントや情報を送受信したい時に使う
- 後から参加したプレイヤーにそれまで送られた Rpc が自動で送られることは無い
- NetworkVariable はずっと必要な永続的なステートや情報の同期に使う
最後に
Unity の Relay についても今度調査したい。まだベータ版。ベータ中は無料みたい。
OpenCV plus Unity の顔検出を iOS で行う
OpenCV plus Unity(無料) は PC(自分は Mac) 上ではインポートしてそのまま iOS 向けに Build すると Build 時にエラーが出たのでその対処方法
環境
参考にしたリンク
エラー1
Exception: IL2CPP error (no further information about what managed code was being converted is available) Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly: 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' at Unity.Cecil.Awesome.AssemblyResolver.ResolveInternal(AssemblyNameReference name) in /Users/bokken/build/output/unity/il2cpp/Unity.Cecil.Awesome/AssemblyResolver.cs:line 153 at Unity.Cecil.Awesome.AssemblyResolver.Resolve(AssemblyNameReference name) in /Users/bokken/build/output/unity/il2cpp/Unity.Cecil.Awesome/AssemblyResolver.cs:line 62 at Unity.Cecil.Awesome.WindowsRuntimeAwareMetadataResolver.Resolve(TypeReference type) in /Users/bokken/build/output/unity/il2cpp/Unity.Cecil.Awesome/WindowsRuntimeAwareMetadataResolver.cs:line 99
Exception: IL2CPP error (no further information about what managed code was being converted is available) Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly: 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' at Unity.Cecil.Awesome.AssemblyResolver.ResolveInternal(AssemblyNameReference name) in /Users/bokken/build/output/unity/il2cpp/Unity.Cecil.Awesome/AssemblyResolver.cs:line 153 at Unity.Cecil.Awesome.AssemblyResolver.Resolve(AssemblyNameReference name) in /Users/bokken/build/output/unity/il2cpp/Unity.Cecil.Awesome/AssemblyResolver.cs:line 62 at Unity.Cecil.Awesome.WindowsRuntimeAwareMetadataResolver.Resolve(TypeReference type) in /Users/bokken/build/output/unity/il2cpp/Unity.Cecil.Awesome/WindowsRuntimeAwareMetadataResolver.cs:line 99 at Mono.Cecil.Mixin.CheckedResolve(TypeReference self) at Mono.Cecil.SignatureReader.ReadCustomAttributeEnum(TypeReference enum_type) at Mono.Cecil.SignatureReader.ReadCustomAttributeElement(TypeReference type) at Mono.Cecil.SignatureReader.ReadCustomAttributeNamedArgument(Collection`1& fields, Collection`1& properties) at Mono.Cecil.SignatureReader.ReadCustomAttributeNamedArguments(UInt16 count, Collection`1& fields, Collection`1& properties) at Mono.Cecil.SignatureReader.ReadSecurityAttribute() at Mono.Cecil.MetadataReader.ReadSecurityDeclarationSignature(SecurityDeclaration declaration) at Mono.Cecil.SecurityDeclaration.<Resolve>b__19_0(SecurityDeclaration declaration, MetadataReader reader) at Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TItem item, Func`3 read) at Mono.Cecil.SecurityDeclaration.Resolve() at Mono.Cecil.SecurityDeclaration.get_SecurityAttributes() at Mono.Cecil.ImmediateModuleReader.ReadSecurityDeclarations(ISecurityDeclarationProvider provider) at Mono.Cecil.ImmediateModuleReader.ReadMethods(TypeDefinition type) at Mono.Cecil.ImmediateModuleReader.ReadType(TypeDefinition type) at Mono.Cecil.ImmediateModuleReader.ReadTypes(Collection`1 types) at Mono.Cecil.ImmediateModuleReader.ReadModule(ModuleDefinition module, Boolean resolve_attributes) at Mono.Cecil.ImmediateModuleReader.<ReadModule>b__2_0(ModuleDefinition module, MetadataReader reader) at Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TItem item, Func`3 read) at Mono.Cecil.ImmediateModuleReader.ReadModule() at Mono.Cecil.ModuleReader.CreateModule(Image image, ReaderParameters parameters) at Mono.Cecil.ModuleDefinition.ReadModule(String fileName, ReaderParameters parameters) at Mono.Cecil.AssemblyDefinition.ReadAssembly(String fileName, ReaderParameters parameters) at Unity.Cecil.Awesome.AssemblyLoader.Load(String name) in /Users/bokken/build/output/unity/il2cpp/Unity.Cecil.Awesome/AssemblyLoader.cs:line 84 at System.Linq.Enumerable.SelectArrayIterator`2.MoveNext() at System.Collections.Generic.HashSet`1.UnionWith(IEnumerable`1 other) at System.Collections.Generic.HashSet`1..ctor(IEnumerable`1 collection, IEqualityComparer`1 comparer) at Unity.IL2CPP.Common.AssemblyCollector.CollectAssembliesRecursive(IEnumerable`1 assemblies, IAssemblyLoader assemblyLoader, AssemblyDependenciesComponent assemblyDependencies) in /Users/bokken/build/output/unity/il2cpp/Unity.IL2CPP.Common/AssemblyCollector.cs:line 25 at Unity.IL2CPP.Common.AssemblyCollector.CollectAssembliesToConvert(NPath[] assemblyPaths, IAssemblyLoader assemblyLoader, AssemblyDependenciesComponent assemblyDependencies) in /Users/bokken/build/output/unity/il2cpp/Unity.IL2CPP.Common/AssemblyCollector.cs:line 15 at Unity.IL2CPP.AssemblyConversion.Phases.InitializePhase.Run(AssemblyConversionContext context) in /Users/bokken/build/output/unity/il2cpp/Unity.IL2CPP/AssemblyConversion/Phases/InitializePhase.cs:line 18 at Unity.IL2CPP.AssemblyConversion.Classic.ClassicConverter.Run(AssemblyConversionContext context) in /Users/bokken/build/output/unity/il2cpp/Unity.IL2CPP/AssemblyConversion.Classic/ClassicConverter.cs:line 11 at Unity.IL2CPP.AssemblyConversion.AssemblyConverter.ConvertAssemblies(AssemblyConversionInputData data, AssemblyConversionParameters parameters, AssemblyConversionInputDataForTopLevelAccess dataForTopLevel) in /Users/bokken/build/output/unity/il2cpp/Unity.IL2CPP/AssemblyConversion/AssemblyConverter.cs:line 21 UnityEditorInternal.Runner.RunProgram (UnityEditor.Utils.Program p, System.String exe, System.String args, System.String workingDirectory, UnityEditor.Scripting.Compilers.CompilerOutputParserBase parser) (at /Users/bokken/buildslave/unity/build/Editor/Mono/BuildPipeline/BuildUtils.cs:129) UnityEditorInternal.Runner.RunManagedProgram (System.String exe, System.String args, System.String workingDirectory, UnityEditor.Scripting.Compilers.CompilerOutputParserBase parser, System.Action`1[T] setupStartInfo) (at /Users/bokken/buildslave/unity/build/Editor/Mono/BuildPipeline/BuildUtils.cs:65) UnityEditorInternal.IL2CPPBuilder.RunIl2CppWithArguments (System.Collections.Generic.List`1[T] arguments, System.Action`1[T] setupStartInfo, System.String generatedCppOutputDirectory) (at /Users/bokken/buildslave/unity/build/Editor/Mono/BuildPipeline/Il2Cpp/IL2CPPUtils.cs:755) UnityEditorInternal.IL2CPPBuilder.ConvertPlayerDlltoCpp (UnityEditor.Il2Cpp.Il2CppBuildPipelineData data, System.String outputDirectory, System.Boolean platformSupportsManagedDebugging) (at /Users/bokken/buildslave/unity/build/Editor/Mono/BuildPipeline/Il2Cpp/IL2CPPUtils.cs:727) UnityEditorInternal.IL2CPPBuilder.Run () (at /Users/bokken/buildslave/unity/build/Editor/Mono/BuildPipeline/Il2Cpp/IL2CPPUtils.cs:560) UnityEditorInternal.IL2CPPUtils.RunIl2Cpp (System.String tempFolder, System.String stagingAreaData, UnityEditorInternal.IIl2CppPlatformProvider platformProvider, System.Action`1[T] modifyOutputBeforeCompile, UnityEditor.RuntimeClassRegistry runtimeClassRegistry) (at /Users/bokken/buildslave/unity/build/Editor/Mono/BuildPipeline/Il2Cpp/IL2CPPUtils.cs:231) UnityEditor.iOS.PostProcessiPhonePlayer.CrossCompileManagedDlls (UnityEditor.iOS.PostProcessiPhonePlayer+BuildSettings bs, UnityEditor.iOS.PostProcessiPhonePlayer+ProjectPaths paths, UnityEditor.AssemblyReferenceChecker checker, UnityEditor.RuntimeClassRegistry usedClassRegistry, UnityEditor.Build.Reporting.BuildReport buildReport) (at /Users/bokken/buildslave/unity/build/PlatformDependent/iPhonePlayer/Extensions/Common/BuildPostProcessor.cs:908) UnityEditor.iOS.PostProcessiPhonePlayer.PostProcess (UnityEditor.iOS.PostProcessiPhonePlayer+BuildSettings bs, UnityEditor.iOS.PostProcessiPhonePlayer+ProjectPaths paths, UnityEditor.RuntimeClassRegistry usedClassRegistry, UnityEditor.Build.Reporting.BuildReport buildReport) (at /Users/bokken/buildslave/unity/build/PlatformDependent/iPhonePlayer/Extensions/Common/BuildPostProcessor.cs:718) UnityEditor.iOS.PostProcessiPhonePlayer.PostProcess (UnityEditor.iOS.PostProcessorSettings postProcessorSettings, UnityEditor.Modules.BuildPostProcessArgs args) (at /Users/bokken/buildslave/unity/build/PlatformDependent/iPhonePlayer/Extensions/Common/BuildPostProcessor.cs:663) UnityEditor.iOS.iOSBuildPostprocessor.PostProcess (UnityEditor.Modules.BuildPostProcessArgs args) (at /Users/bokken/buildslave/unity/build/PlatformDependent/iPhonePlayer/Extensions/Common/ExtensionModule.cs:45) Rethrow as BuildFailedException: Exception of type 'UnityEditor.Build.BuildFailedException' was thrown. UnityEditor.iOS.iOSBuildPostprocessor.PostProcess (UnityEditor.Modules.BuildPostProcessArgs args) (at /Users/bokken/buildslave/unity/build/PlatformDependent/iPhonePlayer/Extensions/Common/ExtensionModule.cs:49) UnityEditor.Modules.DefaultBuildPostprocessor.PostProcess (UnityEditor.Modules.BuildPostProcessArgs args, UnityEditor.BuildProperties& outProperties) (at /Users/bokken/buildslave/unity/build/Editor/Mono/Modules/DefaultBuildPostprocessor.cs:29) UnityEditor.PostprocessBuildPlayer.Postprocess (UnityEditor.BuildTargetGroup targetGroup, UnityEditor.BuildTarget target, System.String installPath, System.String companyName, System.String productName, System.Int32 width, System.Int32 height, UnityEditor.BuildOptions options, UnityEditor.RuntimeClassRegistry usedClassRegistry, UnityEditor.Build.Reporting.BuildReport report) (at /Users/bokken/buildslave/unity/build/Editor/Mono/BuildPipeline/PostprocessBuildPlayer.cs:337) UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr, Boolean&) (at /Users/bokken/buildslave/unity/build/Modules/IMGUI/GUIUtility.cs:189)
対処方法1
Assets
直下に下記ファイル link.xml
を置く。
<linker> <assembly fullname="netstandard" preserve="all"/> </linker>
その他
CascadeClassifier haarCascade = new CascadeClassifier("Assets/OpenCV+Unity/Demo/Face_Detector/haarcascade_frontalface_default.xml");
で顔検出したい場合は Assets/StreamingAssets
下に haarcascade_frontalface_default.xml
を置いて
CascadeClassifier haarCascade = new CascadeClassifier(Application.streamingAssetsPath + "/haarcascade_frontalface_default.xml");
で指定しれば iOS 上でも顔検出が可能。
Azure Spatial Anchor を触って ARCloud について考察する
目的
- Azure Spatial Anchor がどんなものなのか触ってみて理解する
- 今後 ARCloud のようにクラウド上で Map やそれに紐づく情報を管理するシステムを作ろうとした時 Azure Spatial Anchor がどんな感じに使えそうなのか掴む
ドキュメント
開発環境
- HoloLens
- Windows 10 Home 1903
- Unity 2019.2.9 f1
- Visual Studio 2019+コンポーネント
- Azure 無料アカウント
事前知識
以下のリンク先の内容は開発前にざっと目を通しておくと良さそう。
- Spatial Anchors の概要
- Github の Spatial Anchors Sample
- 認証について
- アンカーのリレーションシップとウェイファインディング
- CloudSpatialAnchorSession クラス
- アンカーを効果的に作成して配置するためのガイドラインと考慮事項
- ログの記録
1. Azure Spatial Anchors を使用する Unity HoloLens アプリを作成する
↑の通りに
- Spatial Anchors リソースを作成する
- Unity のサンプル プロジェクトをダウンロードして開く
- アカウント識別子とキーを構成する
- HoloLens Visual Studio プロジェクトをエクスポートする
- HoloLens アプリケーションをデプロイする
をやれば HoloLens にサンプルアプリがデプロイされる。
ちなみに、Visual Studio のユニバーサル Windows プラットフォーム開発のオプションの[USBデバイスの接続]にチェックを入れないと、USB 経由で HoloLens にアプリがデプロイできないので注意。
Android/iOS へのデプロイも同じ Scene でビルド対象プラットフォームを PlayerSettings で切り替えれば可能なのが驚き。
次は、別デバイスや別セッション間で Anchor を共有する。
2. セッションやデバイス間での Azure Spatial Anchors の共有
これも↑の通りやればよい。何なのかサマると
- Anchor を一定期間保存して共有するための ASP.NET Core Web App を Visual Studio から発行
- Unity 側の設定で
Base Sharing Url
に Web API の url を指定 - Android や iOS, HoloLens にアプリをデプロイ
をやっている。
公式ドキュメントは何故か .NET Core 2.2 SDK 指定だが 3.0 でも問題なく動作した。
Web App(というか API) は Swagger UIで書かれている。
試しに Anchor を登録した後にブラウザから https://<appname>.azurewebsites.net/api/anchors/last
GET を叩くと
6a83ce25-2604-41bc-9199-caec66b27583
という string 値が返ってきた。これが Anchor を特定する Key である。
では次に Anchor が一定期間たっても消えないよう、メモリでなく DB に保存して例えば別の日でも Anchor が共有できるようにする。
3. Azure Cosmos DB を使って Azure Spatial Anchors を共有する
Azure Cosmos DB の概要 | Microsoft Docs
↑Azure Cosmos DB について参考になったリンク
↑の手順通り進める。
先程との違いは Azure Cosmos DB のインスタンスをたてて、Anchor をインメモリではなく DB に保存するようにしてる点。Web App も API のレイヤでは全く変わらない。
試しに Android 端末にデプロイし Anchor を追加した後 Azure ポータルから Cosmos DB の中を確認すると1行追加されている。
ここにある Anchor key は、
Azure Spatial Anchors についてよく寄せられる質問 | Microsoft Docs
アンカーが作成または特定されるときは、デバイス上で環境の画像が処理されて派生形式になります。 この派生形式が転送されて、サービスに格納されます。 透明性を提供するため、環境の画像と派生されたまばらな点の集まりを以下に示します。 点の集まりは、サービスに転送されて格納される環境の幾何学的表現を示します。 まばらな点の集まりの各点について、その点の視覚的特徴のハッシュが送信されて格納されます。 ハッシュは任意のピクセル データから派生されますが、そのピクセル データは含みません。
ということらしい。この key が Anchor そのものだったということか。
ここで一つ疑問が。
Anchor の共有はこのハッシュ値があれば可能だが、Anchor に紐付いて共有されるオブジェクト(サンプルだと黄色の Cube)は誰が持ってるんだろう?と思い調べたけどよく分からなかった。
簡単なオブジェクト(Cube)の position くらいならこの key に含まれているのかもしれない?ただそれだと共有できるオブジェクト数や種類に限界があるため、いろいろ共有したりリアルタイムに更新したいなら別 DB 等用意する必要がありそう。
MixedRealityToolkit の SharingWithUNET サンプルと比較
1年ほど前に MixedRealityToolkit のサンプルにあった(今もあるかは分からないが)UNet を使い複数 HoloLens 間で P2P 接続してクライアント・サーバに分かれてオブジェクトをリアルタイムに共有する SharingWithUNET を触ったことがある。
↑参考記事
それと今回の Azure App Service 経由での Anchor 共有をざっくり比較すると、
Pros | Cons | |
---|---|---|
SharingWithUNET | 同一ネットワーク内でのP2P接続なのでリアルタイムでの同期が早い | 別ネットワーク・端末間では共有できない |
Azure Spatial Anchor | 別端末・ネットワーク間でも共有できる サーバ側に Anchor の情報が蓄積される |
レイテンシがそれなりにありそう オブジェクトをリアルタイムに共有するには別の仕組みを用意する必要がありそう |
こんな印象。
まとめ
Azure Spatial Anchor の概要は理解できた。が、まだサンプル通りにやって動かせただけなので、いろいろなオブジェクトのリアルタイム共有やクラウド側でのデータ活用もやっていきたい。
少し勘違いしていたが HoloLens が持つような(wifi をキーとした?)Map をまるまる Azure にあげてそれを複数端末間で共有することではない。Anchor も特徴点の情報+αくらいの情報しか含まれていない。ARCore/ARKit には現時点で Map の Save/Load 機能はないので当然か。
Anchor での位置合わせは少し時間がかかるな、という印象を受けた。特徴点が多い場所を使ったり、特徴点マッチングアルゴリズムを自前で用意するのもアリかも?
ARCloud への応用例
また ARCloud のようなものを実現したいと考えた時、(何をどうクラウドで管理したいかはユースケースによりつつも)仮にいくつかの公園の Map をクラウドで管理し、特定の時間に特定の場所にアイテムを出現させたい、としたら
- Map データは S3 or GCS に保存
- 公園ID、中心 GPS 座標、Mapへのリンク、ゲームアイテムのプロパティ(リスト)、Anchor
あたりは最低限クラウドのデータベースに持たせる必要がありそうな気がする。
これに加えて、池や砂場がある等それぞれの公園が持つ特有のプロパティも保持れば、その公園の特徴にあったオブジェクトを表示させたり、も出来て楽しそう。(どうプロパティを集めるかはさておき)
このように Anchor では保持できない情報を別 DB 等で管理し、端末間での位置合わせは Azure Spatial Anchor を使うように分担するのが良さげかな、とざっくりと妄想してこの記事は終わり。
Nreal Developer Gathering に参加してきた
Nreal Developer Gathering@10/8渋谷に参加し、nreal 開発者のプレゼンを聞き実機デモを体験してきました。
個人的に nreal なかなか良かったので MR アプリ開発者目線で感じたことをまとめようかなと。書きなぐりですが。
(Nreal と nreal どっちが正しいか分かりませんが本記事では nreal に統一)
nreal 基本情報
ここらへん読めば雰囲気掴めると思いますが、
- 1199$の Developer Kit 版と499$の Consumer Kit 版がある
- Dev 版は購入申請可能。Cons 版は 2020年早期より出荷予定
- 軽い。88g
- SLAM, 6DoF, 平面検知, イメージトラッキング等が可能
- マイクとスピーカーも搭載し将来的には音声コントロールにも対応予定
- 対角52°の FoV
- 日本の MR 市場を重視
- KDDI とパートナーシップを組んでる
nreal 開発者のプレゼンで分かったこと
- nreal の社員は現在120人
- 45%は修士卒
- 北京に HQ
- スピーカーは DTS surround 対応
- 用途は game/sns/education/production と特に制限なく想定
- preorder 数は 4K で US>JP>CN の順に多い
- 会社を立ち上げてから2年でリリース
- 度つきレンズが装着可能(マグネット式)で、眼鏡を外して体験できた
- 表示素子のマイクロ OLED は片目フル HD の120Hz駆動(ソニー製?
- ほぼ半球形の曲面レンズorハーフミラーで前方に飛ばした光をレンズ部の曲面ハーフミラーで収束させて表示
- CES2020 までは Dev 向け開発、CES2020 以降は Cons 向け開発にシフト
- 大手町に日本支社を立ち上げ
- 将来のロードマップにはハンドトラッキングも
- Android Native App が nreal で動作するらしい(詳細は不明
- MESON 社、Psychic VR Lab 社は nreal のエバンジェリスト
- MESON 社は nreal を使って街空間での AR コミュニケーションを開発、実証実験
nreal の所感
まず良かった点。
- デザイン良し。軽いので日常使いできそう
- デザインには相当力を入れているらしい
- SLAM の安定感は Holo/ML1/ARKit/ARCore に比べると劣りブレが気になる
- ただかけやすさ、つけ心地、値段等を鑑みると Holo/ML1 より全体として良い感じ
- 視野角が Holo より広く、ML1 と同等程度の印象
- ただし横長視野角なのでキャラクターを近くに表示させたり、とかは難しそう
- Android 端末と接続できるので、GPS を活用したり他アプリと連携したりとコンテンツの幅を広げられそう
- nreal でデモすれば「 AR グラスを日常的につけられる時代がきたら〜」の説得力が増しそう
- コントローラー付きなのでインタラクティブなゲームも作れそう
- ただしコントローラーは 3DoF なので用途は限られるかも
ここからうーんな点。
- 現実がかなり見えづらい
- 体感70%ほど外の情報が遮断されていた気がしたので nreal をつけて外を出歩くのは厳しそう
- 現実がほぼほぼ見えないせいで AR オブジェクトが現実にある感(MR 感)が薄い
- オクルージョン無いのが残念
- Spatial Computing してる感が Holo/ML1 に比べて薄い、というか無い
- イメージとしては SLAM つき Google Glass のような印象で、眼の前にディスプレイがあるなーという感じ(伝わるかな・・
- 床や壁の上に何か出したり、という感じではなさそう。今まさに環境の認識まわり頑張っているところなのかも?
- マップの保存等についても言及されてなかったけど、まだ実装されていないのかも?
という感じでした! ちょっとふわっとした感想が多いけど、少しでも nreal をかけた印象が伝われば。
Oculus Quest Mixed Reality Capture Tools セットアップ手順
(注意)現状では Oculus Quest のゲームを Mixed Realty Capture(以降 MRC と略)できないようです。自分で開発した MRC 対応アプリ、もしくは MRC に対応したアプリ(まだ0個)のみ撮影が可能です。
しかも途中で OBS Studio の使い方がわからずギブアップしてます・・すみません。何かわかれば追記していくつもりです。
ただ今後 MRC 対応 Quest アプリが増えてくるかもなので、現状できたところまでですが、公式のセットアップの日本語訳版ということでメモ。
参考リンク
下準備
必要な機材
- Oculus Quest
- USB/HDMI カメラ
- PC
- Oculus Quest と同 wifi ネットワークに繋ぐ必要があるためノート PC の方が良いかも?
- キャプチャーカード
- Elgato HD6OS、HD60Pro 等が候補として挙がっているが、ノート PC でも使える HD6OS を今回は使用
- Oculus Rift or Oculus Rift S
- Quest とは別に、セットアップ用に必要。せっかくなので自分は最新の Rift S を購入。腹くくる
- クロマキー背景布
必要なソフトウェア
// フォルダ構成 Oculus_Quest_MRC_Tools_1.0 |- MRCCameraCalibration_Quest | |- MrcCameraCalibration.apk // Oculus Quest にインストールする apk |- oculus-mrc_OBS | |- data | |- obs-plugin |- OVRCameraCalibration_PC // PC 側で起動するカメラキャリブ用ファイルが入ってる |- OVRCameraCalibration.bat |- etc
- OBS Studio
- 映像の合成に使用
必要な資材
Oculus_Quest_MRC_Tools_1.0/OVRCameraCalibration_PC/Assets/pattern.png
を印刷- カメラのキャリブで使うためサイズや画質は変更しない方がいいとのこと(なので画像はここには貼りません)
- ただし自分は画像をスマホ上に表示してそれを使いました。これでも問題なさそうです
- Oculus Quest と Rift S の近接センサー(額の前あたりにあるやつ)を覆うテープ
- キャリブ中テープを使って HMD の電源が落ちないように
カメラキャリブレーション
- Quest を adb コマンドが使える PC に接続
adb install -r MrcCameraCalibration.apk
- Quest から MRC Camera Calibration Service を起動して IP アドレス(192.168.1.4)をメモ
- カメラを設置(今後カメラ位置は動かないように)
- Quest の近接センサーにテープを貼り電源がオフにならないようにする
OVRCameraCalibration.bat
内の IP アドレスを VSCode 等で開き先ほど調べた Quest の IP アドレスに変更OVRCameraCalibration.bat
を起動し Quest で起動しているアプリと接続が開始すると QuestMode というウインドウが開く- Calibrate Camera を選択
- 適切なカメラを選択し次へ
- 画質は 1920*1080 が推奨なのでそれで設定して次へ
- Start Intrinsic Calibration を選択
- この時点でカメラ映像が見えてない場合はどこかで設定を間違えている可能性があります
- マーカーを画面上に表示されているボックスに合わせる作業を20回やりカメラの歪みを補正
- 認識がうまくいくと枠内が虹色になります
- 最後に出てきた値が1以下なら良いみたい
- static camera を選択して次へ
- 今度は Oculus Rift S のセンサーにシールを貼り、矩形内に入れて A ボタンを押して進める
- Oculus Touch コントローラとバーチャル Touch コントローラが重なって表示されていたら成功なので mrc.xml という名前で保存
キャリブレーションファイルを Quest へ転送
Mixed Reality Capture を行いたいアプリへ先ほど作った mrc.xml をコピーします。
以下は例として Beat Saber 1. android file transfer や adb コマンドを使用して mrc.xml を /Android/data/com.{yourorgname}.{yourappname}/files/
にコピー + adb なら adb push <path of the saved mrc.xml> /sdcard/Android/data/com.{yourorgname}.{yourappname}/files/
+ android file transfer なら↑の場所にドラッグ&ドロップでコピー
OBS を使ってシーンを複合
- Oculus_Quest_MRC_Tools_1.0\oculus-mrc_OBS の下にある data, obs-plugins を obs-studio のルートにコピー
- 例) C:\Program Files\obs-studio
- OBS Studio を起動
- OBS Studio の使い方はここ
ここからは OBS Studio の使い方がよくわからなかったため、公式ドキュメントを読んで進めてください。
とりあえずここまで。中途半端ですみませんがギブアップ・・
Unity で地図が表示可能な mapbox を触ってみた(初級編)
はじめに
XR+位置情報
なゲームを作りたいなーと思い Unity 向けにパッケージが公開されてる mapbox を触ってみました。
公式ドキュメントはこちら。
今回は mapbox SDK を使って地図をUnity 上で出してみることを実現したいと思います。こんな感じです。
環境
Maps SDK for Unity v2.0.0
Unity 2019.1.2f1
使い方
Unity でのセットアップ方法は↑に書いてあるため割愛。
基本的には unitypackage をインポートし Mapbox->Prefabs->Map
プレハブを hierarchy 上に配置後
Play
すれば地図が表示されます。
この Map
プレハブについてる Abstract Map(Script)
から地図のいろんなパラメータの調整や、レイヤの追加等が可能です。
ただし↑の公式ドキュメントは SDK バージョンが1.4.0対応っぽく少し古いため、一部 API 仕様が微妙に違うので注意。(2019/5/21現在)
Scene の中心に9マスに分割された地図が表示されています。しかしこのままでは画面をドラッグしてカメラ視点を変えたり、緯度経度を自由に指定する等できません。
SDK の Mapbox->Examples
にあるサンプルを参考にいろんな操作ができることを確かめてみてください。
では実際にいろいろ触ってみようと思います。
その前に
Unity 2019 で SDK をインポートしたら下記のエラーをはいたので、
Library/PackageCache/com.unity.xr.arfoundation@1.0.0-preview.22/Runtime/AR/ARSessionOrigin.cs(3,19): error CS0234: The type or namespace name 'SpatialTracking' does not exist in the namespace 'UnityEngine' (are you missing an assembly reference?)
https://github.com/Unity-Technologies/arfoundation-samples/issues/79#issuecomment-450263059
どうもここが参考になりそうとのことで
"com.unity.xr.legacyinputhelpers": "1.0.0"
の設定を manifest.json
の一番下に追記することで暫定対応。
理由を詳しく追えていませんが、どうも AR Foundation(ARKit/ARCore のマルチプラットフォーム AR 環境)関連の問題のようなので、Unity のアップデートでいずれ対応されると思います。
AR Foundation についてはこちらが参考になりました。
それでは気を取り直して
特定の緯度経度にプレハブを配置
大崎駅付近 (35.619707, 139.7283495)
に適当なプレハブを表示したいと思います。
GENERAL
タブ内の Location を35.619707, 139.7283495
にMAP LAYERS
のData Source
をMapbox Streets With Building Ids
にPOINTS OF INTEREST
内のAdd Layer
をクリックしてレイヤーを追加- 追加されたレイヤを選択して Prefab を適当なものに設定
Prefab Locations
のFind by
をAddress Or Lat Lon
にしてAdd Location
をクリックLocation 0
に35.619707, 139.7283495
を指定- 実行
大崎駅にピンをたたせることができました!
これを応用すればポケストップを表示する、なんてこともできそうですね。
建物を立体的に
3D地図っぽい感じに建物を立体的にのばしてみたいと思います。
MAP LAYERS
のData Source
をMapbox Streets With Building Ids
に(さっきのまま)FEATURES
のAdd Feature
をクリックしてMap Features
が追加Data Layer
をbuilding
に- 実行
デフォルトの設定でここまで表示できるのは良いですね。
建物の外観は Texturing
の Style Type
から変更可能です。例えば Style Type
を Color
にすると
こんな風に変えることができます。
Mapbox の簡単な使い方はおさらいできたので、今回はここまで。次はもう少し詳しくドキュメントを読んで遊んでみたいと思います。