// 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); } } }