Microsoft MVP for Mixed Realiyを受賞
2023-2024 Microsoft MVP for Mixed Realiyを受賞しました。
2008年から17年連続となります。
オフラインイベントなども増えてきましたし、オンライン中心からの再シフトなどもあるかもしれませんが、状況に応じて自分ができる範囲で無理せずコミュニティ活動を続けていけたらと思います。
今年は、.NET 6/7/8/9、.NET MAUI、AzureのAI関係を基軸にして、xRealやMeta Quest 3のアウトプットを増やしていけたらと思っています。
特にセンシングした結果をAzure OpenAI Serviceなどで情報負荷/変換し、HMDに表示するようなアプリ作成方法にフォーカスしていきたいと思います。
また、Thunkableをつかったローコード開発についても引き続き注目していきます。
Windows DevelopmentからMixed Realiyへとカテゴリ移動となりましたが、引き続きよろしくお願いいたします。
Thunkable でSNSを作ってみよう(変数編)
Thunkableには「App」「Stored」「Cloud」の3つの変数(variable)タイプがあります。
Thunkableで変数を使うときは、一部の例外を除いて、initializeブロックを使って変数の名前を宣言してあげる必要があります。
3つの変数タイプ
App Variable
App Variableは実行中だけ値を入れて置ける箱になります。アプリを終わらせて再起動したときには値はなくなっています。
それではどのような時にApp Variableを使うのでしょうか。
App Variableを使うときはこんなとき
- 途中の計算結果をいれておきたいとき
- 画面と画面の間で値を受け渡したいとき(例:画面Aで選択したデータの詳細を画面Bで表示したいときに「選択」したものをApp Variableに入れて渡す)
App Variableを使うときはこの2つの場合が大半です。あとは迷ったらまずはApp Variableとしておいて、それでは問題があるようなら、あとでStored VariableやCloud Variableに変更してみるという使い方もよい使い方です。
なぜ、まずはApp Variableなのか?
App Variableはアプリ実行中のメモリ上に箱があるため、他の2つの変数タイプに比べると圧倒的に出し入れが速いからです。この出し入れの速さは、アプリの動作速度に直結しますので、必要もないのにStored VariableやCloud Variableを使うよりも快適な動きになるからです。
App Variableの初期化方法


![]()
App Variableの初期化方法は、initializeブロックで初期値を指定して行います。
初期値が文字ならば文字型変数、数字ならば数字型変数、EmptyListならばリスト型変数として初期化されます。
Stored Variable
Stored Variableは、アプリごとに用意された端末(スマホ)の領域に箱を用意している変数です。
そのため、アプリを終わらせて再起動したとしても、値が残っています。
Stored Variableを使うときはこんなとき
- IDやパスワードなど、その端末(スマホ)に覚えさせておくことで、次回実行時に1から入力しなくて済ませたい
- ネット対戦のないゲームなどのスコアなどを記録しておく
Stored Variableは、アプリを再実行したときも前回実行時までの記録をとっておきたいようなときに使います。
なぜ、Stored Variableなのか?
例えば、すごく一生懸命入力していたものがアプリが万が一落ちてしまって消えたら悲しいですよね。
そういった場合、再実行すれば落ちる直前からやり直せるようにしていたらどうでしょうか。
App Variableではこのようなことができないので、こんなときはStored Variableを使うといいでしょう。
Stored Variableの初期化方法
Stored Variableの初期化方法もinitializeブロックですが、初期値は指定できません。
どうして初期値が指定できないといえば、initializeブロックで初期値を指定してしまうと、アプリを再実行したときに初期値に初期化されてしまって、せっかく残っていた前回終了時の値が消えてしまうからです。
それではどう初期化すればいいのでしょうか。
アプリ実行時の一番最初に開く画面のオープン時に「まだ初期化されていなかったら初期化する」というIfブロックを使って初期化します。

上のようにブロックを組むことによって
- Screen1がOpneするとき(When)
- もし(if)、Stored Variableのusersがnull(値が入ってない)ならば
- Stored VariableのusersにEmpty Listをいれてリスト型変数として初期化する
- もし(if)、Stored Variableのusersがnull(値が入ってない)ならば
のような動きが作れます。
こうすることによって、「本当の」初回起動時だけ初期化、それ以降は初期化せずに保存されている値を継続利用、というような動きが作れます。
通常の学習では、変数を習ってから WhenブロックやIfブロックを習うという順番で学習していくため、このStored Variableの初期化が最初の妻月になることが非常に多いようです。
なので、initialize、when、ifのこの3点セットは「こういったもの」としてStored Variableを使うときには必ず最初に塊として覚えておくといいでしょう。
Cloud Variable
Cloud Variableは、クラウド上の保存領域に箱が用意されている変数です。
Cloud Variableを使うときはこんなとき
Cloud Variableは端末(スマホ)ごとの領域ではなく、そのアプリを使っている利用者全体で1つの領域が使われます。
そのため、いろいろな利用者の間で情報を共有するようなSNSをつくるときには、SNS上に表示する内容にはCloud Variableを使うことになります。
なぜ、Cloud Variableなのか?
App VariableやStored Variableでは、同じアプリを使っていても変数が格納される場所が端末(スマホ)ごとになってしまうため、他の端末(他の人)からの投稿を自分がみたり、自分の投稿を他の端末(他の人)が見たりすることができません。
すなわり、他の人に見せたくないものはCloud Variableにいれたらダメなのですが、見せたいものは入れないといけないということになります。
Cloud Variableの初期化方法
Cloud Variableの初期化方法もinitializeブロックですが、初期値は指定できません。
どうして初期値が指定できないといえば、initializeブロックで初期値を指定してしまうと、誰かがアプリを実行するたびに初期値に初期化されてしまって、せっかく残っていた前回終了時の値が消えてしまうからです。
それではどう初期化すればいいのでしょうか。
アプリ実行時の一番最初に開く画面のオープン時に「まだ初期化されていなかったら初期化する」というIfブロックを使って初期化します。

上のようにブロックを組むことによって
- Screen1がOpneするとき(When)
- もし(if)、Cloud Variableのusersがnull(値が入ってない)ならば
- Cloud VariableのusersにEmpty Listをいれてリスト型変数として初期化する
- もし(if)、Cloud Variableのusersがnull(値が入ってない)ならば
のような動きが作れます。
こうすることによって、「どこかのだれか最初にアプリを使った人」の「本当の」初回起動時だけ初期化、それ以降は初期化せずに保存されている値を継続利用、というような動きが作れます。
Cloud Variableはどこに保存されるのでしょう
変数を格納できるクラウドサービスは様々なものがありますが、Cloud Variableの格納先はFireBaseとなります。
firebase.google.com
格納先のFireBaseを指定する
FireBaseに格納するといっても、FireBaseの「どこ」に?という疑問が湧いてきます。FireBaseを使うときには
- どこに:DatabaseURL
- どのアプリが:API Key
の2つを指定して、FireBaseの「どこ」に「どのアプリ」が読み書きするを紐付けます。
FireBaseプロジェクトの構成
表現がわかりづらいですが、アプリからFireBaseを使うための設定を行うことを「アプリを追加」とFireBaseでは呼びます。
Thunkableのアプリの種類「ウェブ」になります。
1. FireBaseにウェブアプリを追加

2. アプリの登録
アプリ名は何でもいいので「Thunkable」としておくといいでしょう。

3. Firebase SDK の追加
特に何もせず「コンソールに進む」をクリックします。
サインインの方法を追加

コンソールが表示されたらAuthenticationを選んで、FireBaseへのサインイン方法を設定します。
1. 始める

「始める」をクリックします。
2. 「メール/パスワード」プロバイダを選択します。

3. メールとパスワードで登録できるように「有効」にして「保存」をクリックします。


RealtimeDatabaseの作成

コンソールのRealtimeDatabaseをクリックします。
1. データベースを作成

「データベースを作成」をクリックします。
2. ロケーション設定

「米国」を選択して「次へ」をクリックします。
3. セキュリティルール

「ロックモード」を選んで「有効にする」をクリックします。
4. 公開する
ルールをクリックしてルールの中の「false」を「"auth != null"」に変更して「公開する」をクリックします。

これは「認証が通っているときのみ読み書きできる」という最低限のルールになるので、アプリに準じたルールに適時書き換える必要があります。
5. メール列挙保護を無効にする
thunkableから使っているとFirebaseにつながらなくなる時があります。
「The supplied auth credential is incorrect, malformed or has expired. (auth/invalid-credential)」というエラーがでるようであれば、メール列挙保護を無効にしてみてください。

でも、まずは、これでドンドン開発を進めてみてください。
API KeyとDatabaseURLを確認する
2. DatabaseURL

DataBaseURLはRealtime Databaseの「データ」のところに表示されます。
API KeyとDatabaseURLをThunkableのプロジェクトに紐付ける
FireBase側でAPI KeyとDatabaseURLが設定できたら、それをThunkableのプロジェクトに紐付けます。

Thunkableの「Design」で左側に表示される一番したの歯車マークのSettingsをクリックすると色々な設定を入れる場所がでてくるので、そこをスクロールしてFireBaseと表示されているところの[API Key]と[DatabaseURL]をみつけてください。
ここにFireBase側で表示されている「API Key」と「DatabaseURL」をコピーします。
ここまでのまとめ
- ThunkableのCloud VariableはFireBaseというクラウドデータベースに保存される
- FireBaseとThunkableアプリ(Thunkableプロジェクト)はAPI KeyとDatabaseURLの2つの値で紐付ける
- API Keyは、FirebaseのサイトでFirebaseプロジェクトを作成すると表示される
- DatabaseUrlは、Firebaseコンソールで、FireBaseプロジェクトを指定してRealtime Databaseを作成すると表示される
- アプリからFireBaseにサインインするためにAuthenticationでアプリからのサインイン方法を指定する
ユーザ登録(サインアップ)を実現するブロック
アプリからFirebaseにサインインするためには、まずは、Firebaseに対して、そのアプリのユーザ登録(サインアップ)を行わなければなりません。
ユーザ登録は2つのステップがあります。
- メールアドレスとパスワードの登録
- 登録したメールアドレスに届く認証メールのURLをクリックして、メールアドレスの有効性確認(なりすまし防止)
メールアドレスとパスワードを登録するためのブロック
Thunkableアプリで、Firebaseに【そのアプリの利用】ユーザ登録を行うためには「Firebase Sign Up」ブロックを使います。
「Firebase Sign Up」ブロックは、Blocksの左サイドメニューの[App Features]-[Sign In]グループの中にあります。

この「Firebase Sign U」ブロックですが、このまま使わずに、右クリックでメニューを表示して[Show Advanced Block]を選んで拡張表示ブロックにしてから使います。

拡張表示ブロックにすることで、FirebaseへSign Upしたときの処理結果を処理できるようになります。

IfブロックでErrorだったらラベルにエラーメッセージを表示し、
Errorではければ(else)つまり正常ならば "認証メールのURLをクリックしてサインアップを完了してください。"と表示するような処理を行います。
よくあるエラーと防止策
よくあるエラーとしてはパスワードの長さが短い場合で、そのようなときは次のようなエラーとなります。
Firebase: Password should be at least 6 characters (auth/weak-password).
このようなエラーを防ぐためには、パスワード欄に6文字以上入力された時だけサインアップボタンが押せるようにするとよいでしょう。

このほかによくあるエラーとしては、すでに登録済のメールアドレスが指定されたときです。そのときには次のようなエラーメッセージとなります。
Firebase: The email address is already in use by another account.
ユーザー登録と認証について

正常にサインアップできたら、入力したメールアドレスとパスワードがFirebaseのAuthenticationに登録され、認証メールが送信されます。
認証メールをクリックするまでは、メールアドレスは「有効」になっていないため、Firebaseにはユーザ登録できていますが、アプリの登録ユーザーとしては認証されていないので、Cloud Variableに値の出し入れはできません。
認証メール

認証メールが届いたらリンクをクリックします。正しく認証できれば、次のような表示がでます。

サインインを実現するブロック
Thunkableアプリで、Firebaseに【そのアプリの利用】ユーザのサインインを行うためには「Firebase Sign In」ブロックを使います。
「Firebase Sign In」ブロックは、Blocksの左サイドメニューの[App Features]-[Sign In]グループの中にあります。

メールアドレスとパスワードでサインインを実行して
- もし(If)、errorだったら
- Message_Labelにエラーメッセージを表示
- そうではなく(else)もし(if)、認証済(is email verified>)じゃない(not)なら
- "認証メールのURLをクリックしてサインアップを完了してください。"と表示
- それ以外(else) 、つまりメール認証も済んでいるのなら
- App Variable IDにFirebaseで認証された「user id」値を代入し、App Variable IDがnullかどうかで認証できているかが一目で分かるようにしておく
という処理を実施します。
サインインできたあとの具体的な動きの例
サインインできた後の動きはアプリごとに異なりますが、ここでは、名前などのプロファイルが登録されていれば「Main_Screen」、登録されていなければ「Profile_Screen」を表示する動きになるようにelseブロックの中を拡張してみましょう

App Varialble Mailの初期化
elseブロックの最初の命令は、プロファイルが登録されているかどうかの判定用のApp Variable LastNameの初期化です。
後々、nullだったらプロファイルなしという判断をしたいので、ここでnullを代入しています。
Cloud Variable Usersの初期化
elseブロックの次の命令は、もし(if)、Cloud Variable users自体がFirebase上に存在しない(=null)だったらというIfブロックです。
存在しない場合は、empty listを設定します。このブロックが実行されるとFireBaseのRealtime Database上に「users」というテーブルが作成されます。

usersテーブルからのプロファイルの取り出し
Cloud Variable usersが存在しない以外(else)の場合、つまり、存在する場合は、「cloud variable users」から1行づつ取り出してサインインした人のプロファイルデータを探します。

1行づつ取り出すのは、[for each item]ブロックとなります。取り出した行は変数userに格納されます。この「user」ですがブロック初期値は「j」となっていますがわかりやすくするために名前を変更しています。
もし(if)、変数userには複数の要素が含まれているので、要素(property)「ID」が「App Variable ID」ならば、その行がサインインした人のプロファイルになるので「App Variable LastName」を設定しています。
あとはプロファイルが見つかったので[Break out of loop]ブロックで[for each item]ブロックのループから抜けます。
### プロファイルのあるなしで次の画面を判断する
[for each item]ブロックを抜けたときに、「App Variable LastName」は2つの状態が考えられます。
プロファイルが最後まで見つからなかった時の「null」と、見つかったときにLastNameが入っている状態です。
そこで、もし(if)、「App Variable LastName」が「null」だったならば、[Navigate]ブロックで「Profile_Screen」画面に移動します。
それ以外(else)ならば、[Navigate]ブロックで「Main_Screen」画面に移動します。
Profile_Screen画面でのBlocks
それでは最後にProfile_ScreenでFireBaseのusersテーブルに値を設定する部分を紹介しましょう。

- IDとMailはサインイン画面でApp Variableに設定されているので、あとはプロファイル画面で「姓」「名」を入力したオブジェクト(1行分の要素の入ったもの)を[Create Object]ブロックで作成
- オブジェクトを[in list]ブロックを使って、「Cloud Variable Users」テーブルの最後(last)に追加(insert at)します。
[Create Object]ブロックは、Blocksの左サイドメニューの[core]-[Objects]の中にあります。
[in list]ブロックは、Blocksの左サイドメニューの[core]-[Lists]の中にあります。
Cloud Vairableへの「代入」がFireBaseへの格納
FireBaseへのサインインが成功している状態でのCloud Variableへの代入はになります。

ちょっとした工夫
名前を入力せずにプロファイルを登録できないように次のように入力欄チェックをいれておくとよいでしょう。

最後に
ThunkableでSNSを作るときに必要になってくる知識の中で「変数」に着目してみました。
FireBaseの設定の仕方から、ThunkableでFireBaseがどのようにみえるのか、FireBase側とThunkable側を対比することで少しだけイメージしやすくなったのではないでしょうか。
次回は、なぜ、Cloud Variable usersの要素をどうきめたのか、また、自分の投稿と他の人の投稿はどう区別がつくようにできるのかなど「テーブル要素」に注目してご紹介します。
hatsune.hatenablog.jp
Thunkableでノーコードアプリケーション開発を極めよう
Thunkableって知っていますか?
thunkable.com
Thunkableは、scratchのようなビジュアルプログラミングでモバイルアプリ開発ができるツールです。
ブラウザでプログラミングできるだけではなく、アプリ画面デザインやテスト動作などもブラウザで行うことができるため、Androidアプリ、iOSアプリ、Webアプリ(PWA)などが実機がなくても開発できてしまうというツールです。
Thunkableの特徴
Thunkableの特徴の一つは「Block」と呼ばれる部品を使ってロジックがかける点です。

基本的な使い方は、画面左端にあるCoreと書かれているところからBlockを選んで、それを組み合わせます。
ブロックの形には上に凹、下に凸があるブロックと、右に凹、左に凸があるブロックがあります。
上下の凹凸のブロックを縦に並べることでプログラムの流れが作れます。左右の凹凸は数式などに使うブロックになります。
Blockには次のようなCoreブロックと呼ばれる基本的なBlockの種類があります。
- Control
- Logic
- Math
- Text
- Lists
- Color
- Device
- Objects
- Variables
- Functions
そのほかにも、App Featuresと呼ばれるデバイス特有の機能を使うBlock、AdvancedとよばれるWeb APIやOpenAIを呼び出すのに便利なBlockがあります。

今回の一連の投稿では、1つ1つの機能を網羅的に説明するのではなく「〇〇するには」のような遣りたいことデザインパターンを色々紹介していきます。
Microsoft AI Tour Tokyoに参加して思ったこと
少し報告が遅くなってしまいましたが、2024年2月に開催された「Microsoft AI Tour Tokyo」について、参加した感想を書きます。
news.microsoft.com
一言でいえば「最高のMicrosoftグローバルイベント」でした。
まずゆるゆると入場する感じがいいし、入ったら軽食とコーヒーがあるのがBuildとかIgniteっぽいしと開場からセッションが始まるまでの時間がすばらしい。会場も通路というか導線が広くて、何よりも外の景色が見えるのがいい。
キーノートや各セッションもすばらしかったのですが、特筆すべきはハンズオンですね。
2回お手伝いさせていただいたのですが、時間内で動くところまでもっていくコンテンツの流れもよかったのですが、参加している人たちの真剣度が高く「このハンズオンで知識をものにする」という思いが会場に満ちていて、それがよい空気感をだしていて、すごく素敵な空間になっていました。
ハンズオンで立ち見という状況が生じていて、着席できた人も背後からの(立ったままノートPCを抱えてハンズオンしている立ち見勢の)勢いに圧されてというのもあったかも。
つまずいたり質問がある場合には挙手していただいて、手伝っている我々が駆けつけてトラブルシューティングをしていたのですが、そこで感じたのは、参加者の中には、明らかにマイクロソフトのツールが初めてという方もいらっしゃるけれど、「使ったことないから」としり込みするのではなく「使ったことがないからこそ」という前向きな気持ちが、手伝っているこちら側としても気持ちよくサポートできたので、手伝わせてもらってありがとうという気持ちになりました。
Meta Quest 3でビデオシースルーをしてみよう
MRTKを使ったMeta Quest 3でビデオシースルーで周りの風景がみえるMRアプリを作ってみましょう。
開発環境
Meta Quest 3アプリをMRTKで開発する場合の2024/01現在の環境は次のような環境になります。

- Unity 2022.3.18f1
- Mixed Reality Feature Tool 1.0.2209.0
- MRTK3
- MRTK Graphics Tools 0.6.7
- MRTK Core Definitions 3.0.1
- MRTK Input 3.0.0
- MRTK Spatial Manipulation 3.0.0
- MRTK Standard Assets 3.0.0
- MRTK UX Components 3.1.0
- MRTK UX Core Scripts 3.1.0
- Mixed Reality OpenXR Plugin 1.9.0
実装
- Unityで空のプロジェクトを新規作成
- Mixed Reality Feature ToolでMRTK3をインストール
- MRTK3の設定
- シーンの設定
- サンプルオブジェクトの配置
- ハンドメニューの追加
Unityで空のプロジェクトを新規作成
新規作成
Unity Hubを起動したら、[Projects]メニューから[New Project]をクリックして新規プロジェクトを作成します。
テンプレートとUnity Editorバージョンの指定
テンプレートとしては「3D」を選択し、プロジェクト名には「MetaSeeThroughWorld」とします。もちろんプロジェクト名は任意につけることができるので他の名前でもOKです。
もし、複数のUnity Editorをインストールしている場合は、利用するUnity Editorのバージョンが想定しているバージョンか確認して必要に応じてバージョンを選択しなおしてください。
Mixed Reality Feature ToolでMRTK3をインストール
ダウンロード
ダウンロードサイトから「MixedRealityFeatureTool.exe」をダウンロードします。インストーラーではなく実行ファイルそのもののダウンロードとなります。
www.microsoft.com
Mixed Reality Feature ToolでUnityプロジェクトフォルダを指定
Mixed Reality Feature Toolを起動したら、MRTK3を配置するUnityプロジェクトフォルダを指定して、[Discover Features]ボタンをクリックします。

導入機能の選択

今回導入する機能を次の機能になります。
- MRTK3
- MRTK Graphics Tools 0.6.7
- MRTK Core Definitions 3.0.1
- MRTK Input 3.0.0
- MRTK Spatial Manipulation 3.0.0
- MRTK Standard Assets 3.0.0
- MRTK UX Components 3.1.0
- MRTK UX Core Scripts 3.1.0
- Platform Support
- Mixed Reality OpenXR Plugin 1.9.0
導入する機能を選択して、[Get Features]をクリックしてUnityプロジェクトにMRTK3に含まれている機能から使用する機能を追加します。

選択した機能を確認したら[Import]ボタンで導入します。
MRTK3の設定
Unity Editor 起動
MRTK3導入後の初回起動時に、MRTK3が使用している「Unityの新しいInput System」を使用しているため、切り替え確認ダイアログが表示されるので、[YES]をクリックして先に進みます。

シーンの設定
それでは、「シーン」を設定してMRTK3が利用できるように設定します。
シーンのカメラ削除
シーンに初期設定されている「Main Camera」ではなく、MRTK3のカメラを使うので「Main Camera」は削除します。
[Hierarchy]タブの「Main Camera」を右クリックしてメニューから[Delete]を選択して削除します。
MRTK XR Rigの追加
[Project]タブの[Packages]-[MRTK Input]-[Assets]-[Prefabs]から「MRTK XR Rig」を[Hierarchy]タブへドラッグ&ドロップして追加します。

MRTK XR Rigの設定
「MRTK XR Rig」の初期設定値では、[Camera Y Offset]がY方向「1.6」mとなっています。
この「MRTK XR Rig」がアプリ上のカメラであり、Meta Quest 3を被った時の目の位置と目線の方向を表しますので、このオフセット値を0に設定します。また、[Position Y]も「0」、[Tracking Origin Mode]も「Device」とします。

- Position:0, 0, 0
- Tracking Origin Mode:Device
- Camera Y Offset:0
Input Simulatorの追加
Unity Editor上でデバッグするときに、Meta Quest 3の入力をキーボードでシミュレートするための機能「Input Simulator」を追加します。
[Project]タブの[Packages]-[MRTK Input]-[Simulation]-[Prefabs]から「MRTKInputSimulator」を[Hierarchy]タブへドラッグ&ドロップして追加します。

これでUnity Editor上でPlay Modeにすると[Shift]キーで左手、[Space]キーで右手の操作がシミュレートできるようになります。
learn.microsoft.com
サンプルオブジェクトの配置
共通設定が完了したらMRアプリのHello Worldともいえる空中に浮かんだCubeを表示するアプリを作成します。
Cubeの追加
[Hierarchy]タブを右クリックして、[3D Object]-[Cube]メニューをクリックしてCubeを追加します。
Cubeの位置と大きさを調整
初期位置ではカメラと重なってしまうので、[Hierarchy]タブで「Cube」を選択して、その[Inspector]タブで[Position]を(0, 0, 2)=2m先の位置、[Rotation]を(30, 30, 0)、[Scale]を(0.5, 0.5, 0.5)にして配置します。

Cubeへの物理演算追加
Cubeの[Inspector]タブで[Add Component]ボタンをクリックして、[Physics]-[Rigidbody]を選択して、Cubeに物理演算が適用されるように設定します。
また、初期状態では重力が聞いている状態なのでアプリを動かした途端にCubeが自由落下で下に落ちてしまうので、[Use Gravity]のチェックを外します。

Cubeへの操作追加
Cubeの[Inspector]タブで[Add Component]ボタンをクリックして、[MRTK]-[Special Manipulation]-[Object Manipulator]を選択して、CubeにMRTKによる操作が適用されるように設定します。
learn.microsoft.com
ハンドメニューの追加
MRTKにはHoloLens 2などで実装されているハンドメニューを実現する部品があります。
[Project]タブの[Packages]-[MRTK UX Components]-[HandMenu]から「HandMenuBase」を[Hierarchy]タブへドラッグ&ドロップして追加します。

[TMP Importer]ダイアログが表示された場合は、[Import TMP Essentials]ボタンだけクリックして、TextMeshProのリソースのみをインポートしてください。

初期状態の確認
「HandMenuBase」を[Hierarchy]タブに配置出来たら、[Inspector]タブ[Position Z]を0.5に変更してカメラの前に配置して、Playボタンで実行してみましょう。
youtu.be
このように縦に4つのボタンが表示され、左手または右手の操作をシミュレートしてあげればメニューを選択する動きが再現できます。
なお、初期状態では、顔から少し離れた位置で手をかなり平らな状態で視線に対して75度以上の角度でカメラの前(視線の前)に持ってくるような動きが必要です。
一番下のボタンに「終了」を割り当てる
[Hierarchy]タブの「HandMenuBase」の中をクリックしていって、「Action Button (4)」の[Frontplate]-[AnimatedContent]-[Icon]-[UIButtonFontIcon]を選択します。
[Inspector]タブで「Front Icon Selector」コンポーネントを探して、その中から「アプリケーションの終了」を意味するアイコンを選択します。

アプリケーション終了の実装
C#コード
[Project]タブの[Assets]を右クリックして[Create]-[Folder]メニューを選択して、[Assets]の中に新しいフォルダを作成します。フォルダ名は「Scripts」とします。
[Scripts]を右クリックして[Create]-[C# Script]メニューを選択して、C#のコードを記入するファイルを作成します。ファイル名は「Menu」とします。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Menu : MonoBehaviour
{
public void ApplicationExit()
{
Application.Quit();
}
}
C#コードをHandMenuBaseに追加
[Hierarchy]タブの「HandMenuBase」を選択し、[Inspector]タブの[Add Component]ボタンで[Scripts]-[Menu]を選択します。
Action ButtonのClickイベントに「ApplicationExit」メソッドを割り当て
[Hierarchy]タブの「HandMenuBase」の中をクリックしていって、「Action Button (4)」を選択します。
[Inspector]タブで[Pressable Button]の[On Clicked()」で、「HandMenuBase」を選択してから、「No Function」と表示されているドロップダウンリストをクリックして[Menu]-[ApplicationExit()]を選択します。

Unity EditorのPlay Modeでハンドメニューを表示する
[Shift]を押して左手シミュレートを表示したら[P]キーを押ししてから[F]キーを押して手のひらを返すとハンドメニューが表示できます。
Meta Quest 3 固有機能の実装
- Meta Quest 3用必要アセットのインポート
- ターゲットプラットフォーム指定
- Player設定
- Quality設定
- MRTKプロファイル作成
- XR Plugin Management (Open XR) の設定
- ビデオシースルー設定
- 壁や床を検出する
Meta Quest 3用必要アセットのインポート
アセット追加
Meta Quest 3に合わせたアプリにするためには、Meta XR SDKをプロジェクトに追加する必要があります。
そのために、まずはUnityのアセットストアから必要なアセットに対して[Add to My Assets]をクリックしてUnity Editorで参照できるようにします。
assetstore.unity.com
今回使用するMeta XR SDKは次のものになります。
インポート
My Assetsに追加出来たらUnity Editorでインポートします。
そのためには、Unity Editorで[Window]-[Package Manager]メニューをクリックして、Package Mangerを表示します。
[Packages]を「My Assets」に変更して3つのSDKをインポートします。

Meta XR Feature Setは有効にしない
MRTK3とMeta XR SDKを併用した場合、次のようなダイアログが表示されます。

必ず[Cancel]ボタンをクリックしてください。
Validationの解消
インポートが終わったら、[Edit]-[Project Settings]メニューでリストから[Oculus]を選択します。
[Checklist]の各項目の[Fix]ボタンをクリックします。

最終的に次の2つ以外が解消されればOKです。
- Oculus must be added to the XR Plugin active loaders
- Unity's OpenXR Plugin is not recommended when using the Oculus SDK, please use Oculus XR Plug-in instead
ターゲットプラットフォーム指定
Unity Editorで[File]-[Build Setting]で、Platfromで「Android」を選択してから、[Switch Platform]をクリックしてターゲットプラットフォームを切り替えます。

Player設定
[Edit]-[Project Setting]メニューで[Player]を選択します。
Meta Quest 3用に設定を整えます。

- [Other Settings]-[Rendering]-[Color Space]:Linear
- [Other Settings]-[Rendering]-[Auto Graphics API]:チェックを外す
- [Other Settings]-[Rendering]-[Multithreaded Rendering]:チェック
- [Other Settings]-[Identification]-[Minimum API Level]:Android 10.0 Marshmallow (API level 29)
- [Other Settings]-[Identification]-[Target API Level]:Automatic (highest installed)
- [Other Settings]-[Configuration]-[Install Location]:Automatic
Quality設定
[Edit]-[Project Setting]メニューで[Quality]を選択します。

- [Rendering]-[Pixel Light Count]:1
- [Rendering]-[Anti Aliasing]:4x Multi Sampling
- [Textures]-[Global Mipmp Limit]:0:Full Resolution
- [Texture]-[Anisotropic Textures]:Per Texture
- [Particles]-[Soft Particles]:チェックを外す
- [Terrain]-[Billboards Face Camera Position]:チェック
MRTKプロファイル作成
Unity Editorで[Edit]-[Project Settings]メニューで[MRTK3]を選択します。
[Android settings]タブを選択して[Assign MRTK Default]ボタンをクリックします。

警告マークがでますが[Profile]欄が「MRTKProfile (MRTK Profile)」となっていればOKです。

XR Plugin Management (Open XR) の設定
インストール
Unity Editorで[Edit]-[Project Settings]メニューで[XR Plug-in Mangement]を選択します。
[Android Settings]タブを選択して[OpenXR]と[OpenXR]-[Meta XR feature group]をチェックします。

OpenXRの設定
[Edit]-[Project Settings]メニューで[XR Plug-in Mangement]-[OpenXR]を選択して、OpenXRの設定を行います。

- Enabled Interaction Profile:List is Empty
- Hand Tracking:チェック
- Meta Quest Support:チェックを外す
- Motion Controller Model:チェック
Validationの解消
[Edit]-[Project Settings]メニューで[XR Plug-in Mangement]-[Project Validation]を選択して、エラーや警告がないかを確認します。
エラーや警告の行で[Fix]ボタンが表示されているものは、順次クリックして解消します。

ただし、現状は次の2つについては解消できないので、この2つの[Fix]は無視します。
- [Packages] Unity's OpenXR Plugin is not recommended when using the Oculus SDK, please us Oculus XR Plug-in insted
- [Compatibility] Build Target (Unknown) is not supported
ビデオシースルー設定
Main Camera設定
[Hierarchy]タブで[MRTK XR Rig]-[Camera Offset]-[Main Camera]を選択し、[Inspector]タブの[Camera Setting Manager]-[Opaque Display]を次のように設定します。
- Clear Mode: Solid Color
- Clear Color: 0, 0, 0, 0
[Transparent Display]についても同様に設定します。

SeeThroughオブジェクトの追加
- [Hierarchy]タブの任意の場所を右クリックして[Create Empty]メニューを選択して、空のGameObjectを追加します。
- 追加したオブジェクトの名前を「SeeThrough」にします。
- 「SeeThrough」オブジェクトを選択し、[Inspector]タブで[Add Component]ボタンをクリックして、[Scripts]から「OVR Manager」「OVR Passthrough Layer」を追加します。
OVR Managerの設定
[Target Devices]-[Quest 3]をチェック、[Insight Passthrough]-[Enable Passthrough]をチェックします。

![]()
[Insight Passthrough]セクションが変更不可の場合は、[Edit]-[Project Settings]-[Oculus]の[CheckList]でエラーが表示されていないかを確認して、エラーを[Fix]してから再度試してみてください。
OVR Passthrough Layerの設定
[Placement]を「Underlay」、[Opacity]を「1」にします。

途中実行
ここまでの設定でビデオシースルーモードでの実機実行が可能です。
試しに、Meta Quest 3をUSB接続して[File]-[Build And Run]メニューでアプリを実機転送して実行してみましょう。
Cubeをつかんで投げることができました。
www.youtube.com
重力加速度を適用していないので無重力状態でどこまでも飛んでいきます。
壁や床を検出する
Meta Quest 3ではシーン(Unityのシーンとは別のもの)という機能で、実際の空間をスキャンして壁や床を検出して仮想的なルームを作成することができます。いわゆる空間マッピングと呼ばれるものです。
検出した壁や床などに対してColliderを設定するこることでUnity内のオブジェクトが壁や床と相互作用ができるようになります。
先ほどまでは、Cubeを投げるとずっと飛んで行ってしまいましたが、相互作用ができれば壁や床で跳ね返るような動きが実現できます。
空間マッピングオブジェクトの追加
- [Hierarchy]タブの任意の場所を右クリックして[Create Empty]メニューを選択して、空のGameObjectを追加します。
- 追加したオブジェクトの名前を「OVRSceneManager」にします。
- 「OVRSceneManager」オブジェクトを選択し、[Inspector]タブで[Add Component]ボタンをクリックして、[Scripts]から「OVR Scene Manager」「OVR Scene Model Loader」を追加します。
「OVR Scene Manager」で管理する壁や床のオブジェクトは[Plane Prefab]およぶ[Volume Prefab]に設定します。単なるオブジェクトではなく「Prefab」形式で指定します。
Plane Prefabの作成

- [Project]タブの[Assets]-[Scenes]を左クリックし、[Create]-[Prefab]で新しいPrefabを追加します。
- 追加したPrefabの名前を「OVRScenePlane」にします。
- 「OVRScenePlane」Prefabを選択し、[Inspector]タブで[Add Component]ボタンをクリックして、[Scripts]から「OVR Scene Anchor」「OVR Scene Plane Mesh Filter」の2つを追加します。
- 「OVRScenePlane」Prefabを選択し、[Inspector]タブで[Add Component]ボタンをクリックして、[Mesh]から「Mesh Filter」「Mesh Renderer」の2つを追加します。
- 「OVRScenePlane」Prefabをダブルクリックして、[Hierarchy]タブに「OVRScenePlane」Prefabを表示します。
- [Hierarchy]タブの任意の場所を右クリックして[Create Empty]メニューを選択して、「OVRScenePlane」Prefabに空のGameObjectを追加します。
- 追加したGameObjectの名前を「Collider」にします。
- 「Collider」を選択し、[Inspector]タブで[Add Component]ボタンをクリックして、[Physics]から「Box Collider」を追加します。
- Box ColliderのSizeを(1, 1, 0.01)にします。
Volume Prefabの作成
- [Project]タブの[Assets]-[Scenes]を左クリックし、[Create]-[Prefab]で新しいPrefabを追加します。
- 追加したPrefabの名前を「OVRSceneVolume」にします。
- 「OVRSceneVolume」Prefabを選択し、[Inspector]タブで[Add Component]ボタンをクリックして、[Scripts]から「OVR Scene Anchor」「OVR Scene Plane Mesh Filter」の2つを追加します。
- 「OVRSceneVolume」Prefabを選択し、[Inspector]タブで[Add Component]ボタンをクリックして、[Mesh]から「Mesh Filter」「Mesh Renderer」の2つを追加します。
- 「OVRSceneVolume」Prefabをダブルクリックして、[Hierarchy]タブに「OVRSceneVolume」Prefabを表示します。
- [Hierarchy]タブの任意の場所を右クリックして[Create Empty]メニューを選択して、「OVRSceneVolume」Prefabに空のGameObjectを追加します。
- 追加したGameObjectの名前を「Collider」にします。
- 「Collider」を選択し、[Inspector]タブで[Add Component]ボタンをクリックして、[Physics]から「Box Collider」を追加します。
- [Hierarchy]タブの任意の場所を右クリックして[Create Empty]メニューを選択して、「OVRSceneVolume」Prefabに空のGameObjectを追加します。
- 追加したGameObjectの名前を「Mesh」にします。
- 「Mesh」を選択し、[Inspector]タブで[Add Component]ボタンをクリックして、[Mesh]から「Mesh Filter」「Mesh Renderer」の2つを追加します。
- [Hierarchy]タブの任意の場所を右クリックして[Create Empty]メニューを選択して、「OVRSceneVolume」Prefabに空のGameObjectを追加します。

Prefabの設定
2つのPrefabの準備ができたならば、[Hierarchy]タブで「OVRSceneManager」オブジェクトを選択し、[Inspector]タブで[Plane Prefab]およぶ[Volume Prefab]に設定します。
完成品動作
部屋の壁や床を認識して、無重力状態のCubeが部屋の中から飛び出していかない設定が以上で完成しました。
実機に転送して動作を確認してみましょう。
www.youtube.com
まとめ
本エントリの前半の「実装」部分は、MRTK3でアプリを作成するときの「機種依存しない」部分になります。
「Meta Quest 3 固有機能の実装」部分で、Meta Quest 3固有の設定、ビデオシースルー、空間認識を実装しています。この3点は、デバイスが異なる場合も必要な作業は同じで実現手順が異なりますので、HoloLensやXreal Airなどの実装もそのうちご紹介したいと思います。
次回は、現実空間と仮想オブジェクトのオクルージョンなども確認していきたいと思います。
雑感
Meta Quest 3も壁や床の認識過程を見ると空間スキャンした結果をメッシュ構造で取得しているようですが、最終的には「ルーム」という四角い部屋のような面で当たり判定をしています。
HoloLensのようにメッシュそのものをつかってくれればいいのですが、もし、実現するのであれば、実際の部屋の少し外側になるようにルームを設定し、その中でメッシュ状に認識している空間スキャン結果を自前でオブジェクトを張り付けるようなそんなコードが必要なのかもしれません。
でも、ルームという考え方は空間スキャン結果のオブジェクト数を最小(壁4平面+床と天井の2平面=6平面)におさえることができて、結果的に快適なMR空間を演出できる手法ではあります。
#mathsolver で数式入力に便利なルートキー!使い方を詳しく解説します
mathsolverで次のようにルートが入った式を入力したいときはどうすればよいでしょうか。
![]()
キーボードには √ (ルート) 記号のキーはありません。
安心してください。ルートを入力したいときは、次のようなステップで入力が可能です。
mathsolverで「1/100」とキー入力すると次のように表示されます。

次に、表示の100を選択します。

それから、入力欄の右端にあるキーボードマークをクリックします。
![]()

そして、表示されたキーボードのルートキーを押します。
![]()
すると選択していた「100」にルートが付きます。








