⏮️

【UE5】Wrap演算子を使って特定の範囲だけラップしようとしたが上手くいかなかった話

2024/12/10に公開

概要

この記事は一人アドベントカレンダー by ダリアの10日目の記事です。

今回はWrap演算子という便利そうなノードを見つけたのですが、
うまく使えそうになかったので、このノードは何なのかを調べたことをまとめた記事なります。

Wrap演算子とはスクショ通りのものになります。

使うまではMinからMaxより下の値に丸めてくれる機能なのかと思って使いましたが、MaxのときだけMaxの値が返ってきてどうなっているんだろうと思い式をソースコードから調べました。
内部的な式はこうなっています。

template< class T >
[[nodiscard]] static constexpr FORCEINLINE T Wrap(const T X, const T Min, const T Max)
{
    // Use unsigned type for integers to allow for large ranges which don't overflow on subtraction
    // We don't do that for floating point types because there are no unsigned versions of those.  We
    // could bump up to the next size up (though there's no `long double` on some platforms), but such
    // values near the extremes are unlikely.
    using SizeType = typename std::conditional_t<std::is_integral_v<T>, std::make_unsigned<T>, TIdentity<T>>::type;

    // Our asserts are not constexpr-friendly yet
    // checkSlow(Min <= Max);

    SizeType Size = (SizeType)Max - (SizeType)Min;
    if (Size == 0)
    {
        // Guard against zero-sized ranges causing division by zero.
        return Max;
    }

    T EndVal = X;
    if (EndVal < Min)
    {
        SizeType Mod = FMath::Modulo((SizeType)((SizeType)Min - (SizeType)EndVal), Size);
        EndVal = (Mod != (T)0) ? (T)((SizeType)Max - Mod) : Min;
    }
    else if (EndVal > Max)
    {
        T Mod = FMath::Modulo((SizeType)((SizeType)EndVal - (SizeType)Max), Size);
        EndVal = (Mod != (T)0) ? (T)((SizeType)Min + Mod) : Max;
    }
    return EndVal;
}

template <typename T>
[[nodiscard]] static constexpr FORCEINLINE T Modulo(T Value, T Base)
{
    if constexpr (std::is_floating_point_v<T>)
    {
        return FMath::Fmod(Value, Base);
    }
    else
    {
        return Value % Base;
    }
}


以下の式を見ると、例えば最小値が0,最大値が2の場合に-1を入れると「2-1=1」になり、返ってくる値は1になります。

if (EndVal < Min)
{
    SizeType Mod = FMath::Modulo((SizeType)((SizeType)Min - (SizeType)EndVal), Size);
    EndVal = (Mod != (T)0) ? (T)((SizeType)Max - Mod) : Min;
}

次にMaxである2を入れた場合は、Maxを超えないため2が返ってきます。

else if (EndVal > Max)
{
    T Mod = FMath::Modulo((SizeType)((SizeType)EndVal - (SizeType)Max), Size);
    EndVal = (Mod != (T)0) ? (T)((SizeType)Min + Mod) : Max;
}
return EndVal;

式としてはなるほどとなりましたが、期待通りの使い方ができずに残念だったので参考させていただいたサイトの方法を取ろうと思います。
きちんと使ってみて式も見ることも大事だと思わされた内容でした。

短いですが記事はここまでです。
ここまで読んでいただきありがとうございます。

環境

UE5.4.4

参考

Wrap (Integer) Not Picking Min Value

Discussion