MRが楽しい

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

MRTKでHoloLens2の空間メッシュの詳細レベルや範囲を動的に切り替える その2

本日はMRTKの小ネタ枠です。
MRTKで空間メッシュの詳細レベルや範囲を動的に切り替える方法を記事にします。
今回はメッシュの範囲を切り替える方法です。

SpatialAwarenessの設定

MRTKを利用してHoloLens2アプリを作成する場合、空間メッシュはSpatialAwarenessの設定で管理されます。
SpatialAwarenessの範囲をスクリプトから操作するにはSpatialAwarenessMeshObserverのObservationExtentsにアクセスします。
learn.microsoft.com
learn.microsoft.com

空間メッシュの観測範囲に関連する設定項目は以下の3つです。

Is Stationary Observer

観測点を固定するかユーザーと一緒に移動するかを切り替えます。
trueの場合、[Observation Extents]で定義されたボリュームを持つ観測点は起動時の原点に残り続けます。
falseの場合、観測点はユーザーの頭に追従します。

Observer Shape

空間メッシュを観測するときに使用するボリュームのタイプを定義します。
サポートされているオプションは次のとおりです。

タイプ 詳細
Axis Aligned Cube アプリ起動時のワールド座標系の軸に整列した矩形形状
User Aligned Cube ユーザーのローカル座標系に合わせて回転する矩形形状
Sphere ワールド空間の原点を中心とする球状。[Observation Extents]のX値が球の半径になります

Observation Extents

空間メッシュが観測される観測点からの距離を定義します。

サンプルプロジェクトの作成

MRTKを使った基本プロジェクトの環境構築手順は以下の記事を参照ください。
bluebirdofoz.hatenablog.com

今回はデフォルトの DefaultMixedRealityToolkitConfigurationProfile で作成したプロジェクトを利用します。

サンプルスクリプト

SpatialAwarenessの観測範囲を切り替える関数を持った以下のスクリプトを作成しました。
・SpatialAwarenessRangeSwitch.cs

using UnityEngine;

using Microsoft.MixedReality.Toolkit;
using Microsoft.MixedReality.Toolkit.XRSDK.OpenXR;

public class SpatialAwarenessRangeSwitch : MonoBehaviour
{
    public void SwitchSmall()
    {
        // 利用可能なOpenXRメッシュオブザーバーを取得する
        var openXRSpatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<OpenXRSpatialAwarenessMeshObserver>();

        // 0.5m x 0.5m x 0.5m の範囲を観察する
        openXRSpatialAwarenessMeshObserver.ObservationExtents = new Vector3(0.5f, 0.5f, 0.5f);
    }
    public void SwitchMiddle()
    {
        // 利用可能なOpenXRメッシュオブザーバーを取得する
        var openXRSpatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<OpenXRSpatialAwarenessMeshObserver>();

        // 1m x 1m x 1m の範囲を観察する
        openXRSpatialAwarenessMeshObserver.ObservationExtents = new Vector3(1, 1, 1);
    }
    
    public void SwitchLarge()
    {
        // 利用可能なOpenXRメッシュオブザーバーを取得する
        var openXRSpatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<OpenXRSpatialAwarenessMeshObserver>();

        // 3m x 3m x 3m の範囲を観察する
        openXRSpatialAwarenessMeshObserver.ObservationExtents = new Vector3(3, 3, 3);
    }
}

作成したサンプルスクリプトを適当なオブジェクトにアタッチし、シーンに配置したボタンから関数を呼び出せるようにしました。


HoloLens2上での動作確認

アプリケーションをビルドしてHoloLens2にデプロイします。
ボタン操作で空間メッシュの観測距離を切り替えることができれば成功です。

3m x 3m x 3m(Large)


0.5m x 0.5m x 0.5m(Small)

TextMeshProのSpacingOpetionを使って行間や文字間隔を調整する

本日はUnityの小ネタ枠です。
TextMeshProのSpacingOpetionを使って行間や文字間隔を調整する方法です。

SpacingOpetion

TextMeshProのSpacingOpetionでは以下の行間または文字間隔の設定を行えます。
・Character:文字間隔
・Line:行間
・Word:単語ごとの間隔
・Paragraph:段落ごとの間隔

行間または文字間隔

CharactorまたはLineの値を変更して行間または文字間隔を調整できます。

Character

文字間隔の広さを変更します。値が大きいほど文字間隔が広くなります。負値を設定すると文字が重なるほど狭くなります。


Line

行間の広さを変更します。値が大きいほど行間が広くなります。負値を設定すると文字が重なるほど狭くなります。


単語または段落ごとの間隔

WordまたはParagraphの値を変更して単語または段落ごとの間隔を調整できます。

Word

単語ごとの文字間隔の広さを変更します。値が大きいほど文字間隔が広くなります。

Paragraph

段落ごとの文字間隔の広さを変更します。値が大きいほど文字間隔が広くなります。

MRTKでHoloLens2の空間メッシュの詳細レベルや範囲を動的に切り替える

本日はMRTKの小ネタ枠です。
MRTKで空間メッシュの詳細レベルや範囲を動的に切り替える方法を記事にします。
今回はメッシュの詳細レベルを切り替える方法です。

SpatialAwarenessの設定

MRTKを利用してHoloLens2アプリを作成する場合、空間メッシュはSpatialAwarenessの設定で管理されます。
SpatialAwarenessの詳細レベルをスクリプトから操作するにはSpatialAwarenessMeshObserverのLevelOfDetailにアクセスします。
learn.microsoft.com
learn.microsoft.com

使用しているXRプラグインによって取得すべきMeshObserverの種類が変わることに注意が必要です。

// Unity OpenXR pluginを選択している場合
var openXRSpatialAwarenessMeshObserver =
    CoreServices.GetSpatialAwarenessSystemDataProvider<OpenXRSpatialAwarenessMeshObserver>();
openXRSpatialAwarenessMeshObserver.LevelOfDetail = SpatialAwarenessMeshLevelOfDetail.Fine;

// Built-in Unity pluginsを選択している場合
var mixedRealitySpatialAwarenessMeshObserver =
    CoreServices.GetSpatialAwarenessSystemDataProvider<IMixedRealitySpatialAwarenessMeshObserver>();
mixedRealitySpatialAwarenessMeshObserver.LevelOfDetail = SpatialAwarenessMeshLevelOfDetail.Fine;

サンプルプロジェクトの作成

MRTKを使った基本プロジェクトの環境構築手順は以下の記事を参照ください。
bluebirdofoz.hatenablog.com

今回はデフォルトの DefaultMixedRealityToolkitConfigurationProfile で作成したプロジェクトを利用します。

サンプルスクリプト

SpatialAwarenessの詳細レベルを[表示(Visible)][オクルージョン(Occlusion)][非表示(None)]に切り替える関数を持った以下のスクリプトを作成しました。
・SpatialAwarenessStatusSwitch.cs

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

using Microsoft.MixedReality.Toolkit;
using Microsoft.MixedReality.Toolkit.SpatialAwareness;
using Microsoft.MixedReality.Toolkit.XRSDK.OpenXR;

public class SpatialAwarenessStatusSwitch : MonoBehaviour
{
    public void SwitchCoarse()
    {
        // 利用可能なOpenXRメッシュオブザーバーを取得する
        var openXRSpatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<OpenXRSpatialAwarenessMeshObserver>();

        // 荒い(Coarse)に設定する
        openXRSpatialAwarenessMeshObserver.LevelOfDetail = SpatialAwarenessMeshLevelOfDetail.Coarse;
    }
    
    public void SwitchMedium()
    {
        // 利用可能なOpenXRメッシュオブザーバーを取得する
        var openXRSpatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<OpenXRSpatialAwarenessMeshObserver>();

        // 中(Medium)に設定する
        openXRSpatialAwarenessMeshObserver.LevelOfDetail = SpatialAwarenessMeshLevelOfDetail.Medium;
    }
    
    public void SwitchFine()
    {
        // 利用可能なOpenXRメッシュオブザーバーを取得する
        var openXRSpatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<OpenXRSpatialAwarenessMeshObserver>();

        // 細かい(Fine)に設定する
        openXRSpatialAwarenessMeshObserver.LevelOfDetail = SpatialAwarenessMeshLevelOfDetail.Fine;
    }

    public void SwitchCustom1()
    {
        // 利用可能なOpenXRメッシュオブザーバーを取得する
        var openXRSpatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<OpenXRSpatialAwarenessMeshObserver>();

        // カスタム(Custom)の1立法メートル辺り1ポリゴンに設定する
        openXRSpatialAwarenessMeshObserver.LevelOfDetail = SpatialAwarenessMeshLevelOfDetail.Custom;

        openXRSpatialAwarenessMeshObserver.TrianglesPerCubicMeter = 1;
    }

    public void SwitchCustom10()
    {
        // 利用可能なOpenXRメッシュオブザーバーを取得する
        var openXRSpatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<OpenXRSpatialAwarenessMeshObserver>();

        // カスタム(Custom)の1立法メートル辺り10ポリゴンに設定する
        openXRSpatialAwarenessMeshObserver.LevelOfDetail = SpatialAwarenessMeshLevelOfDetail.Custom;
        openXRSpatialAwarenessMeshObserver.TrianglesPerCubicMeter = 10;
    }
}

作成したサンプルスクリプトを適当なオブジェクトにアタッチし、シーンに配置したボタンから関数を呼び出せるようにしました。


HoloLens2上での動作確認

アプリケーションをビルドしてHoloLens2にデプロイします。
ボタン操作で空間メッシュの詳細レベルを切り替えることができれば成功です。

荒い(Coarse)


中(Medium)


細かい(Fine)


カスタム(Custom)

今回カスタム(Custom)の設定についてはメッシュの詳細レベルが変化せず、機能しませんでした。
以下のドキュメントページによると「全てプラットフォームで適用されることは保証されません」とあるため、HoloLens2かつOpenXR pluginを利用している環境では適用されない可能性が高いです。
learn.microsoft.com

MRTKでHoloLens2の空間メッシュの表示方法や有効無効を動的に切り替える その2

本日はMRTKの小ネタ枠です。
MRTKで空間メッシュの表示方法や有効無効を動的に切り替える方法を記事にします。
今回は空間メッシュ自体の有効無効を切り替える方法です。

前回記事

以下の前回記事の続きです。
bluebirdofoz.hatenablog.com

SpatialAwarenessの設定

MRTKを利用してHoloLens2アプリを作成する場合、空間メッシュはSpatialAwarenessの設定で管理されます。
SpatialAwarenessの有効無効を切り替えるにはCoreServices.SpatialAwarenessSystemにアクセスします。
learn.microsoft.com

// 全てのメッシュオブザーバを有効にする
CoreServices.SpatialAwarenessSystem.Enable();

// 全てのメッシュオブザーバを無効にする
CoreServices.SpatialAwarenessSystem.Disable();

空間メッシュの有効無効は全てのメッシュオブザーバで共通管理されるため、使用しているXRプラグインに関わらず、CoreServices.SpatialAwarenessSystemから変更可能です。

サンプルスクリプト

新たにSpatialAwarenessの有効無効を切り替える関数を持った以下のスクリプトを作成しました。
・SpatialAwarenessEnableSwitch.cs

using UnityEngine;

using Microsoft.MixedReality.Toolkit;

public class SpatialAwarenessEnableSwitch : MonoBehaviour
{
    public void SwitchEnable()
    {
        // 全てのメッシュオブザーバを有効にする
        CoreServices.SpatialAwarenessSystem.Enable();
    }

    public void SwitchDisable()
    {
        // 全てのメッシュオブザーバを無効にする
        CoreServices.SpatialAwarenessSystem.Disable();
    }
}

作成したサンプルスクリプトを適当なオブジェクトにアタッチし、シーンに配置したボタンから関数を呼び出せるようにしました。


HoloLens2上での動作確認

アプリケーションをビルドしてHoloLens2にデプロイします。
ボタン操作で空間メッシュの有効無効を切り替えることができれば成功です。

前回記事の表示切替との違いは有効無効切り替えの場合、メッシュそのものを無効化しているため当たり判定も消失します。
一方で表示設定を無効(None)にした場合は当たり判定はそのまま残っています。

MRTKでHoloLens2の空間メッシュの表示方法や有効無効を動的に切り替える

本日はMRTKの小ネタ枠です。
MRTKで空間メッシュの表示方法や有効無効を動的に切り替える方法を記事にします。

SpatialAwarenessの設定

MRTKを利用してHoloLens2アプリを作成する場合、空間メッシュはSpatialAwarenessの設定で管理されます。
SpatialAwarenessの表示設定をスクリプトから操作するにはSpatialAwarenessMeshObserverのDisplayOptionにアクセスします。
learn.microsoft.com
learn.microsoft.com

使用しているXRプラグインによって取得すべきMeshObserverの種類が変わることに注意が必要です。

// Unity OpenXR pluginを選択している場合
var openXRSpatialAwarenessMeshObserver =
    CoreServices.GetSpatialAwarenessSystemDataProvider<OpenXRSpatialAwarenessMeshObserver>();

// Built-in Unity pluginsを選択している場合
var mixedRealitySpatialAwarenessMeshObserver =
    CoreServices.GetSpatialAwarenessSystemDataProvider<IMixedRealitySpatialAwarenessMeshObserver>();

サンプルプロジェクトの作成

MRTKを使った基本プロジェクトの環境構築手順は以下の記事を参照ください。
bluebirdofoz.hatenablog.com

今回はデフォルトの DefaultMixedRealityToolkitConfigurationProfile で作成したプロジェクトを利用します。

サンプルスクリプト

SpatialAwarenessの表示設定を[表示(Visible)][オクルージョン(Occlusion)][非表示(None)]に切り替える関数を持った以下のスクリプトを作成しました。
・SpatialAwarenessDisplaySwitch.cs

using UnityEngine;

using Microsoft.MixedReality.Toolkit;
using Microsoft.MixedReality.Toolkit.SpatialAwareness;
using Microsoft.MixedReality.Toolkit.XRSDK.OpenXR;

public class SpatialAwarenessDisplaySwitch : MonoBehaviour
{
    public void SwitchVisible()
    {
        // プラグインの選択でUnity OpenXR pluginを選択している場合、以下のコードでメッシュの表示を切り替えることができる
        // 利用可能なOpenXRメッシュオブザーバーを取得する
        var openXRSpatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<OpenXRSpatialAwarenessMeshObserver>();

        // 表示(Visible)に設定する
        openXRSpatialAwarenessMeshObserver.DisplayOption = SpatialAwarenessMeshDisplayOptions.Visible;
        
        // プラグインの選択でBuilt-in Unity pluginsを選択している場合、以下のコードでメッシュの表示を切り替えることができる
        // 利用可能なMixedRealityメッシュオブザーバーを取得する
        var mixedRealitySpatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<IMixedRealitySpatialAwarenessMeshObserver>();

        // 表示(Visible)に設定する
        mixedRealitySpatialAwarenessMeshObserver.DisplayOption = SpatialAwarenessMeshDisplayOptions.Visible;
    }

    public void SwitchOcclusion()
    {
        // プラグインの選択でUnity OpenXR pluginを選択している場合、以下のコードでメッシュの表示を切り替えることができる
        // 利用可能なOpenXRメッシュオブザーバーを取得する
        var openXRSpatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<OpenXRSpatialAwarenessMeshObserver>();

        // オクルージョン(Occlusion)に設定する
        openXRSpatialAwarenessMeshObserver.DisplayOption = SpatialAwarenessMeshDisplayOptions.Occlusion;
        
        // プラグインの選択でBuilt-in Unity pluginsを選択している場合、以下のコードでメッシュの表示を切り替えることができる
        // 利用可能なMixedRealityメッシュオブザーバーを取得する
        var mixedRealitySpatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<IMixedRealitySpatialAwarenessMeshObserver>();

        // オクルージョン(Occlusion)に設定する
        mixedRealitySpatialAwarenessMeshObserver.DisplayOption = SpatialAwarenessMeshDisplayOptions.Occlusion;
    }

    public void SwitchNone()
    {
        // プラグインの選択でUnity OpenXR pluginを選択している場合、以下のコードでメッシュの表示を切り替えることができる
        // 利用可能なOpenXRメッシュオブザーバーを取得する
        var openXRSpatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<OpenXRSpatialAwarenessMeshObserver>();

        // 非表示(None)に設定する
        openXRSpatialAwarenessMeshObserver.DisplayOption = SpatialAwarenessMeshDisplayOptions.None;
        
        // プラグインの選択でBuilt-in Unity pluginsを選択している場合、以下のコードでメッシュの表示を切り替えることができる
        // 利用可能なMixedRealityメッシュオブザーバーを取得する
        var mixedRealitySpatialAwarenessMeshObserver =
            CoreServices.GetSpatialAwarenessSystemDataProvider<IMixedRealitySpatialAwarenessMeshObserver>();

        // 非表示(None)に設定する
        mixedRealitySpatialAwarenessMeshObserver.DisplayOption = SpatialAwarenessMeshDisplayOptions.None;
    }
}

作成したサンプルスクリプトを適当なオブジェクトにアタッチし、シーンに配置したボタンから関数を呼び出せるようにしました。


HoloLens2上での動作確認

アプリケーションをビルドしてHoloLens2にデプロイします。
ボタン操作で空間メッシュの表示を切り替えることができれば成功です。

UniTaskを使って徐々に変化するゲージバーを作成する その2(連続で呼び出された場合は前の処理を省略する)

本日はUniTaskの小ネタ枠です。
UniTaskを使って徐々に変化するゲージバーを作成する方法を記事に残します。
今回は少し応用して連続で呼び出された場合は前の処理を省略するコードを作成します。

前回記事

以下の前回記事の続きです。
bluebirdofoz.hatenablog.com

UniTask.YieldのcancellationToken引数

UniTask.YieldのcancellationToken引数にCancellationTokenSourceを引き渡すことで任意のタイミングでOperationCanceledExceptionを発生させることができます。

// キャンセルトークンを作成
var _cancelTokenSource = new CancellationTokenSource();

try
{
    while (true)
    {
        // フレームごとの実行処理
                
        // キャンセル(_cancelTokenSource?.Cancel())が要求された場合にOperationCanceledExceptionを発生させて即座に処理を終了する
        await UniTask.Yield(cancellationToken: _cancelTokenSource.Token);
    }
}
catch (OperationCanceledException)
{
    // キャンセルされた場合に実行する処理
}
finally
{
    // 最後に必ず実行する処理
}

サンプルスクリプト
前回作成したスクリプトを改修して連続して増加関数を呼び出すと、ひとつ前のアニメーションを省略して次のアニメーションに即座に移行するようにしました。
・SmoothStretchGaugeTest2.cs

using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;

public class SmoothStretchGaugeTest2 : MonoBehaviour
{
    /// <summary>
    /// 全体のサイズ
    /// </summary>
    [SerializeField] private RectTransform _totalRectTransform;
    
    /// <summary>
    /// 青ゲージ
    /// </summary>
    [SerializeField] private RectTransform _blueRectTransform;

    /// <summary>
    /// 赤ゲージ
    /// </summary>
    [SerializeField] private RectTransform _redRectTransform;

    /// <summary>
    /// 現在の青ゲージの比率
    /// </summary>
    [SerializeField] private float _currentBlueRatio = 0.0f;

    /// <summary>
    /// アニメーションの排他制御
    /// </summary>
    private SemaphoreSlim _semaphoreSlim = new (1,1);

    /// <summary>
    /// キャンセル用トークン
    /// </summary>
    private CancellationTokenSource _cancelTokenSource;
    
    async void Start()
    {
        ChangeGauge(_currentBlueRatio);
        
        // 2秒後にゲージを増加させる
        await UniTask.Delay(TimeSpan.FromSeconds(2));
        ChangeGaugeAnimationAsync(30).Forget();
        
        // 2秒後にゲージを増加させる
        await UniTask.Delay(TimeSpan.FromSeconds(2));
        ChangeGaugeAnimationAsync(30).Forget();
    }

    public async UniTask ChangeGaugeAnimationAsync(
        int incrementRatio,
        float tweenTime = 5.0f)
    {
        float animationTime = 0;

        try
        {
            // ひとつ前の処理で実行中のキャンセルトークンが存在する場合
            // 即座にキャンセルを呼び出してひとつ前の処理を終了する
            _cancelTokenSource?.Cancel();

            // アニメーション中は排他制御を行い、アニメーションが終わるまで必ず待機する
            await _semaphoreSlim.WaitAsync();
            
            // 本スレッド用にキャンセルトークンを作成
            _cancelTokenSource = new CancellationTokenSource();
            
            // アニメーションの開始地点と終了地点を計算する
            var animationStartRatio = _currentBlueRatio;
            var animationEndRatio = _currentBlueRatio + incrementRatio;

            // キャンセルに備えて現在値は先に更新しておく
            _currentBlueRatio = animationEndRatio;

            while (animationTime < tweenTime)
            {
                // ゲージを徐々に変化させる
                animationTime += Time.deltaTime;
                var progressRatio = (int)Mathf.Lerp(animationStartRatio, animationEndRatio,
                    animationTime / tweenTime);
                ChangeGauge(progressRatio);
                
                // キャンセルが要求された場合は即座に処理を終了する
                await UniTask.Yield(cancellationToken: _cancelTokenSource.Token);
            }

            ChangeGauge(animationEndRatio);
        }
        catch (OperationCanceledException)
        {
            // キャンセルされた場合は最終値に更新する
            ChangeGauge(_currentBlueRatio);
        }
        finally
        {
            // 本スレッドのキャンセルトークンを解放する
            _cancelTokenSource = null;
            
            // アニメーションが終わったら排他制御を解除する
            _semaphoreSlim.Release();
        }
    }

    private void ChangeGauge(float blueRatio)
    {
        var totalGaugeWidth = _totalRectTransform.rect.width;
        var blueGaugeWidth = totalGaugeWidth * blueRatio / 100.0f;
        var redGaugeWidth = totalGaugeWidth - blueGaugeWidth;
        
        _blueRectTransform.offsetMin = new Vector2(0, 0);
        _blueRectTransform.offsetMax = new Vector2(-1.0f * redGaugeWidth, 0);

        _redRectTransform.offsetMin = new Vector2(blueGaugeWidth, 0);
        _redRectTransform.offsetMax = new Vector2(0, 0);
    }
}

シーンを再生すると自動で2秒間隔でゲージの増加関数が呼びされます。

2秒経過するとゲージの増加が始まりますが、その2秒後、即座に現在のアニメーションが省略されて次のアニメーションに移行します。

UniTaskを使って徐々に変化するゲージバーを作成する

本日はUniTaskの小ネタ枠です。
UniTaskを使って徐々に変化するゲージバーを作成する方法を記事に残します。

前回記事

今回は以下の前回記事で作成したゲージバーのサンプルシーンを利用します。
bluebirdofoz.hatenablog.com

UniTaskの環境構築手順は以下を参照ください。
bluebirdofoz.hatenablog.com

UniTask.Yield

UniTaskが戻り値の関数であればUniTask.Yieldを使ってフレーム待機することができます。
これを使って関数内で簡単にフレーム待機の処理を制御できます。

public async UniTask WaitFrame()
{
    // 現在のフレーム番号を取得する
    var startFrame = Time.frameCount;

    // 1フレームだけ待機する
    await UniTask.Yield(PlayerLoopTiming.Update, new CancellationToken());

    // 経過フレームを表示する
    Debug.Log($"現在の経過フレーム:{Time.frameCount - startFrame}");
}

サンプルスクリプト

前回作成したスクリプトを改修して増加関数を呼び出すとゲージを少しずつ増加させるようにしました。
・SmoothStretchGaugeTest.cs

using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;

public class SmoothStretchGaugeTest : MonoBehaviour
{
    /// <summary>
    /// 全体のサイズ
    /// </summary>
    [SerializeField] private RectTransform _totalRectTransform;
    
    /// <summary>
    /// 青ゲージ
    /// </summary>
    [SerializeField] private RectTransform _blueRectTransform;

    /// <summary>
    /// 赤ゲージ
    /// </summary>
    [SerializeField] private RectTransform _redRectTransform;

    /// <summary>
    /// 現在の青ゲージの比率
    /// </summary>
    [SerializeField] private float _currentBlueRatio = 0.0f;

    /// <summary>
    /// アニメーションの排他制御
    /// </summary>
    private SemaphoreSlim _semaphoreSlim = new (1,1);

    void Start()
    {
        ChangeGauge(_currentBlueRatio);
    }
    
    [ContextMenu("IncrementGauge")]
    public void IncrementGauge()
    {
        var nextRatio = _currentBlueRatio + 10;
        if (nextRatio > 100)
        {
            nextRatio = 100;
        }

        ChangeGaugeAnimationAsync((int)nextRatio).Forget();
    }

    public async UniTask ChangeGaugeAnimationAsync(
        int changeRatio,
        float tweenTime = 1.0f)
    {
        float animationTime = 0;

        // アニメーション中は排他制御を行い、アニメーションが終わるまで待機する
        await _semaphoreSlim.WaitAsync();

        var animationStartRatio = _currentBlueRatio;
        var animationEndRatio = changeRatio;

        while (animationTime < tweenTime)
        {
            // ゲージを徐々に変化させる
            animationTime += Time.deltaTime;
            var progressRatio = (int)Mathf.Lerp(animationStartRatio, animationEndRatio,
                animationTime / tweenTime);
            ChangeGauge(progressRatio);
            await UniTask.Yield();
        }

        ChangeGauge(animationEndRatio);
        _currentBlueRatio = changeRatio;
        
        // アニメーションが終わったら排他制御を解除する
        _semaphoreSlim.Release();
    }

    private void ChangeGauge(float blueRatio)
    {
        var totalGaugeWidth = _totalRectTransform.rect.width;
        var blueGaugeWidth = totalGaugeWidth * blueRatio / 100.0f;
        var redGaugeWidth = totalGaugeWidth - blueGaugeWidth;
        
        _blueRectTransform.offsetMin = new Vector2(0, 0);
        _blueRectTransform.offsetMax = new Vector2(-1.0f * redGaugeWidth, 0);

        _redRectTransform.offsetMin = new Vector2(blueGaugeWidth, 0);
        _redRectTransform.offsetMax = new Vector2(0, 0);
    }
}

シーンを再生してIncrementGauge関数を呼び出します。

フレームに合わせてゲージが増加することでスムーズにゲージが変化します。