うにてぃブログ

主にUnityとC#に関する記事を書いていきます

【Unity】乱数を管理する

UnityEngine.Random は InitState(seed) を利用すると乱数を固定化できる

しかし一度使うと、次に Seed を利用しない乱数を利用したい場合戻す処理をする必要がある

そのため、通常の乱数と Seed を指定した乱数でよしなに利用できるようなクラスを作成しました

使い方

RandomUtility.Seed

指定した Seed を利用する

RandomUtility.Clear

Seed を利用しないようにする

RandomUtility.Reset

指定した Seed を初期化する

RandomUtility.Range

RandomUtility.Seed を実行していた場合は seed を利用した乱数
Seed を指定してなかったり Clear を実行していたら 通常の乱数

また、第3引数に Seed 値を指定していた場合 その Seed を利用して乱数を取得する

サンプル

private void Start()
{
    var seed = 10;
    var index = 0;

    // Seed を指定
    RandomUtility.Seed(seed);
    Debug.Log($"Print Range {++index} {RandomUtility.Range(0, 100)}");
    Debug.Log($"Print Range {++index} {RandomUtility.Range(0, 100)}");

    RandomUtility.Reset(seed);
    // リセットしたので1回目と同じになる
    Debug.Log($"Print Range {++index} {RandomUtility.Range(0, 100)}");
    Debug.Log($"Print Range {++index} {RandomUtility.Range(0, 100)}");

    // Seed を利用しないようにしたので、毎回ここは値が変わる
    RandomUtility.Clear();
    Debug.Log($"Print Range {++index} {RandomUtility.Range(0, 100)}");
    Debug.Log($"Print Range {++index} {RandomUtility.Range(0, 100)}");

    RandomUtility.Reset(seed);

    // seed を指定しても実行できる
    Debug.Log($"Print Range {++index} {RandomUtility.Range(0, 100, seed)}");
    Debug.Log($"Print Range {++index} {RandomUtility.Range(0, 100, seed)}");
}

通常乱数を使う 5,6 だけ異なった値が表示されています

f:id:hacchi_man:20201128225052p:plain:h300f:id:hacchi_man:20201128225114p:plain:h300

コード

using System.Collections.Generic;

/// <summary>
/// 乱数を管理するクラス
/// </summary>
public static class RandomUtility
{
    private static readonly Dictionary<int, UnityEngine.Random.State> _dic;
    private static UnityEngine.Random.State _defaultState;
    private static UnityEngine.Random.State _cacheState;
    private static int? _currentSeed;
 
    static RandomUtility()
    {
        _dic = new Dictionary<int, UnityEngine.Random.State>();
        _defaultState = UnityEngine.Random.state;
        _currentSeed = null;
    }
 
    /// <summary>
    /// シードを使わないようにする
    /// </summary>
    public static void Clear()
    {
        _currentSeed = null;
    }
 
    /// <summary>
    /// シード値を変更する
    /// </summary>
    public static void Seed(int seed)
    {
        if (!_dic.ContainsKey(seed))
        {
            UnityEngine.Random.InitState(seed);
            _dic.Add(seed, UnityEngine.Random.state);
        }

        _currentSeed = seed;
    }
 
    /// <summary>
    /// シードの順番を最初からにする
    /// </summary>
    public static void Reset(int seed)
    {
        if (!_dic.ContainsKey(seed))
            return;

        UnityEngine.Random.InitState(seed);
        _dic[seed] = UnityEngine.Random.state;
        Seed(seed);
    }
 
    /// <summary>
    /// 乱数生成
    /// シードが指定してあればシードを利用して生成する
    /// </summary>
    public static int Range(int min, int max, int? seed = null)
    {
        _cacheState = UnityEngine.Random.state;
        var r = 0;
        if (seed.HasValue)
            Seed(seed.Value);

        UnityEngine.Random.state = _currentSeed.HasValue ? _dic[_currentSeed.Value] : _defaultState;
        r = UnityEngine.Random.Range(min, max);
        if (_currentSeed.HasValue)
            _dic[_currentSeed.Value] = UnityEngine.Random.state;
        else
            _defaultState = UnityEngine.Random.state;

        UnityEngine.Random.state = _cacheState;
 
        return r;
    }
}