MRが楽しい

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

BlenderのInstaller版とPortable版での設定ファイル保存先の違い

本日は Blender の小ネタ枠です。
BlenderのInstaller版とPortable版での設定ファイル保存先の違いを記事にします。

BlenderのInstaller版とPortable版

Blenderインストーラを使う Installer 版と、Zipファイルを展開して使う Portable 版が選択できます。

Installer版の設定ファイル

Installer 版の場合、Blender の設定ファイルは以下のユーザフォルダに生成されます。

C:\Users\(ユーザ名)\AppData\Roaming\Blender Foundation\Blender\(バージョン番号)\config


Portable版の設定ファイル

Portable 版の場合、設定ファイルは blender.exe の展開フォルダ直下の以下のフォルダに生成されます。

(バージョン番号)\config


ただし既に Installer 版をインストールしている場合、Portable 版でもユーザフォルダ配下に設定ファイルが生成されることがあるようです。

HoloLens2でホロモンアプリを作る その108(QRコードによる位置合わせの実装)

本日はアプリ作成枠です。
HoloLens2でホロモンアプリを作る進捗を書き留めていきます。

QRコードによる位置合わせの実装

AR デバイス同士でシェアリングするに当たって、共通の空間の原点座標をどう設定するかという問題があります。
今回は QR コードを使って位置合わせを行うことにしました。

QR コードによる位置合わせは認識精度が高いほか、AndroidiOS のプラットフォームでも利用可能なメリットがあります。
一方で QR コードを準備する必要があるデメリットがあります。

Microsoft.MixedReality.QR パッケージ

QR コードを利用するパッケージには Microsoft.MixedReality.QR パッケージを利用します。
bluebirdofoz.hatenablog.com

空間を示すゲームオブジェクトを作成し、その配下にアプリ内の全てのモデルを含めます。
QR コードを認識した位置にこのゲームオブジェクトを移動すれば、全てのデバイスで現実世界における共通の原点座標(QRコード位置)が利用できます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace SharingMarker.HologramCollection
{
    public class HologramCollectionTracker : MonoBehaviour
    {
        /// <summary>
        /// 共通座標トランスフォーム
        /// </summary>
        private Transform p_SharedWorldAnchorTransform;


        void Awake()
        {
            // 共通座標トランスフォームを取得する
            p_SharedWorldAnchorTransform = SharedCollectionForXR.Instance.gameObject.transform;
        }

        void Update()
        {
        }

        /// <summary>
        /// 指定のトランスフォームに追従する
        /// </summary>
        /// <param name="a_TargetTransform"></param>
        public void TrackTransform(Transform a_TargetTransform)
        {
            TrackSharedTransform(a_TargetTransform);
        }

        /// <summary>
        /// 共有座標をリセットする
        /// </summary>
        public void ResetTransform()
        {
            ResetSharedTransform();
        }

        /// <summary>
        /// 追跡対象トランスフォームの位置に共有座標を移動する
        /// </summary>
        private void TrackSharedTransform(Transform a_TargetTransform)
        {
            if (p_SharedWorldAnchorTransform != null)
            {
                // 追従対象オブジェクトの現在位置に共通座標トランスフォームの位置を移動する
                p_SharedWorldAnchorTransform.transform.position = a_TargetTransform.position;
                p_SharedWorldAnchorTransform.transform.rotation = a_TargetTransform.rotation;
            }
        }

        /// <summary>
        /// 共有座標をリセットする
        /// </summary>
        private void ResetSharedTransform()
        {
            if (p_SharedWorldAnchorTransform != null)
            {
                // 共通座標トランスフォームの位置をリセットする
                p_SharedWorldAnchorTransform.transform.position = new Vector3();
                p_SharedWorldAnchorTransform.transform.rotation = new Quaternion();
            }
        }
    }
}

C#でオブジェクトのnull判定を行うときにReferenceEqualsを利用する

本日は C# の小ネタ枠です。
C# の null 判定では ReferenceEquals を利用すべきというお話です。
(または C# 8.0 以降では is null)

ReferenceEquals

ReferenceEquals は指定した Object インスタンスが同一インスタンスかどうかを判断します。
learn.microsoft.com

object obj = null;

if (Object.ReferenceEquals(obj, null)) { // null 時処理 }

等価演算子の問題点

オブジェクトの null 判定のよくある方法として等価演算子を利用する方法があります。

object obj = null;

if (obj == null) { // null 時処理 }

ただし等価演算子を利用する場合は対象オブジェクトのクラスが等値演算子の処理をオーバーロードしていた場合に問題が発生します。
例えば下記のケースでは NullReferenceException が発生します。

ObjectSample obj = null;
if (obj == null) { // null 時処理 }

class ObjectSample
{
    public static bool operator ==(ObjectSample x, ObjectSample y) => x.Equals(y);
    public static bool operator !=(ObjectSample x, ObjectSample y) => !x.Equals(y);
}

一方で ReferenceEquals は参照の等価性をチェックする関数のため、等価演算子の処理は呼び出されず、対象の参照が null であるかのみを必ずチェックできます。
このため、オブジェクトの null 判定を行いたい場合は等価演算子ではなく ReferenceEquals を利用した方が安全です。

is null

C# 8.0 以降はポインターに対してもパターンマッチングが使えるため、以下のように安全に null 判定をすることもできます。

object obj = null;

if (obj is null) { // null 時処理 }

参考ページ

ufcpp.net
ufcpp.net
qiita.com

HoloLens2でホロモンアプリを作る その107(ホロモンのクライアントモードの実装)

本日はアプリ作成枠です。
HoloLens2でホロモンアプリを作る進捗を書き留めていきます。

ホロモンのクライアントモードの実装

シェアリング時の動作用にホロモンのクライアントモードを実装しました。
ホスト側で空間認識との判定等も管理するため、クライアント側ではアタリ判定を含むすべてのコントロールを無効化します。

行動ロジックに以下のような None ロジックを新たに追加し、アプリがクライアント接続で起動した場合は本ロジックを有効化するようにしました。
・HoloMonModeLogicNone.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using Cysharp.Threading.Tasks;
using UniRx;

using HoloMonApp.Content.Character.Interrupt;

namespace HoloMonApp.Content.Character.Behave.ModeLogic.None
{
    public class HoloMonModeLogicNone : MonoBehaviour, HoloMonActionModeLogicIF
    {
        /// <summary>
        /// モードロジック共通参照
        /// </summary>
        private ModeLogicReference p_ModeLogicReference;

        /// <summary>
        /// モードロジック共通情報
        /// </summary>
        [SerializeField, Tooltip("モードロジック共通情報")]
        private HoloMonModeLogicCommon p_ModeLogicCommon = new HoloMonModeLogicCommon();

        /// <summary>
        /// 初期化
        /// </summary>
        /// <param name="reference"></param>
        public void AwakeInit(HoloMonBehaveReference reference)
        {
            p_ModeLogicReference = new ModeLogicReference(reference);
        }

        /// <summary>
        /// 現在の実行待機中フラグ
        /// </summary>
        /// <returns></returns>
        public bool CurrentRunAwaitFlg()
        {
            return p_ModeLogicCommon.RunAwaitFlg;
        }

        /// <summary>
        /// ホロモンアクションモード種別
        /// </summary>
        /// <returns></returns>
        public HoloMonActionMode GetHoloMonActionMode()
        {
            return HoloMonActionMode.None;
        }

        /// <summary>
        /// モード実行(async/await制御)
        /// </summary>
        public async UniTask<ModeLogicResult> RunModeAsync(ModeLogicSetting a_ModeLogicSetting)
        {
            // 設定アクションデータを保持する
            p_ModeLogicCommon.SaveCommonSetting(a_ModeLogicSetting);

            // 開始処理を行う
            EnableSetting();

            // モードを開始して完了を待機する
            ModeLogicResult result = await p_ModeLogicCommon.RunModeAsync();

            // 終了状態を返却する
            return result;
        }

        /// <summary>
        /// モードキャンセル
        /// </summary>
        public void CancelMode()
        {
            // 停止処理を行う
            DisableSetting();

            // キャンセル処理を行う
            p_ModeLogicCommon.CancelMode();
        }

        /// <summary>
        /// モード内部停止
        /// </summary>
        private void StopMode(ModeLogicResult a_StopModeLogicResult)
        {
            // 停止処理を行う
            DisableSetting();

            // 停止状態を設定する
            p_ModeLogicCommon.StopMode(a_StopModeLogicResult);
        }

        /// <summary>
        /// 割込み通知
        /// </summary>
        public bool TransmissionInterrupt(InterruptInformation a_InterruptInfo)
        {
            bool isProcessed = false;

            // 処理なし

            return isProcessed;
        }


        /// <summary>
        /// 固有アクションデータの参照
        /// </summary>
        private ModeLogicNoneData p_Data =>
            (ModeLogicNoneData)p_ModeLogicCommon.ModeLogicSetting.ModeLogicData;


        void Update()
        {
            // 処理なし
        }

        /// <summary>
        /// モードの設定を有効化する
        /// </summary>
        private bool EnableSetting()
        {
            // アニメーションを待機モードにする
            p_ModeLogicReference.Control.AnimationsBodyAPI.ReturnStandbyMode();

            // 無行動中はアタリ判定を切る
            p_ModeLogicReference.Control.BodyComponentsToColliderAPI.SetEnabled(false);

            // 無行動中は重力を切る
            p_ModeLogicReference.Control.BodyComponentsToRigidbodyAPI.SetUseGravity(false);

            return true;
        }

        /// <summary>
        /// モードの設定を無効化する
        /// </summary>
        private bool DisableSetting()
        {
            // アニメーションを待機モードにする
            p_ModeLogicReference.Control.AnimationsBodyAPI.ReturnStandbyMode();

            // 無行動終了時はアタリ判定を戻す
            p_ModeLogicReference.Control.BodyComponentsToColliderAPI.SetEnabled(true);

            // 無行動終了時は重力を元に戻す
            p_ModeLogicReference.Control.BodyComponentsToRigidbodyAPI.SetUseGravity(true);

            return true;
        }
    }
}

次はクライアント時にホスト側のステータス変化を反映するネットワークモジュールを作成中です。

GoogleMaterialColorToolを使って配色ごとのテキストの可読性をチェックする

本日は UI に関する小ネタ枠です。
Google Material Color Tool を使って配色ごとのテキストの可読性をチェックする方法を記事にします。

Google Material Color Tool

Google Material Color Tool は任意の色の組み合わせでカラーパレットを作成してそのアクセシビリティ(可読性)を判定します。
material.io

利用例

例えば Unity で以下のように赤色の背景色のテキストパネルを作成したいとします。
テキストが黒色だと読みづらいように感じるので、実際に Google Material Color Tool を使って可読性をチェックしてみます。

Google Material Color Tool のページを開きます。
material.io

任意の色のチェックを行いたいので[CUSTOM]タブを開き、[Primary]に背景色を指定します。
Unity 側からはカラーパレットの[Hexadecital]でカラーの値を取得できます。

次にテキストの色を指定します。[Text on P]にテキスト色を指定します。
なお[Primary]設定時、テキスト色に白または黒から可読性の高い方の色が自動で設定されます。

色の設定が完了したら[ACCESSIBILITY]タブを開いて結果を確認します。
今回は[Primary -> Custom Text]の項に[NOT LEGIBLE(判読不可)]が表示されており、可読性の低い配色であることが分かります。

同時に白または黒をテキスト色に指定した場合の結果も表示されており、[Primary -> White Text]では[min 55% opacity]が表示されており、こちらは可読性の高い配色であることが分かります。
このことから背景色にこの赤色を利用する場合、テキスト色は白色にした方が適切と言えます。(なお % が小さいほど可読性が高い評価になります)

配色による可読性の変化を定量的にチェックしたい場合に役立ちます。

TortoiseGitのインストール手順

本日は環境構築の手順枠です。
Windows 11 環境で TortoiseGit のインストールを行ったので手順を記録します。

TortoiseGit

TortoiseGit は Windows のファイルエクスプローラから利用できる Git クライアントツールです。
tortoisegit.org

前提条件

TortoiseGit を利用するには事前に Git for Windows のインストールが必要です。
以下の記事に従って Git for Windows をインストールしてください。
bluebirdofoz.hatenablog.com

TortoiseGitのインストール手順

TortoiseGit のインストーラを以下のページから取得します。
ページ内の最新バージョンのリンクをクリックします。
tortoisegit.org

32bit版と64bit版があるのでOSに合わせてインストーラを選択してダウンロードします。

ページ下部には言語パックのインストーラもあります。
TortoiseGit を日本語化して利用したい場合はこちらも適切な項目を選択してダウンロードします。

ダウンロードした TortoiseGit-X.X.X.X-XXbit.msi を実行します。

インストーラが起動するので[Next]をクリックします。

利用規約が表示されます。確認して[Next]をクリックします。

TortoiseGit で利用するSSHクライアントを選択して[Next]をクリックします。
特に拘りがなければデフォルトの TortoisePLink で良いでしょう。

インストールするコンポーネントを選択して[Next]をクリックします。
基本的にはデフォルトのままで問題ありません。

最後に[Install]を実行します。

これで TortoiseGit のインストールは完了です。

ファイルエクスプローラ上で右クリックを押すと、以下の通り[TortoiseGit]の項目が追加されています。

エラー

[TortoiseGit -> Clone]を選択した場合に以下のエラーが発生する場合は Git for Windows がインストールされていません。
改めて以下の記事を確認して Git for Windows をインストールしてみてください。

TortoiseGitの日本語化手順

次に日本語化の手順です。
先ほどダウンロードした言語パックの TortoiseGit-LanguagePack-X.X.X.X-XXbit-ja.msi を実行します。

インストーラが起動するので[次へ]をクリックします。

日本語の言語パッケージがインストールされます。

ファイルエクスプローラ上で右クリックから[TortoiseGit -> Settings]を選択します。

[General]タブを開き、[Language]のプルダウンから[日本語]が選択できるようになっているのでこれを選択します。
選択後、[OK]または[適用]ボタンで設定を反映します。

これで日本語化は完了です。

GitForWindowsのインストール手順

本日は環境構築の手順枠です。
Windows 11 環境で GitForWindows のインストールを行ったので手順を記録します。

GitForWindows

Windows 環境で Git を利用可能な CUI 環境を提供するオープンソースプロジェクトです。
gitforwindows.org

GitForWindowsのインストール手順

GitForWindows のインストーラを以下のページから取得します。
gitforwindows.org

ダウンロードした Git-X.XX.X-XX-bit.exe を実行します。

インストールウィザードの選択

GitForWindows のインストールでは多くの設定項目が表示されます。
基本的にデフォルトで問題ありませんが、それぞれの設定内容を以下に書き出します。

ライセンス条項の確認

GitForWindows のライセンス情報などが表示されます。
設定操作はないので[Next]で次に進みます。

インストールディレクトリの選択

インストールディレクトリを指定します。
任意のインストールディレクトリを指定して[Next]で進みます。

コンポーネントの選択

インストールするコンポーネントを選択します。
通常デフォルトのままで問題ありません。

スタートメニューの設定

スタートメニューの設定です。名前を変更したり作成の有無を指定できます。
デフォルトでは[Git]という名前でスタートメニューに追加されます。

デフォルトテキストエディタの選択

GitForWindows で利用するテキストエディタを指定します。
デフォルトでは VIM が指定されていますが、使いづらいため最新の GUI エディターへの変更が推奨されています。


ブランチ初期化時の名称

新規リポジトリ作成時の初期ブランチの名称を設定します。
デフォルトでは[master]になりますが、例えば GitHub などでは[main]が利用されています。

本設定は既存のリポジトリには影響しません。

環境変数のパスへの設定

環境変数に Git へのパスを行うか設定できます。
推奨の[Use Git from the Windows Command Prompt]を選択すると、環境変数に (インストールディレクトリ)\Git\cmd が追加されてコマンドプロンプトPowerShellから Git が利用可能になります。

SSHの実行ファイルの選択

GitForWindows で利用する SSH の種類を選択できます。
基本的にデフォルトの[Use bundled OpenSSH]で問題ありません。

HTTPS通信での利用ツールの選択

HTTPS通信で利用するツールを選択できます。
基本的にデフォルトの[Use the OpenSSL library]で問題ありません。

改行の取り扱い

チェックイン、チェックアウト時の改行の取り扱いについての設定です。
デフォルトの[checkout Windows-style]を選択すると、自動で改行が変更されます。(チェックアウト時 CRLF、チェックイン時 LF)
これは事故の元なので変更が行われない[Checkout as-is, commit as-is]の利用を推奨しているサイトも多いです。

ターミナルエミュレーターの設定

GitBash で利用するターミナルエミュレーターを選択します。
基本的にデフォルトの[Use MinTTY(the default terminal of MSYS2)]で問題ありません。

GIT PULL時の動作

git pull を行った際の動作を指定します。
デフォルトの[Default(fast-forward or merge)]では可能な場合は現在のブランチを取り込んだブランチに反映を行い、そうでない場合はマージコミットを作成します。
これは git pull の標準的な動作になります。

Credential Helperの設定

Credential Helper はAzure DevOps、Azure DevOps Server、GitHub、Bitbucket、GitLab での多要素での認証をサポートします。
基本的にデフォルトの[Git Credential Manager]で問題ありません。

追加オプション

追加で行うオプションの設定です。
[Enable file system caching]はgit status時などでの応答速度を改善するfscacheキャッシュを有効にします。(ただしPCのリソースを多少消費します)
[Enable symbolic links]はシンボリックリンクの使用を有効にします。
基本的にデフォルトの選択のままで問題ありません。

実験的オプション

現在実験的な段階のオプションについての設定です。
基本的のデフォルトの非選択のままで問題ありません。
[Install]を選択すると GitForWindows のインストールが開始されます。

これで GitForWindows のインストールは完了です。

参考サイト

以下のページで各ウィザードの選択肢についても翻訳されて詳しい解説が書かれています。
qiita.com