MRが楽しい

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

VisualStudioの変数名の変更でリファクタリング機能を利用する

本日は VisualStudio の小ネタ枠です。
VisualStudioの変数名の変更でリファクタリング機能を利用する手順について記事にします。
f:id:bluebirdofoz:20210403232636j:plain

変数名または関数名のリファクタリング

変数名または関数名上で右クリックから[名前の変更]を実行することでリファクタリング機能を使って名前を変更できます。
f:id:bluebirdofoz:20210403232649j:plain

リファクタリング機能を利用すると、その変数または関数を参照する名称も自動的に変更されます。
f:id:bluebirdofoz:20210403232704j:plain

クラス名のリファクタリング

クラス名上で右クリックから[名前の変更]を実行することで、同様に名前を変更できます。
f:id:bluebirdofoz:20210403232716j:plain

クラス名の場合も、そのクラスを参照する名称も自動的に変更されます。
f:id:bluebirdofoz:20210403232729j:plain

またクラス名の場合は、[シンボルのファイルの名前を変更する]にチェックを入れておくとファイル名も自動的に変更を行ってくれます。
f:id:bluebirdofoz:20210403232741j:plain

HoloLens2でホロモンアプリを作る その24(ホロモンに任意の方向を注視させる)

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

今回はホロモンに任意の方向を注視させるメモです。

ホロモンに任意の方向を注視させる

ホロモンに任意の方向を中止させるには、以下の記事で紹介した HeadLookController を利用します。
bluebirdofoz.hatenablog.com

注視対象を任意の位置にするため、以下の注視用オブジェクトを作成しました。
f:id:bluebirdofoz:20210402231944j:plain

このオブジェクトは表示と非表示を切り替えられるように、Mesh と Renderer コンポーネントを持つオブジェクトを別にしています。
f:id:bluebirdofoz:20210402231958j:plain

プレイヤーの注視を行うスクリプトを改修し、指定された任意のオブジェクトを注視させるスクリプトを作成しました。
・HoloMonLookLogicTargetTracking.cs

using UnityEngine;
using UniRx;

using HMProject.HoloMon;

namespace HMProject.HoloMonLogic
{
    public class HoloMonLookLogicTargetTracking : MonoBehaviour, HoloMonLogicInterface
    {
        /// <summary>
        /// 担当ホロモン視線ロジック
        /// </summary>
        const HoloMonLook MyHoloMonModeLogic = HoloMonLook.PlayerTracking;

        /// <summary>
        /// 視線ロジックの起動状態
        /// </summary>
        [SerializeField, Tooltip("視線ロジックの起動状態")]
        private BoolReactiveProperty p_LookLogicFlg = new BoolReactiveProperty(false);

        /// <summary>
        /// 視線ロジックの起動状態のReadOnlyReactivePropertyの保持変数
        /// </summary>
        private IReadOnlyReactiveProperty<bool> p_IReadOnlyReactivePropertyLookLogicFlg;

        /// <summary>
        /// 視線ロジックの起動状態のReadOnlyReactivePropertyの参照変数
        /// </summary>
        public IReadOnlyReactiveProperty<bool> IReadOnlyReactivePropertyLookLogicFlg
            => p_IReadOnlyReactivePropertyLookLogicFlg
            ?? (p_IReadOnlyReactivePropertyLookLogicFlg = p_LookLogicFlg.ToSequentialReadOnlyReactiveProperty());

        
        /// <summary>
        /// ホロモン頭部視点の制御コンポーネント
        /// </summary>
        [SerializeField, Tooltip("ホロモン頭部視点の制御コンポーネント")]
        private HeadLookControllerSmooth p_HeadLookController;


        /// <summary>
        /// 追跡オブジェクト位置
        /// </summary>
        [SerializeField, Tooltip("追跡オブジェクト位置")]
        Transform p_TargetPoint;

        
        /// <summary>
        /// HeadLookControllerの各ボーンの最大角デフォルト設定
        /// </summary>
        [SerializeField, Tooltip("HeadLookControllerの各ボーンの最大角デフォルト設定")]
        private float p_DefaultMaxBendingAngles;

        /// <summary>
        /// HeadLookControllerのアニメーションオーバーライドのデフォルト値
        /// </summary>
        [SerializeField, Tooltip("HeadLookControllerのアニメーションオーバーライドのデフォルト値")]
        private bool p_DefaultOverrideAnimation;


        /// <summary>
        /// モードの有効無効を取得する
        /// </summary>
        /// <returns></returns>
        public bool GetModeStatus()
        {
            return p_LookLogicFlg.Value;
        }

        /// <summary>
        /// モードを有効化する
        /// </summary>
        /// <returns></returns>
        public bool EnableMode()
        {
            bool result = false;

            result = EnableTrackingStatus();
            return result;
        }

        /// <summary>
        /// モードを無効化する
        /// </summary>
        /// <returns></returns>
        public bool DisableMode()
        {
            bool result = false;

            result = DisableTrackingStatus();
            return result;
        }

        /// <summary>
        /// 追跡対象を設定する
        /// </summary>
        /// <param name="a_Target"></param>
        /// <returns></returns>
        public bool SetTargetTransform(Transform a_Target)
        {
            bool result = false;

            p_TargetPoint = a_Target;
            result = true;

            return result;
        }


        /// <summary>
        /// 開始処理
        /// </summary>
        void Start()
        {
        }

        /// <summary>
        /// 定期処理
        /// </summary>
        void Update()
        {

        }

        /// <summary>
        /// 定期処理(Late)
        /// </summary>
        void LateUpdate()
        {
            if (p_LookLogicFlg.Value)
            {
                // プレイヤーの頭部位置を毎フレーム更新する
                p_HeadLookController.target = p_TargetPoint.position;
            }
        }


        /// <summary>
        /// 視線モードの設定を有効化する
        /// </summary>
        private bool EnableTrackingStatus()
        {
            // 視線の追従を有効化する
            ChangeMaxBendingAngle(true);

            // モードフラグを設定する
            p_LookLogicFlg.SetValueAndForceNotify(true);

            return true;
        }

        /// <summary>
        /// 視線モードの設定を無効化する
        /// </summary>
        private bool DisableTrackingStatus()
        {
            // 視線の追従を無効化する
            ChangeMaxBendingAngle(false);

            // モードフラグを落とす
            p_LookLogicFlg.SetValueAndForceNotify(false);

            return true;
        }

        /// <summary>
        /// 有効無効に合わせてHeadLookControllerの最大角を変更する
        /// </summary>
        /// <param name="onoff"></param>
        private void ChangeMaxBendingAngle(bool onoff)
        {
            // 有効無効に合わせてHeadLookControllerの最大角を変更する
            int segmentnum = p_HeadLookController.segments.Length;
            if (segmentnum > 0)
            {
                for (int loop = 0; loop < segmentnum; loop++)
                {
                    float setValue = 0;
                    if (onoff)
                    {
                        setValue = p_DefaultMaxBendingAngles;
                    }
                    p_HeadLookController.segments[loop].maxBendingAngle = setValue;
                }
            }
            // 有効無効に合わせてOverrideAnimationの設定を変更する
            if (onoff)
            {
                p_HeadLookController.overrideAnimation = p_DefaultOverrideAnimation;
            }
            else
            {
                p_HeadLookController.overrideAnimation = false;
            }
        }
    }
}

更に、注視オブジェクトには以下のようなオブジェクトの有効化と無効化、表示と非表示を切り替えるスクリプトを設定しました。
・ItemAttentionController

using UnityEngine;
using UnityEngine.Events;
using System;

namespace HMProject.Item
{
    [Serializable]
    public enum AttentionMode
    {
        Disable = 0,
        Show = 1,
        Hide = 2,
    }
    
    public class ItemAttentionController : MonoBehaviour
    {
        /// <summary>
        /// 開始位置
        /// </summary>
        [SerializeField, Tooltip("開始位置")]
        private Transform p_StartPosition;

        /// <summary>
        /// デフォルト座標
        /// </summary>
        [SerializeField, Tooltip("デフォルト座標")]
        private Vector3 p_DefaultWorldPosition;

        /// <summary>
        /// デフォルトスケール
        /// </summary>
        [SerializeField, Tooltip("デフォルトスケール")]
        private Vector3 p_DefaultLocalScale;

        /// <summary>
        /// 注視モード
        /// </summary>
        [SerializeField, Tooltip("注視モード")]
        private AttentionMode p_AttentionMode;

        /// <summary>
        /// 注視オブジェクトの参照
        /// </summary>
        [SerializeField, Tooltip("注視オブジェクトの参照")]
        private GameObject p_AttentionObject;

        /// <summary>
        /// モデルオブジェクトの参照
        /// </summary>
        [SerializeField, Tooltip("モデルオブジェクトの参照")]
        private GameObject p_ModelObject;


        /// <summary>
        /// Disable切り替え時の実行イベント
        /// </summary>
        [SerializeField, Tooltip("Disable切り替え時の実行イベント")]
        private UnityEvent p_DisableEvent;

        /// <summary>
        /// Show切り替え時の実行イベント
        /// </summary>
        [SerializeField, Tooltip("Show切り替え時の実行イベント")]
        private UnityEvent p_ShowEvent;

        /// <summary>
        /// Hide切り替え時の実行イベント
        /// </summary>
        [SerializeField, Tooltip("Hide切り替え時の実行イベント")]
        private UnityEvent p_HideEvent;


        /// <summary>
        /// 開始処理
        /// </summary>
        void Start()
        {
            // デフォルト座標を保存する
            p_DefaultWorldPosition = this.transform.position;

            // デフォルトスケールを保存する
            p_DefaultLocalScale = this.transform.localScale;

            // 注視/モデルオブジェクトを無効化する
            p_AttentionObject.SetActive(false);
            p_ModelObject.SetActive(false);

            // 状態を設定する
            p_AttentionMode = AttentionMode.Disable;
        }

        /// <summary>
        /// 定期処理
        /// </summary>
        void Update()
        {

        }

        /// <summary>
        /// オブジェクトリセット
        /// </summary>
        public void ResetObject()
        {
            // デフォルト位置に戻す
            this.transform.position = p_DefaultWorldPosition;

            // デフォルトスケールに戻す
            this.transform.localScale = p_DefaultLocalScale;
        }

        /// <summary>
        /// オブジェクト開始
        /// </summary>
        public void StartObject()
        {
            // 開始位置にオブジェクトを移動する
            this.transform.position = p_StartPosition.position + (p_StartPosition.up * 0.1f);

            // デフォルトスケールに戻す
            this.transform.localScale = p_DefaultLocalScale;
        }

        /// <summary>
        /// モードをスイッチする
        /// </summary>
        public void SwitchMode()
        {
            // 現在のモードに合わせて次のモードに切り替える
            switch(p_AttentionMode)
            {
                case AttentionMode.Disable:
                    // Disableの場合は次はShowに切り替える
                    SwitchShowMode();
                    break;
                case AttentionMode.Show:
                    // Showの場合は次はHideに切り替える
                    SwitchHideMode();
                    break;
                case AttentionMode.Hide:
                    // Hideの場合は次はDisableに切り替える
                    SwitchDisableMode();
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// アクティブ状態をスイッチする
        /// </summary>
        public void SwitchActive()
        {
            // 現在のモードに合わせて次のモードに切り替える
            switch (p_AttentionMode)
            {
                case AttentionMode.Disable:
                    // Disableの場合は次はShowに切り替える
                    SwitchShowMode();
                    break;
                case AttentionMode.Show:
                case AttentionMode.Hide:
                    // ShowまたはHideの場合は次はDisableに切り替える
                    SwitchDisableMode();
                    break;
                default:
                    break;
            }

            // オブジェクトを開始位置にリセットする
            StartObject();
        }

        /// <summary>
        /// 表示状態をスイッチする
        /// </summary>
        public void SwitchVisible()
        {
            // 現在のモードに合わせて次のモードに切り替える
            switch (p_AttentionMode)
            {
                case AttentionMode.Disable:
                case AttentionMode.Hide:
                    // DisableまたはHideの場合は次はShowに切り替える
                    SwitchShowMode();
                    break;
                case AttentionMode.Show:
                    // Showの場合は次はHideに切り替える
                    SwitchHideMode();
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Disableモードに切り替える
        /// </summary>
        private void SwitchDisableMode()
        {
            // 注視/モデルオブジェクトを無効化する
            p_AttentionObject.SetActive(false);
            p_ModelObject.SetActive(false);

            // イベントを実行する
            p_DisableEvent.Invoke();

            // 状態を設定する
            p_AttentionMode = AttentionMode.Disable;
        }

        /// <summary>
        /// Showモードに切り替える
        /// </summary>
        private void SwitchShowMode()
        {

            // 注視/モデルオブジェクトを有効化する
            p_AttentionObject.SetActive(true);
            p_ModelObject.SetActive(true);

            // イベントを実行する
            p_ShowEvent.Invoke();

            // 状態を設定する
            p_AttentionMode = AttentionMode.Show;
        }

        /// <summary>
        /// Hideモードに切り替える
        /// </summary>
        private void SwitchHideMode()
        {
            // 注視オブジェクトを有効化し、
            // モデルオブジェクトを無効化する
            p_AttentionObject.SetActive(true);
            p_ModelObject.SetActive(false);

            // イベントを実行する
            p_HideEvent.Invoke();

            // 状態を設定する
            p_AttentionMode = AttentionMode.Hide;
        }
    }
}

シーンを再生して動作を確認します。
[Attention]ボタンをクリックすると注視オブジェクトが有効化されます。
f:id:bluebirdofoz:20210402232017j:plain

注視オブジェクトが有効化されると、ホロモンがそちらを向きました。
f:id:bluebirdofoz:20210402232031j:plain

また、非表示にすることでホロモンが何もない虚空を見つめるような形になります。
ホロモンが起こす色々なアクションに活用できそうです。
f:id:bluebirdofoz:20210402232044j:plain

WindowsPCを初期状態に戻す

本日は Windows の小ネタ枠です。
個人ファイルを全て削除してWindowsPCを初期状態に戻す手順について記事にします。

このPCを初期状態に戻す

メニューの検索欄から[初期状態]、[リカバリ]などで検索します。
[このPCを初期状態に戻す]の選択肢が表示されるので、これを選択します。
f:id:bluebirdofoz:20210401090928j:plain

[回復]タブを開いた設定パネルが表示されます。
[開始する]ボタンをクリックします。
f:id:bluebirdofoz:20210401090940j:plain

[このPCをリセットする]ダイアログが表示されます。
PCのデータを全て削除して初期化する場合は[すべて削除する]をクリックします。
f:id:bluebirdofoz:20210401090954j:plain

追加の設定画面が開きます。
[次へ]ボタンをクリックします。
f:id:bluebirdofoz:20210401091007j:plain

TIPS

設定の追加からはデータの削除方法を変更することができます。
ドライブクリーニングをオンにするとファイルの回復をより困難にすることができます。
f:id:bluebirdofoz:20210401091018j:plain

リセット画面が開きます。
[リセット]ボタンをクリックすると、PCの初期化が開始されます。
f:id:bluebirdofoz:20210401091029j:plain

UWPアプリのビルド時、Unityプロジェクトで変更したCapabilitiesの設定が反映されない問題の対処

本日は Unity の小ネタ枠です。
UWPアプリのビルド時、Unityプロジェクトで変更したCapabilitiesの設定が反映されない問題の対処について記事にします。

Unityプロジェクトで変更したCapabilitiesの設定が反映されない

以下のようなケースでUnityプロジェクトで変更した Capabilities の設定が反映されない問題が発生します。

1.ソリューションファイルをビルド済みの Unity プロジェクトを開きます。
f:id:bluebirdofoz:20210331084002j:plain

[Project Settings]から[Capabilities]を変更する。
f:id:bluebirdofoz:20210331084012j:plain

この状態で[Build Settings]から再ビルドを行う。
すると、ソリューションファイルに Capabilities の変更が反映されておらず、最終的な UWP アプリに Capabilities の設定変更が反映されません。
f:id:bluebirdofoz:20210331084023j:plain

こういった場合はソリューション内の appxmanifest の[機能]タグで直接 Capabilities の設定を行うか、ソリューションファイルを一度削除して Unity プロジェクトから再ビルドを行うと、正しいソリューションファイルが作成できます。
f:id:bluebirdofoz:20210331084202j:plain

GitHubで100MB以上のファイルをコミットしてしまった場合の応急処置

本日は GitHub の小ネタ枠です。
GitHubで100MB以上のファイルをコミットしてしまった場合の応急処置について記事にします。

this exceeds GitHub’s file size limit of 100.00 MB

GitHub に100MB以上のファイルサイズを持つファイルを含んだコミットをプッシュすると以下のエラーが表示され、プッシュに失敗します。

this exceeds GitHub’s file size limit of 100.00 MB

docs.github.com

本エラーを回避するにはファイルサイズを 100MB 以下に変更するか、 GitLFS を用いて別の場所に 100MB 超のファイルをアップロードする必要があります。
また 100MB 以下のファイルに変更する場合も、100MB 以上のファイルが含まれるコミットが存在する場合はエラーになってしまうため、対象のコミットを一旦取り消す必要があります。

本記事では応急処置として100MB 以上のファイルのコミットを取り消しす対処を紹介します。

ファイルのコミットを削除する場合

100MB 以上のファイルのコミットを削除する場合、以下の手順でコミットの修正を実施します。

1.対象のファイルを追加したコミットを削除する
2.100MBのファイルを削除する
3.改めてその他の変更ファイルをコミットする

以下のコマンドで git のログを表示し、 100MB 以上のファイルをコミットを確認します。

git log

f:id:bluebirdofoz:20210330095253j:plain

以下のいずれかのコマンドでコミットを削除します。
[reset]はコミットの削除コマンドで、[--soft]オプションは現在のファイルを変更せずにコミットを削除します。

・コミットIDを指定する場合

git reset --soft (コミットID)

f:id:bluebirdofoz:20210330095302j:plain

・最新コミットを指定する場合

git reset --soft HEAD^

f:id:bluebirdofoz:20210330095310j:plain

これでコミットが削除されました。
f:id:bluebirdofoz:20210330095318j:plain

後は 100MB 以上のファイルを削除した状態を再コミットすれば、プッシュが実行できます。

参考ページ

reject.tokyo

HoloLens2でホロモンアプリを作る その23(Inspectorから時刻を打ち込んでデバッグを行う)

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

今回はInspectorから時刻を打ち込んでデバッグを行うメモです。

Inspectorから時刻を打ち込む

前回、時間経過でホロモンがお腹を空かせる実装を行いました。
bluebirdofoz.hatenablog.com

エディター上で様々なケースの動作確認を行おうとしましたが、DateTime 型はシリアライズできないため、Inspectorビューに表示できません
そこで以下の記事を参考に、DateTime 型を文字列で設定できるテスト用スクリプトを作成しました。
https://tempura-kingdom.jp/serializabledatetime/

以下が試しに作成したテスト用スクリプトです。
Inspector 上のボタンをクリックすると、入力した時刻情報で経過時間を算出する試験が実行されます。
・TestConditionLife.cs

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

namespace HMProject.HoloMonCondition
{
    [RequireComponent(typeof(HoloMonConditionLifeSingleton))]
    public class TestConditionLife : MonoBehaviour
    {
        const string p_TimeFormat = "yyyy-MM-dd HH:mm:ss";

        [SerializeField, Tooltip("参照テストクラス")]
        private HoloMonConditionLifeSingleton p_Target;

        [SerializeField, Tooltip("始点時刻")]
        private string p_StartTimeText;

        private DateTime p_StartTime;

        [SerializeField, Tooltip("終点時刻")]
        private string p_EndTimeText;

        private DateTime p_EndTime;


        /// <summary>
        /// 時刻計算処理を実行する
        /// </summary>
        public void ExecuteActivityElapsedTime()
        {
            int dayMinute = 0;

            // テキストの時刻を DateTime 型にパースする
            if (!DateTime.TryParse(p_StartTimeText, out p_StartTime))
            {
                Debug.Log("Parse Error : StartTime");
                p_StartTime = new DateTime();
            }
            // テキストの時刻を DateTime 型にパースする
            if (!DateTime.TryParse(p_EndTimeText, out p_EndTime))
            {
                Debug.Log("Parse Error : EndTime");
                p_EndTime = new DateTime();
            }
            dayMinute = p_Target.ActivityElapsedMinutes(p_StartTime, p_EndTime);

            Debug.Log("ExecuteActivityElapsedTime : " + dayMinute);
        }

        /// <summary>
        /// 開始処理
        /// </summary>
        void Start()
        {
            // デフォルトの時刻を設定する
            p_StartTime = DateTime.Now;
            p_EndTime = DateTime.Now.AddMinutes(30.0f);

            // 時刻をテキストでInspectorに表示する
            p_StartTimeText = p_StartTime.ToString(p_TimeFormat);
            p_EndTimeText = p_EndTime.ToString(p_TimeFormat);
        }
    }
}

・TestConditionLifeEditor.cs

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

using UnityEditor;

namespace HMProject.HoloMonCondition
{
# if UNITY_EDITOR
    // 拡張するクラスを指定する
    [CustomEditor(typeof(TestConditionLife))]
    // 継承クラスは Editor を設定する
    public class TestConditionLifeEditor : Editor
    {
        // GUIの表示関数をオーバーライドする
        public override void OnInspectorGUI()
        {
            // 元のインスペクター部分を表示
            base.OnInspectorGUI();

            // targetを変換して対象スクリプトの参照を取得する
            TestConditionLife testIns = target as TestConditionLife;

            // public関数を実行するボタンの作成
            if (GUILayout.Button("ExecuteActivityElapsedTimeの実行"))
            {
                testIns.ExecuteActivityElapsedTime();
            }
        }
    }
# endif
}

作成したテスト用スクリプトをオブジェクトに追加します。
f:id:bluebirdofoz:20210329075856j:plain

シーンを再生して動作を確認します。
Inspector にテキストを入力してボタンをクリックすると、入力した任意の時刻情報で動作を試験することができました。
f:id:bluebirdofoz:20210329075906j:plain

HoloLens2でホロモンアプリを作る その22(ホロモンが時間経過でお腹を空かせる)

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

今回はホロモンが時間経過でお腹を空かせるメモです。

活動時間の時間経過を計算する

単純に定期的な時間経過による処理の実行を行う場合は UniRx の Interval または Timer のオペレータを利用すると便利です。
・UniRxを使って一定間隔で処理を行う
https://bluebirdofoz.hatenablog.com/entry/2021/02/26/024511

ただし、今回に関しては以下の条件を満たしたいため、この手法は利用しませんでした。
・アプリを終了した後、再起動しても経過時間を考慮してお腹を空かせる
・睡眠中は空腹度が変化せず、起きている時間のみ、空腹が進行する

2つの時間から時間経過を求める場合、DateTime 型同士を減算して TimeSpan 型の戻り値を取得することができます。

// 始点時刻と終点時刻
DateTime p_StartTime = DateTime.Now;
DateTime p_EndTime = DateTime.Now.AddMinutes(30.0f);

// 経過時刻を計算する
TimeSpan SubtractionTime = a_EndTime - a_StartTime;

最終的に以下のようなスクリプトを作成しました。
・HoloMonConditionLifeSingleton.cs

f:id:bluebirdofoz:20210328231017j:plain

シーンを再生して動作を確認します。
時間経過に合わせて設定値通りにステータスが変化しました。
f:id:bluebirdofoz:20210328231006j:plain