Unityと.NET環境で乱数生成を共通化する

タイトルの通り、Unity環境下での乱数生成と非Unityの乱数生成を共通化しようという話です。

何故このようなことをするかというと、非UnityのMonoBehaviorも何もない環境で作成したアルゴリズムをUnityへインポートしたりするときに、UnityのRandomクラスと、.NET 標準のRandomクラスの性能が異なるためそういったコードをUnityインポート後にRandomな部分の書き換えが発生するのを抑えたいというのが動機です。

早速コードです。

/// <summary>
/// [デバッグ用] Unityとそれ以外の.NET環境で乱数を共通化するためのクラス
/// </summary>
public static class Rand
{
#pragma warning disable IDE1006

#if false // ← これを環境によって自分で切り替える

    //
    // Unityで使用するコード
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    /// <summary>
    /// シート値を指定して乱数生成を初期化します。
    /// </summary>
    public static void Init(int seed) => Random.InitState(seed);

    /// <summary>
    /// 指定した整数の範囲内でランダムな値を取得します。maxは値に含まれない。
    /// </summary>
    public static int Next(int min, int max) => Random.Range(min, max);

    /// <summary>
    /// 指定した整数の範囲内でランダムな値を取得します。maxは値に含まれない。
    /// </summary>
    public static int Range(int min, int max) => Random.Range(min, max);

    /// <summary>
    /// 指定した浮動小数の範囲内でランダムな値を取得します。maxは値に含まれます。
    /// </summary>
    public static float Range(float min, int max) => Random.Range(min, max);

    /// <summary>
    /// 0.0f ~ 1.0fまでの範囲でランダムな値を取得します。
    /// </summary>
    public static float Value => Random.value; // .NETの一般的な命名規則との互換用

    /// <summary>
    /// 0.0f ~ 1.0fまでの範囲でランダムな値を取得します。
    /// </summary>
    public static float value => Value;
#else
    //
    // Unity以外の通常のシステムで使用するコード
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    private static Random _r = new Random();

    /// <summary>
    /// シート値を指定して乱数生成を初期化します。
    /// </summary>
    public static void Init(int seed) => _r = new Random(seed);

    /// <summary>
    /// 指定した整数の範囲内でランダムな値を取得します。maxは値に含まれない。
    /// </summary>
    public static int Next(int min, int max) => _r.Next(min, max);

    /// <summary>
    /// 指定した整数の範囲内でランダムな値を取得します。maxは値に含まれない。
    /// </summary>
    public static int Range(int min, int max) => _r.Next(min, max);

    /// <summary>
    /// 指定した浮動小数の範囲内でランダムな値を取得します。maxは値に含まれます。
    /// </summary>
    public static float Range(float min, int max) => throw new NotSupportedException();

    /// <summary>
    /// 0.0f ~ 1.0fまでの範囲でランダムな値を取得します。
    /// </summary>
    public static float Value => (float)_r.NextDouble();

    /// <summary>
    /// 0.0f ~ 1.0fまでの範囲でランダムな値を取得します。
    /// </summary>
    public static float value => Value; // Unityの命名規則の互換用
#endif

#pragma warning restore IDE1006
}

同じシグネチャーのメソッドをUnity用、それ以外用の両方でifdefで区切って定義します。

これで冒頭の「#if false」の部分をUnityならtrue、それ以外ならfalseとして各々のコードから使用します。

環境ごとに中身の実装が違うのでちょっと乱数の出かたが異なり(floatの範囲選択が.NET 環境ではできないですが…)Randクラスを使用する事で冒頭の書き換えるなどの課題が解消できると思います。