// MIT License - Copyright (c) 2023 wallstop
// Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE
namespace WallstopStudios.UnityHelpers.Core.Random
{
using System;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using ProtoBuf;
///
/// An adapter over UnityEngine.Random exposing the interface.
///
///
///
/// Uses Unity's global random state. If constructed with a seed, it initializes the global state via
/// UnityEngine.Random.InitState. Without a seed, it reads from whatever global state Unity maintains.
///
/// Pros:
///
/// - Parity with Unity's Random for projects relying on its behavior.
/// - Easy substitution of Unity's RNG with the unified interface.
///
/// Cons:
///
/// - Global shared state; can be modified by other code calling UnityEngine.Random.
/// - Not thread-safe and generally slower than high-performance PRNGs.
/// - Determinism depends on controlling Unity's global state elsewhere in your project.
///
/// When to use:
///
/// - When you must preserve Unity.Random behavior or interact with code that depends on it.
///
/// When not to use:
///
/// - General-purpose gameplay randomness—prefer or a concrete PRNG like PCG.
///
///
///
///
/// using WallstopStudios.UnityHelpers.Core.Random;
///
/// // Explicitly seed Unity's global RNG
/// var unityRng = new UnityRandom(seed: 2024);
/// int roll = unityRng.Next(1, 7);
///
/// // Note: calling UnityEngine.Random elsewhere will affect this sequence.
///
///
[RandomGeneratorMetadata(
RandomQuality.Fair,
"Mirrors UnityEngine.Random (Xorshift196 + additive); suitable for legacy compatibility but not high-stakes simulation.",
"Unity Random Internals",
"https://unity.com/blog/technology/random-numbers-on-the-gpu"
)]
[Serializable]
[DataContract]
[ProtoContract]
public sealed class UnityRandom : AbstractRandom
{
public static readonly UnityRandom Instance = new();
public override RandomState InternalState
{
get
{
unchecked
{
return new RandomState(
(ulong)(_seed ?? 0),
gaussian: _seed != null ? 0.0 : null,
payload: null,
bitBuffer: _bitBuffer,
bitCount: _bitCount,
byteBuffer: _byteBuffer,
byteCount: _byteCount
);
}
}
}
[ProtoMember(6)]
private readonly int? _seed;
public UnityRandom()
: this(null) { }
public UnityRandom(int? seed)
{
if (seed != null)
{
_seed = seed.Value;
UnityEngine.Random.InitState(seed.Value);
}
}
[JsonConstructor]
public UnityRandom(RandomState internalState)
{
unchecked
{
_seed = internalState.Gaussian != null ? (int)internalState.State1 : null;
RestoreCommonState(internalState);
}
}
public override uint NextUint()
{
return unchecked((uint)UnityEngine.Random.Range(int.MinValue, int.MaxValue));
}
public override IRandom Copy()
{
// Clone from full InternalState to preserve reservoirs and cached values
return new UnityRandom(InternalState);
}
}
}