PicoIntegration 更新の際にアプリがクラッシュする現象の対応Tips
稀にPIcoIntegrationのバージョン更新時にアプリのビルドはできるが起動するとPXR系の多重起動が原因としてクラッシュする事がある 大抵は PXR_Enterprise.InitEnterpriseService() を呼び出した直後など
以下の対応を行うと解決するかもしれない
1. PicoIntegration の削除
古いバージョンのPicoIntegration周りのなにかが悪さしてる可能性があるのでUPMからPicoIntegrationを削除
また、PicoIntegrationをインポートした際にResourcesフォルダ以下に自動生成されるPXR系の設定ファイルなどを削除
2.キャッシュ削除
削除対象フォルダ及びファイル
| フォルダ or ファイル | 概要 | パス例 |
|---|---|---|
| Library | Unityのビルドキャッシュ・メタデータ | <ProjectRoot>/Library |
| Temp | 一時ビルドファイル | |
| obj | スクリプトの中間コンパイルキャッシュ | <ProjectRoot>/obj |
| .gradle | プロジェクト単位のGradleキャッシュ | <ProjectRoot>/.gradle |
| ~/.gradle/caches | グローバルGradleキャッシュ | Windows : C/Users/<User>/.gradle/caches macOS : ~/.gradle/caches |
| Assets/Plugins/Android 内の .aar/ .jar | 手動配置された依存ファイル 念の為 ( EDM4Uなどで解消している場合はバックアップ等は不要 ) |
<ProjectRoot>/Assets/Plugins/Android/ |
| packages-lock.json | 依存関係解決の結果 | <ProjectRoot>/Packages/packages-lock.json |
| PXR_ManagerをアタッチしてるPrefabがあればデタッチ | Picoのカメラ周り制御してるやつ | 任意のプレハブ |
3.手順おさらい
- Unityを終了
- 上記フォルダ or ファイルを削除
- Unityを再起動
- External Dependency Manager で .aar, .jar を再インポート ( 必要な場合は )
- Pico Integrationをインポート
PXR_Manager をデタッチしたPrefabがあればアタッチし直す - 新規クリーンビルド
PICO4 Enterprise で EyeTracking 使用時、瞳孔系情報を正常に取得できない不具合が発生したときに確認する項目
不具合の内容
EyeTracking を有効にしたプロジェクトで目の開閉や注視ポイントの座標値が正常に取得できているが瞳孔の情報が全て0になっている

環境
Unity : 2021.3.2f1
Pico Integration : 2.5.3
ハードウェア : PICO4 Enterprise
確認する事 : ハードウェア側
アイトラッキングの設定が有効になっているかを確認

確認する事 : ソフトウェア側
コードを実装していることは前提として以下の項目を確認する
PXR_Manager の EyeTracking にチェックが入っている ( EyeTrackingCalibration のチェックは任意 )

リファレンスを確認すると
<meta-data android:name="pvr.app.et_tob_advance" android:value="true"/>
を AndroidManifest.xml に追加しろとあるので追加する

https://developer.picoxr.com/reference/unity/client-api/PXR_MotionTracking/?v=2.5.0#GetEyePupilInfo
ここが詰まったポイントで <activity> タグの 外に記述 しないと正常に動作しなかった

動作確認
正常に瞳孔情報を取得できるのを確認

どうやらこの手のセンサー系は meta-data のスコープをアプリケーション全体として有効化しないといけないっぽい?
アプリ マニフェストの概要 | App architecture | Android Developers
https://developer.android.com/guide/topics/manifest/meta-data-element?hl=ja
また、正常に AndroidManifest.xml に追加できたかの確認は↓の方法で簡単にできる
備忘録 : 出力した .apk ファイルの中にある AndroidManifest.xml を確認する方法 - TechnicalProtein
備忘録 : 出力した .apk ファイルの中にある AndroidManifest.xml を確認する方法
Unity で Android 向けにビルドする際に Assets > Plugins > Android > AndroidManifest.xml に追加したパーミッションが謎に適用されてなさそうな挙動に遭遇
原因の調査で UnityProject > Temp > StagingArea にあるはずの AndroidManifest.xml が無かった時の対処
やり方
- AndroidStudio を起動し、EditorWindow に .apk ファイルを D&D
- AndroidManifest.xml を探す ( 直下にあるはず )
- AndroidManifest.xml をダブルクリックし、内容を確認

何か面倒くさいツールとか使うのかと思ったら思った以上に簡単に見られた
参考ページ
VSCode + GitHub Copilot でGHE.comへのログインが繰り返される時の対処
個人的な備忘録
前書き
普段はRiderで開発しているが こちら の記事を基に Cline で GitHub Copilot を使用してみようと思い 数十年ぶりに VSCode を開いて設定したときに詰まったのでメモ
何が起きた
既にGitHub Copilotの有料プランの契約をしていたので後は VSCode のプラグインをいくつか入れればいいだけだろとポチポチしてたがGitHubの個人アカウントへのサインインがうまくいかなかった
具体的には下記の画像の通り

サインインする際に GHE.com の方か GitHub の個人アカウントを選択する項目があるので個人アカウントを選択したが必ず GHE の方でサインインを試行して失敗する... を繰り返した
対応
色々とググったりしても特に改善しなかったがふとGitHub Copilotの設定を見ていた時 setting.json で編集の項目を見て、もしかしたら設定周りで何か変な設定になってないか?と思い開いてみたら...

authProvider の項目が github-enterprise になっていた、これを github に変更したら何の問題もなくサインインの画面に飛びGitHub Copilotを利用することが出来るようになった

もしGitHubの個人アカウントでサインインしたいのにGHEの方が呼び出される同士がいたら参考にしてみるのもいいかも
InputSystem を用いたテストをRunAllした際にテストが失敗する対策
概要
TestRunner にて InputSystem を用いた入力処理のテストを InputTestFixture を継承したテストクラスを用いて行った際 個別実行したときには全てのテストが通過するにもかかわらず RunAll で実行すると最初のテスト以外すべて失敗するという現象にぶちあったのでメモ
構成
Unityバージョン : 6000.0.31.f1
TestFrameworkバージョン : 1.4.5
InputSystemバージョン : 1.11.2
現象の詳細
以下のコードのテスト、コード量が多くなるので二つのテストのみ
using System.Collections; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; using UnityEngine.SceneManagement; using UnityEngine.InputSystem; using UnityEditor.SceneManagement; namespace Game.Player.Tests { public class PlayerMoveTests : InputTestFixture { private Keyboard _keyboard; [SetUp] public override void Setup() { base.Setup(); if (_keyboard == null) { Debug.Log("初期設定"); _keyboard = InputSystem.AddDevice<UnityEngine.InputSystem.Keyboard>(); } } [TearDown] public override void TearDown() { base.TearDown(); } [Description("上下左右の矢印キー入力で正常に InputMove に値が入るか.")] [UnityTest] public IEnumerator Test_PlayerMove_ArrowKeys_Correct() { yield return LoadTestScene(); yield return new WaitForSeconds(1f); var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0]; // 上下左右の入力通りに InputMove に値が入るか否か. Press(_keyboard.upArrowKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.up); Release(_keyboard.upArrowKey); InputSystem.Update(); Press(_keyboard.downArrowKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.down); Release(_keyboard.downArrowKey); InputSystem.Update(); Press(_keyboard.leftArrowKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.left); Release(_keyboard.leftArrowKey); InputSystem.Update(); Press(_keyboard.rightArrowKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.right); Release(_keyboard.rightArrowKey); GameObject.DestroyImmediate(player.gameObject); } [Description("WASD キー入力で正常に InputMove に値が入るか.")] [UnityTest] public IEnumerator Test_PlayerMove_WASDKeys_Correct() { yield return LoadTestScene(); yield return new WaitForSeconds(1f); var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0]; // 上下左右の入力通りに InputMove に値が入るか否か. Press(_keyboard.wKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.up); Release(_keyboard.wKey); InputSystem.Update(); Press(_keyboard.sKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.down); Release(_keyboard.sKey); InputSystem.Update(); Press(_keyboard.aKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.left); Release(_keyboard.aKey); InputSystem.Update(); Press(_keyboard.dKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.right); Release(_keyboard.dKey); GameObject.DestroyImmediate(player.gameObject); } // コード量が多くなるので省略. private AsyncOperation LoadTestScene() { return EditorSceneManager.LoadSceneAsyncInPlayMode("Assets/Game/Scripts/Player/Tests/PlayMode/TestScenes/PlayerTestScene.unity", new LoadSceneParameters(LoadSceneMode.Single)); } } }
このコードでテストを RunAll すると以下の画像のように先頭のテストのみ成功し、後ろのテストは全て失敗する現象が発生した

エラーコード
ArgumentOutOfRangeException: Cannot be negative Parameter name: value
似たような不具合が発生していたようだがこちらでは解決していなかった
https://discussions.unity.com/t/error-when-running-all-unit-tests/886736/7
ただ、この中でシーン上にある PlayerInput を含むオブジェクトを削除したらうまくいくとの書き込みがあり試しにシーン上にあるオブジェクトを TearDown 時に削除するようにしてみた
試行錯誤 ( テストは失敗する )
using System.Collections; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; using UnityEngine.SceneManagement; using UnityEngine.InputSystem; using UnityEditor.SceneManagement; namespace Game.Player.Tests { public class PlayerMoveTests : InputTestFixture { private Keyboard _keyboard; [SetUp] public override void Setup() { base.Setup(); if (_keyboard == null) { _keyboard = InputSystem.AddDevice<UnityEngine.InputSystem.Keyboard>(); } } [TearDown] public override void TearDown() { CleanUp(); base.TearDown(); } [Description("上下左右の矢印キー入力で正常に InputMove に値が入るか.")] [UnityTest] public IEnumerator Test_PlayerMove_ArrowKeys_Correct() { yield return LoadTestScene(); yield return new WaitForSeconds(1f); var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0]; // 上下左右の入力通りに InputMove に値が入るか否か. Press(_keyboard.upArrowKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.up); Release(_keyboard.upArrowKey); InputSystem.Update(); Press(_keyboard.downArrowKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.down); Release(_keyboard.downArrowKey); InputSystem.Update(); Press(_keyboard.leftArrowKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.left); Release(_keyboard.leftArrowKey); InputSystem.Update(); Press(_keyboard.rightArrowKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.right); Release(_keyboard.rightArrowKey); } [Description("WASD キー入力で正常に InputMove に値が入るか.")] [UnityTest] public IEnumerator Test_PlayerMove_WASDKeys_Correct() { yield return LoadTestScene(); yield return new WaitForSeconds(1f); var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0]; // 上下左右の入力通りに InputMove に値が入るか否か. Press(_keyboard.wKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.up); Release(_keyboard.wKey); InputSystem.Update(); Press(_keyboard.sKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.down); Release(_keyboard.sKey); InputSystem.Update(); Press(_keyboard.aKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.left); Release(_keyboard.aKey); InputSystem.Update(); Press(_keyboard.dKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.right); Release(_keyboard.dKey); } // 省略. private AsyncOperation LoadTestScene() { return EditorSceneManager.LoadSceneAsyncInPlayMode("Assets/Game/Scripts/Player/Tests/PlayMode/TestScenes/PlayerTestScene.unity", new LoadSceneParameters(LoadSceneMode.Single)); } // シーン上にあるオブジェクトを全て破棄する. private void CleanUp() { var activeScene = SceneManager.GetActiveScene(); foreach (var go in activeScene.GetRootGameObjects()) { GameObject.DestroyImmediate(go); } } } }
このコードにしてもエラーが出てテストが失敗したのであきらめかけていたがエラーコードの内容が
ArgumentOutOfRangeException: Cannot be negative Parameter name: value
から
System.ArgumentNullException : Value cannot be null. Parameter name: source
に変わっていたので、シーン上にあるオブジェクトを削除したのが原因 -> keyboard が null のままになってると推測を立てて
SetUp() 時に毎回 keyboard に値を入れ直すようにしたところ RunAll でテストが全て通過するようになった
[SetUp]
public override void Setup()
{
base.Setup();
//if (_keyboard == null)
//{
_keyboard = InputSystem.AddDevice<UnityEngine.InputSystem.Keyboard>();
//}
}
2回目以降は keyboard == null で keyboard 自体は null じゃなくて内部で利用している何かしらのインスタンスが null になってた?
ような挙動っぽい
解決したコード
using System.Collections; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; using UnityEngine.SceneManagement; using UnityEngine.InputSystem; using UnityEditor.SceneManagement; namespace Game.Player.Tests { public class PlayerMoveTests : InputTestFixture { private Keyboard _keyboard; [SetUp] public override void Setup() { base.Setup(); _keyboard = InputSystem.AddDevice<UnityEngine.InputSystem.Keyboard>(); } [TearDown] public override void TearDown() { // TearDown前にシーン上のオブジェクトを全て破棄. CleanUp(); base.TearDown(); } [Description("上下左右の矢印キー入力で正常に InputMove に値が入るか.")] [UnityTest] public IEnumerator Test_PlayerMove_ArrowKeys_Correct() { yield return LoadTestScene(); yield return new WaitForSeconds(1f); var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0]; // 上下左右の入力通りに InputMove に値が入るか否か. Press(_keyboard.upArrowKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.up); Release(_keyboard.upArrowKey); InputSystem.Update(); Press(_keyboard.downArrowKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.down); Release(_keyboard.downArrowKey); InputSystem.Update(); Press(_keyboard.leftArrowKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.left); Release(_keyboard.leftArrowKey); InputSystem.Update(); Press(_keyboard.rightArrowKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.right); Release(_keyboard.rightArrowKey); } [Description("WASD キー入力で正常に InputMove に値が入るか.")] [UnityTest] public IEnumerator Test_PlayerMove_WASDKeys_Correct() { yield return LoadTestScene(); yield return new WaitForSeconds(1f); var player = GameObject.FindObjectsByType<Player>(FindObjectsSortMode.None)[0]; // 上下左右の入力通りに InputMove に値が入るか否か. Press(_keyboard.wKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.up); Release(_keyboard.wKey); InputSystem.Update(); Press(_keyboard.sKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.down); Release(_keyboard.sKey); InputSystem.Update(); Press(_keyboard.aKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.left); Release(_keyboard.aKey); InputSystem.Update(); Press(_keyboard.dKey); InputSystem.Update(); Assert.AreEqual(player.MoveInput, Vector2.right); Release(_keyboard.dKey); } private AsyncOperation LoadTestScene() { return EditorSceneManager.LoadSceneAsyncInPlayMode("Assets/Game/Scripts/Player/Tests/PlayMode/TestScenes/PlayerTestScene.unity", new LoadSceneParameters(LoadSceneMode.Single)); } private void CleanUp() { var activeScene = SceneManager.GetActiveScene(); foreach (var go in activeScene.GetRootGameObjects()) { GameObject.DestroyImmediate(go); } } } }
テストが全て通過するようになった

今後のバージョンアップでシーン上のオブジェクトを全削除しなくてもよくなってほしい...
疑似Singletonな実装をしたMonoBehaviourクラスのPlayModeテスト後、後続のPlayModeテストが失敗する問題の対策 ( 対症療法 )
今回のようなケースはUnity側の不具合 ( テスト終了時にDontDestroyOnLoad設定したオブジェクトを削除してくれない ) ではなくそもそもの設計が悪いので自戒として残しておく
概要 : プロジェクトの構成
- DontDestroyOnLoad を行った疑似シングルトン実装のクラスのテスト
- このクラスは外部の別クラスを参照して何かしらの処理を行うものとする
- 通常のMonoBehaviour継承クラスのPlayModeテスト
以下はプロジェクトの構成、DontDestroyOnLoadが設定された疑似SingletonのA_ManagerクラスとA_Managerが利用するHogeクラス
それとは関係ないNanikanoKinouクラスの2種類のPlayModeテストを用意

疑似的なSingleton実装のMonoBehaviour継承クラス
using UnityEngine; using UnityEngine.SceneManagement; public class A_Manager : MonoBehaviour { private static object _instance = null; private Hoge _hoge = null; private void Awake() { if (_instance != null) { DestroyImmediate(gameObject); return; } _instance = this; SceneManager.sceneLoaded += OnSceneLoaded; DontDestroyOnLoad(this); } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; } private void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode) { _hoge = FindObjectOfType<Hoge>(); _hoge.Fuga(); } public bool Check() => true; }
上記A_Managerのテスト
サンプルの為必ず通過するテストで良い
using System.Collections; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; using UnityEngine.SceneManagement; using UnityEditor.SceneManagement; public class A_ManagerTests { private AsyncOperation LoadScene() => EditorSceneManager.LoadSceneAsyncInPlayMode("Assets/Scripts/A_Manager/Tests/PlayMode/A_ManagerTestScene.unity", new LoadSceneParameters(LoadSceneMode.Single)); [UnityTest] public IEnumerator Test_Fuga_Passes() { yield return LoadScene(); yield return new WaitForSeconds(1f); var manager = GameObject.FindObjectOfType<A_Manager>(); /* 本来は何かしらのテストが記述される. */ Assert.IsTrue(manager.Check()); } }
A_Managerとは関係のないNanikanoKinouとそのテスト
確認用なので中身はほぼ空
using UnityEngine; public class NanikanoKinou : MonoBehaviour { public bool KinouA() { return true; } }
using System.Collections; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; using UnityEngine.SceneManagement; using UnityEditor.SceneManagement; public class NanikanoKinouTests { private AsyncOperation LoadScene() => EditorSceneManager.LoadSceneAsyncInPlayMode("Assets/Scripts/NanikanoKinou/Tests/PlayMode/NanikanoKinouTestScene.unity", new LoadSceneParameters(LoadSceneMode.Single)); [UnityTest] public IEnumerator Test_KinouA_True() { yield return LoadScene(); yield return new WaitForSeconds(1f); var nanikanoKinou = GameObject.FindObjectOfType<NanikanoKinou>(); bool returnValue = nanikanoKinou.KinouA(); Assert.IsTrue(returnValue); } }
起きた不具合
それぞれのテストを都度実行していった際は全てのテストが通過するが、Run All で実行した際にSingletonクラスのテストの後続のテストが必ず失敗する


原因の解説
Singletonクラス側でシーンがロードされた際に外部のインスタンスを取得し、何かしらの処理を走らせているが、NanikanoKinou側のテストではこのHogeコンポーネントがアタッチされているオブジェクトは存在しないのでnullとなる
その為、NanikanoKinou側のPlayModeテストシーンがロードされた時にシーン上に残っているA_ManagerのOnSceneLoaded()が呼び出され、NullReferenceExceptionが送出されてテストが失敗する
private void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode) { _hoge = FindObjectOfType<Hoge>(); _hoge.Fuga(); }
対症療法
対症療法として、A_Managerのテストが完了した時にGameObject.DestroyImmediate()を実行し、Singletonクラスを削除することで後続のテストシーンがロードされた時にNullReferenceExceptionが送出されなくなる
[UnityTest]
public IEnumerator Test_Fuga_Passes()
{
yield return LoadScene();
yield return new WaitForSeconds(1f);
var manager = GameObject.FindObjectOfType<A_Manager>();
/*
本来は何かしらのテストが記述される.
*/
Assert.IsTrue(manager.Check());
GameObject.DestroyImmediate(manager);
}
本来為すべき設計について
今回の解決方法は所謂臭いものに蓋の対症療法となる
Singleton実装のクラスはテストを組みにくくするのでDIツールを用いての実装等、そもそもSingletonの実装をしないようにしたいところ
Singletonクラスの実装が必要なのであればSingletonクラス単体で完結するように設計するのが好ましいと思う
今回の場合であれば
- Hoge をシーンに配置するべきなのか
- 都度インスタンスを生成することで対応できないか( シーンに配置されたGameObjectからの参照の取得からシーンのロード時にInstantiateすることはできないか )
- A_Manager内で処理できないかを考慮( そもそもHogeクラスを利用しない )
等を考慮するとマシな形になるかも
SingletonなMonoBehaviour継承クラス単体で完結する場合であっても念のため、テストを行う際は最後にDestroyImmediate()でインスタンスを削除するのが無難
UnityHub でプロジェクトを作成する際、パスの文字数制限に引っかかって作成できなかった時の対処方法
たぶんUnity Hub の仕様なんだろうけどちょっと不便に感じる
最新の Unity Hub 3.9.1 も同様の現象は残ってる
構成
- Unity Hub : 3.8.0
- Editor Version : 2022.3.33f1
再現時の状態
Unity Hub で新しくプロジェクトを作成する時、以下の画像のようにパスの文字数制限に引っかかりプロジェクトが作成できなかった
原因は今のところ不明 ( 前日までは結構な長さのプロジェクト名でも正常にプロジェクトが作成できた )

この時の配置場所は以下のようなパスになっている
C:/Users/username/Desktop/UnityProjects/プロジェクト名
対処法 ( 対症療法的な手段 )
新プロジェクト作成時のチェックで引っかかるんだったら既に作ってるプロジェクトはセーフなんじゃね?ってノリでやってみたらうまくいった
- プロジェクトパスの文字数制限に引っかからないフォルダにて ( 例えば Cドライブ直下とか ) プロジェクトを作成
- Projects のリストからいったん削除
- Unity プロジェクトを本来配置したい場所に移動
- Unity Hub の [Add] からプロジェクトを追加

以上の手順を踏んだら問題なくプロジェクトが起動で来たので多分大丈夫そう