com.wallstop-studios.unity-helpers
Version:
Treasure chest of Unity developer tools
256 lines (182 loc) • 7.81 kB
Markdown
# Random Number Generators
**TL;DR:** Use `PRNG.Instance` for 10-15x faster random generation than `UnityEngine.Random`, with a rich API for vectors, colors, weighted selection, and more.
## Overview
Unity Helpers provides 15+ high-performance pseudo-random number generators (PRNGs) through a unified `IRandom` interface. All generators pass standard statistical tests and are optimized for game development workloads.
### Key Features
- **10-15x faster** than `UnityEngine.Random` (see [benchmarks](../../performance/random-performance.md))
- **Thread-safe** access via `PRNG.Instance` (thread-local)
- **Rich API** — vectors, colors, Gaussian distributions, weighted selection, subset sampling
- **Seedable** — reproducible results for replays and testing
- **IL2CPP compatible** — no reflection, AOT-safe
## Quick Start (60 Seconds)
```csharp
using WallstopStudios.UnityHelpers.Core.Random;
// Use the thread-local default (fastest)
IRandom random = PRNG.Instance;
// Basic generation
int number = random.Next(0, 100); // [0, 100)
float value = random.NextFloat(); // [0.0, 1.0)
bool coinFlip = random.NextBool();
uint bits = random.NextUint();
// Unity vectors
Vector2 point2D = random.NextVector2(-10f, 10f);
// Colors
Color randomColor = random.NextColor();
// Weighted selection
string[] items = { "Common", "Rare", "Epic" };
float[] weights = { 70f, 25f, 5f };
string selected = random.NextWeighted(items.Zip(weights, (x, y) => (x, y)));
// Gaussian distribution
float normalValue = random.NextGaussian(mean: 0f, stdDev: 1f);
```
## Choosing a Generator
| Use Case | Recommended Generator | Why |
| --------------------------- | --------------------- | ---------------------------------------------- |
| **General gameplay** | `PRNG.Instance` | Thread-local default, excellent quality |
| **Procedural generation** | `PcgRandom` | Reproducible, excellent statistical properties |
| **High-throughput effects** | `SplitMix64` | Fastest with good quality |
| **Cryptographic seeding** | N/A | Use `System.Security.Cryptography` instead |
| **Legacy compatibility** | `UnityRandom` | Matches `UnityEngine.Random` behavior |
## Available Generators
All generators implement the `IRandom` interface:
| Generator | Speed | Quality | Best For |
| ----------------------------- | --------- | --------- | ---------------------------------- |
| `LinearCongruentialGenerator` | Fastest | Poor | Non-critical effects only |
| `SplitMix64` | Very Fast | Very Good | High-throughput generation |
| `PcgRandom` | Fast | Excellent | General purpose, seeded generation |
| `IllusionFlow` | Fast | Excellent | Balanced speed and quality |
| `XoroShiroRandom` | Fast | Very Good | Game logic, physics |
| `RomuDuo` | Fast | Very Good | Alternative to PCG |
| `XorShiftRandom` | Moderate | Fair | Legacy compatibility |
| `WyRandom` | Moderate | Very Good | Hash-based scenarios |
| `SquirrelRandom` | Moderate | Good | Noise-based generation |
| `PhotonSpinRandom` | Slow | Excellent | Maximum quality needed |
| `UnityRandom` | Slow | Fair | Match Unity behavior |
| `SystemRandom` | Very Slow | Poor | .NET compatibility |
For detailed benchmarks, see [Random Performance](../../performance/random-performance.md).
## Creating Seeded Generators
For reproducible sequences (replays, procedural generation, testing):
```csharp
using WallstopStudios.UnityHelpers.Core.Random;
// Create with specific seed
PcgRandom seeded = new PcgRandom(seed: 12345);
// Generate reproducible sequence
for (int i = 0; i < 10; i++)
{
Debug.Log(seeded.Next(0, 100)); // Same values every run
}
// Different seed = different sequence
PcgRandom different = new PcgRandom(seed: 67890);
```
## API Reference
### Basic Generation
```csharp
IRandom random = PRNG.Instance;
// Integers
int value = random.Next(); // [int.MinValue, int.MaxValue]
int bounded = random.Next(100); // [0, 100)
int ranged = random.Next(10, 50); // [10, 50)
// Unsigned integers
uint bits = random.NextUint();
uint boundedUint = random.NextUint(1000u);
// Floating point
float f = random.NextFloat(); // [0.0, 1.0)
float rangedF = random.NextFloat(-1f, 1f); // [-1.0, 1.0)
double d = random.NextDouble(); // [0.0, 1.0)
// Boolean
bool b = random.NextBool(); // 50% true/false
bool weighted = random.NextBool(0.75f); // 75% true
```
### Vector Generation
```csharp
// 2D vectors
Vector2 v2 = random.NextVector2(); // Each component [0, 1)
Vector2 ranged2 = random.NextVector2(-10f, 10f); // Each component [-10, 10)
// 3D vectors
Vector3 v3 = random.NextVector3();
Vector3 ranged3 = random.NextVector3(-5f, 5f);
```
### Color Generation
```csharp
// Random colors
Color c = random.NextColor(); // Random RGBA
```
### Distributions
```csharp
// Gaussian (normal) distribution
float gaussian = random.NextGaussian(mean: 0f, stdDev: 1f);
// Weighted selection
string[] items = { "Common", "Rare", "Epic", "Legendary" };
float[] weights = { 60f, 25f, 12f, 3f };
string drop = random.NextWeighted(items.Zip(weights, (x, y) => (x, y)));
```
### Collection Operations
```csharp
// Shuffle in place
myList.Shuffle(random);
// Random element
T element = random.NextOf(array);
T element2 = random.NextOf(list);
// Random index
int index = random.Next(collection.Count);
```
## Thread Safety
`PRNG.Instance` provides thread-local instances, making it safe for multithreaded code without locks:
```csharp
// Safe - each thread gets its own instance
Parallel.For(0, 1000, i =>
{
int value = PRNG.Instance.Next(0, 100);
// No race conditions
});
```
For explicit thread-local control:
```csharp
using WallstopStudios.UnityHelpers.Core.Random;
// Create thread-local wrapper around any generator
ThreadLocalRandom<PcgRandom> threadLocal = new();
IRandom random = threadLocal.Value; // Per-thread instance
```
## Perlin Noise
For procedural generation, use the seedable Perlin noise generator:
```csharp
using WallstopStudios.UnityHelpers.Core.Random;
PerlinNoise noise = new PerlinNoise(seed: 42);
// 2D noise (terrain, textures)
float value2D = noise.Noise(x, y);
// Octave noise for more detail
float octaves = noise.OctaveNoise(x, y, octaves: 4, persistence: 0.5f);
```
## Best Practices
1. **Use `PRNG.Instance`** for most cases — it's fast, thread-safe, and well-tested
2. **Seed generators explicitly** when reproducibility matters (replays, tests)
3. **Avoid `new` in hot paths** — cache generator instances
4. **Don't use for security** — these are PRNGs, not CSPRNGs
```csharp
// ✅ Good - cache the reference
private IRandom _random = PRNG.Instance;
void Update()
{
float value = _random.NextFloat();
}
// ❌ Bad - creates new instance every frame
void Update()
{
PcgRandom random = new PcgRandom(); // Allocation!
float value = random.NextFloat();
}
```
## See Also
- [Random Performance Benchmarks](../../performance/random-performance.md)
- [Math & Extensions](./math-and-extensions.md)
- [README - Random Generators](../../readme.md#random-number-generators)