MRが楽しい

MRやVRについて学習したことを書き残す

Unityでスクリプトから作成済みのレイヤー一覧を取得する

本日はUnityの小ネタ枠です。
Unityでスクリプトから作成済みのレイヤー一覧を取得する方法についてです。

LayerMask.LayerToName

レイヤー番号からレイヤー名を取得します。
レイヤー名の設定がないレイヤー番号を指定すると空文字が返ります。
本関数を使って0番~31番のレイヤーがAdd Layerで設定済みのレイヤーか否かを判定できます。
docs.unity3d.com

サンプルスクリプト

現在設定済みの全てのレイヤーを走査してログに出力する以下のサンプルスクリプトを作成しました。
・LayerCheckTest.cs

using System.Collections.Generic;
using UnityEngine;

public class LayerCheckTest : MonoBehaviour
{
    private class LayerData
    {
        public string name;
        public int layer;
    }
    
    [ContextMenu("CheckLayerTest")]
    public void CheckLayerTest()
    {
        // レイヤー名とレイヤー番号の対応を確認する
        List<LayerData> layerDataList = new List<LayerData>();
        
        for (int layer = 0; layer < 32; layer++)
        {
            // 0~31のレイヤー番号からレイヤーの名称を取得する
            string layerName = LayerMask.LayerToName(layer);
            if (string.IsNullOrEmpty(layerName))
            {
                // 名称が設定されていないレイヤー番号はスキップする
                continue;
            }
            
            // 名称が取得できた場合は設定済みのレイヤーとしてリストに追加する
            LayerData layerData = new LayerData { name = layerName, layer = layer };
            layerDataList.Add(layerData);
        }
        
        // 設定済みレイヤー名とレイヤー番号の対応をログ出力する
        foreach (var layerData in layerDataList)
        {
            Debug.Log($"name: {layerData.name}, layer: {layerData.layer}");
        }
    }
}

以下のレイヤー設定を行ったUnityプロジェクトでサンプルスクリプトを実行してみます。

設定済みのレイヤー番号と名称を取得することができました。

Unityでレイヤー名とレイヤー番号の関連をスクリプトから取得する

本日はUnityの小ネタ枠です。
Unityでレイヤー名とレイヤー番号の関連をスクリプトから取得する方法です。

LayerMask

レイヤー名とレイヤー番号の関連はLayerMaskクラスのLayerToName関数またはNameToLayer関数を使って参照できます。
docs.unity3d.com

[Layers]プルダウンの[Edit Layers]から現在のレイヤー名とレイヤー番号の設定を確認できます。


LayerMask.LayerToName

レイヤー番号からレイヤー名を取得します。
レイヤー名の設定がないレイヤー番号を指定すると空文字が返ります。
docs.unity3d.com

LayerMask.NameToLayer

レイヤー名からレイヤー番号を取得します。
存在しないレイヤー名を指定すると-1が返ります。
docs.unity3d.com

サンプルスクリプト

それぞれの関数を実際に実行する以下のサンプルスクリプトを作成しました。
・LayerTest.cs

using UnityEngine;

public class LayerTest : MonoBehaviour
{
    [ContextMenu("NameToLayerTest")]
    public void NameToLayerTest()
    {
        // レイヤー名からレイヤー番号を取得する
        var layerName = "Ignore Raycast";
        var layerMask = LayerMask.NameToLayer(layerName);
        Debug.Log($"{layerName} is {layerMask}");
        
        // 存在しないレイヤー名の場合は-1が返る
        var invalidLayerName = "Invalid Layer";
        var invalidLayerMask = LayerMask.NameToLayer(invalidLayerName);
        Debug.Log($"{invalidLayerName} is {invalidLayerMask}");
    }
    
    [ContextMenu("LayerToNameTest")]
    public void LayerToNameTest()
    {
        // レイヤー番号からレイヤー名を取得する
        var layerMask = 2;
        var layerName = LayerMask.LayerToName(layerMask);
        Debug.Log($"{layerMask} is {layerName}");
        
        // 存在しないレイヤー番号の場合はエラーログが出力されて空文字が返る
        var invalidLayerMask = -1;
        var invalidLayerName = LayerMask.LayerToName(invalidLayerMask);
        Debug.Log($"{invalidLayerMask} is {invalidLayerName}");
        
        // 名称が設定されていないレイヤー番号の場合は空文字が返るのみ
        var emptyLayerMask = 31;
        var emptyLayerName = LayerMask.LayerToName(emptyLayerMask);
        Debug.Log($"{emptyLayerMask} is {emptyLayerName}");
    }
}


Unityでスクリプトからオブジェクトのレイヤーを変更する

本日はUnityの小ネタ枠です。
Unityでスクリプトからオブジェクトのレイヤーを変更する方法です。

GameObject.layer

ゲームオブジェクトが存在するレイヤーを参照・設定できます。
0 から 31 までの値が設定可能です。
docs.unity3d.com

サンプルスクリプト

自身の子オブジェクトを全て指定名のレイヤーに切り替える以下のサンプルスクリプトを作成しました。
・LayerSetTest.cs

using UnityEngine;

public class LayerSetTest : MonoBehaviour
{
    [SerializeField]
    private string _layerName = default;
    
    [ContextMenu("SetLayerTest")]
    public void SetLayerTest()
    {
        // レイヤー名からレイヤー番号を取得する
        int layer = LayerMask.NameToLayer(_layerName);
        if (layer == -1)
        {
            Debug.LogError($"Layer name {_layerName} is not found.");
            return;
        }
        
        // 全ての子オブジェクトのレイヤーを切り替える
        RecursiveSetLayer(gameObject, layer);
    }
    
    private void RecursiveSetLayer(GameObject targetObject, int layer)
    {
        // 対象オブジェクトの子オブジェクトをチェックする
        foreach (Transform child in targetObject.transform)
        {
            // 子オブジェクトのレイヤーを切り替える
            GameObject childObject = child.gameObject;
            childObject.layer = layer;

            // 再帰的に全ての子オブジェクトを処理する
            RecursiveSetLayer(childObject, layer);
        }
    }
}

以下の通りDefaultレイヤーの子オブジェクトを持つオブジェクトにサンプルスクリプトを設定します。

SetLayerTest関数を呼び出すと、以下の通り全てのレイヤーが切り替わりました。

ライブラリで取り込んだAndroidManifestが衝突した場合の対処

本日はMetaQuest3の小ネタ枠です。
ライブラリで取り込んだAndroidManifestが衝突した場合の対処についてです。

発生事象

Quest3向けのUnityプロジェクトに幾つかのライブラリを取り込んだ際、ビルド時にAndroidManifestの衝突エラーが発生しました。

[:InteractionSdk:] C:\Users\User\.gradle\caches\transforms-3\6c06\transformed\InteractionSdk\AndroidManifest.xml Warning:
	Package name 'com.oculus.Integration' used in: :InteractionSdk:, :SDKTelemetry:, :OVRPlugin:.

See http://g.co/androidstudio/manifest-merger for more information about the manifest merger.

84 actionable tasks: 6 executed, 78 up-to-date

UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)

Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
C:\Work\UnityProject\Library\Bee\Android\Prj\IL2CPP\Gradle\launcher\src\main\AndroidManifest.xml:49:9-36 Error:
	Attribute application@allowBackup value=(false) from [:unityLibrary] AndroidManifest.xml:49:9-36
	is also present at [:sqlite-android:] AndroidManifest.xml:12:9-35 value=(true).
	Suggestion: add 'tools:replace="android:allowBackup"' to <application> element at AndroidManifest.xml:3:3-83 to override.

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':launcher:processReleaseMainManifest'.
> Manifest merger failed with multiple errors, see logs

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 12s

UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)

CommandInvokationFailure: Gradle build failed. 
C:\Program Files\Unity\2022.3.16f1\Editor\Data\PlaybackEngines\AndroidPlayer\OpenJDK\bin\java.exe -classpath "C:\Program Files\Unity\2022.3.16f1\Editor\Data\PlaybackEngines\AndroidPlayer\Tools\gradle\lib\gradle-launcher-7.2.jar" org.gradle.launcher.GradleMain "-Dorg.gradle.jvmargs=-Xmx4096m" "assembleRelease"

原因

元のプロジェクトと取り込んだライブラリの両方にAndroidManifestが含まれており、application@allowBackupの設定がそれぞれtrueとfalseに設定されていたため競合が発生したことが原因でした。

対処方法

元のプロジェクトのAndroidManifest.xmlにtools:replace="android:allowBackupの設定を追加することで回避できます。
この場合、元のプロジェクトのAndroidManifest.xmlに書かれたapplication@allowBackupの設定が優先されるようになります。
・AndroidManifest.xml

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:installLocation="auto">
  <application android:label="@string/app_name" android:icon="@mipmap/app_icon" android:allowBackup="false" tools:replace="android:allowBackup">
(以下略)

MRTKv2.xを使ってMetaQuest3向けのUnityプロジェクト作成を行う その22(アプリの境界線を無効化する)

本日はMetaQuest3の技術調査枠です。
MRTKv2.xを使ってMetaQuest3向けのUnityプロジェクト作成を行う手順を記事にします。
本記事はアプリの境界線を無効化する手順です。

境界線の無効化

AndroidManifest.xmlの権限設定を行うことで、境界線を無効化したアプリを作成することができます。
境界線を無効化する場合はユーザーにとって安全なアプリを作成するための考慮を行う必要があります。
ユーザーに対する安全上のリスクが発生しないようにするのは開発者側の責任であると言及されている点に留意してください。
・Boundaryless | Oculus Developers
https://developer.oculus.com/documentation/unity/unity-boundaryless/
・Boundaryless and Contextual-Boundaryless Safety Best Practices | Oculus Developers
https://developer.oculus.com/resources/boundaryless-best-practices/

前提条件

以下の記事で作成した Unity プロジェクトを基に設定を行います。
記事その1~その4までの作業を実施済みであることが前提になります。
bluebirdofoz.hatenablog.com

Meta XR Pluginのバージョン

境界線の無効化はv66から利用可能です。
メニューから[Window -> Package Manager]を開き、Meta XR Pluginのバージョンをv66にアップデートしておきます。

パススルーを有効にする

境界線の無効を利用する際はユーザが周囲の状況を確認できるようにパススルーを有効にします。
以下の記事を参考にパススルーの設定を実施してください。
bluebirdofoz.hatenablog.com

AndroidManifest.xmlへの権限設定

プロジェクトのアセットフォルダにあるAndroidManifest.xmlを開きます。

Assets/Plugins/Android/AndroidManifest.xml

AndroidManifest.xmlに以下の機能権限を追加します。

<uses-feature
    android:name="com.oculus.feature.BOUNDARYLESS_APP"
    android:required="true"/>


ビルドと動作確認

本設定で境界線が無効になります。
以下の記事を参考にプロジェクトのビルドとQuest3へのデプロイを実行してください。
bluebirdofoz.hatenablog.com

Quest3でデプロイしたアプリを起動して空間を歩き回ります。
ルーム空間を設定していない場所でも境界線が表示されず、アプリを利用することができました。

MetaQuestで境界線を無効化する

本日はMetaQuestの小ネタ枠です。
MetaQuestで境界線を無効化する方法について記事にします。

ホーム画面で境界線を無効化する

Questのホーム画面で境界線を無効化するには[クイック設定]を開きます。
[パススルー]を[オン]にしているとき、[境界線]を[オフ]にできます。

境界線をオフにしている場合、静止モードや歩行モードの境界線を設定していなくても自由に移動が可能です。
なお[パススルー]を[オフ]にしているときは[境界線]は[静止モード]または[歩行モード]で設定する必要があります。

[境界線]はアプリ起動中は有効になります。
[境界線]を[オフ]に設定してパススルーが有効なアプリを起動しても境界線による警告は表示されます。

開発者モードで境界線を無効化する

開発者モードで境界線を無効化すると、アプリを含めた全ての境界線が無効化されます。
メニューから[クイック設定 -> 設定]を開きます。

設定一覧が表示されるので[システム]を選択します。

[開発]タブを開き、[カスタム設定を有効にする]にチェックを入れます。
[開発者設定]を変更できるようになるので[物理的空間機能]のチェックを外します。

確認ダイアログが表示されるので[同意する]にチェックを入れます。

これで境界線が無効化されます。本設定が有効なとき、ホーム画面のパススルーはオフに固定されます。
パススルーが有効なアプリを起動するとパススルーで表示され、境界線を無視して実行することができます。

アプリで境界線を無効化する

アプリの設定として境界線の無効を実装したい場合はAndroidManifest.xmlに権限を設定します。
詳細は以下の記事を参照ください。
bluebirdofoz.hatenablog.com

MetaQuestでデバイスの言語設定を変更する

本日はMetaQuestの小ネタ枠です。
MetaQuestでデバイスの言語設定を変更する方法について記事にします。

バイスの言語設定を変更する

メニューから[クイック設定 -> 設定]を開きます。

設定一覧が表示されるので[システム]を選択します。

[言語と地域]タブから言語設定を変更できます。
[言語]プルダウンから変更する言語を選択します。

言語設定の変更には再起動が必要になります。

Questが再起動すると以下の通り、デバイスの言語設定が切り替わります。