Island Generation
The island generation system turns a seed and a set of tuning parameters into a playable island. It runs in two stages: first the tile stage - every grid cell gets a ground type and possibly a nature object; then the mesh stage - the tile layer is turned into a PolygonMesh with a HeightMap. Both stages are Burst‑compiled IJobs launched from static methods of IslandGeneration.
IslandGeneration
IslandGeneration is the central class responsible for the entire island generation pipeline. The bodies of nested types (Parameters, NoiseLayers, GenerateJob, etc.) are expanded in the following sections.
namespace DiceKingdoms.Game;
public static class IslandGeneration
{
public static float EPSILON;
public static float WATER_MAX_DEPTH;
public sealed class ConnectedComponents<T> : Il2CppSystem.ValueType
where T : new() { /* section "ConnectedComponents" */ }
[Serializable, StructLayout(LayoutKind.Explicit)]
public struct Parameters { /* section "Parameters" */ }
public sealed class References : Il2CppSystem.ValueType
{ /* section "References" */ }
[StructLayout(LayoutKind.Explicit)]
public struct NoiseLayers { /* section "NoiseLayers" */ }
public sealed class GenerateOutput : Il2CppSystem.ValueType
{ /* section "GenerateOutput" */ }
public sealed class GenerateJob : Il2CppSystem.ValueType
{ /* section "GenerateJob" */ }
public sealed class HeightMap : Il2CppSystem.ValueType
{ /* section "HeightMap" */ }
public struct Bounds { /* section "Bounds" */ }
public sealed class MeshOutput : Il2CppSystem.ValueType
{ /* section "MeshOutput" */ }
public sealed class MeshJob : Il2CppSystem.ValueType
{ /* section "MeshJob" */ }
public static GenerateOutput Generate(
ref Parameters parameters,
ref IEnumerable<InnerCliff.Key> innerCliffKeys,
ref int2 size,
ref uint seed);
public static MeshOutput BuildMesh(
IslandGrid.Layer<GroundTile> layer,
Biome biome,
int heightMapSubdivide,
PolygonMesh lavaTileMesh,
PolygonMesh pierTileMesh);
public static PolygonMesh BuildAltitudeMesh(
ref Parameters parameters,
float2 center,
int2 size,
ref Unity.Mathematics.Random random);
public static PolygonMesh BuildHeightMapMesh(
IslandGrid.Layer<GroundTile> layer,
Biome biome,
int heightMapSubdivide,
PolygonMesh lavaTileMesh,
PolygonMesh pierTileMesh);
public static void HeightMapFromMesh(
ref PolygonMesh mesh,
ref HeightMap heightMap);
public static void MaxFilter(
ref HeightMap heightMap,
ref float radius);
private static ConnectedComponents<T> ComputeConnectedComponents<T, P>(
UnsafeLayer<T> layer, P predicate, Allocator allocator)
where T : unmanaged;
}
Generation is split into two sequential stages.
The first stage - tiles. The entry point is Generate(ref parameters, ref innerCliffKeys, ref size, ref seed). First, NoiseLayers.Generate(...) is called: it fixes random offsets for the altitude and moisture noise and produces NoiseLayers - an immutable struct from which Altitude(position) and Moisture(position) can later be sampled. Then a GenerateJob is created, which in its Burst Execute() walks every grid cell and makes four passes:
- AssignGround - picks the ground type from altitude, moisture, and
HardLimit.
namespace DiceKingdoms.Game;
public enum GroundTile
{
WATER,
WATER_ROCK,
GRASS,
SAND,
INNER_CLIFF,
METEORITE,
PIER
}
- PlaceInnerCliffs - places inland cliffs using
numberMains,numberExtensions,minDistance,minSpacing. - PlaceBlobs - plants trees and rocks as clusters ("blobs"), filtered by
moistureRangeandminDistance. - PlaceDots - places point objects (fish) via a probability mask; flowers are distributed along the way by
flowerProbability. - ScoreTerritory - computes three scores (
buildSpace,naturalResources,cliffs) and their sum inScoreData.
Generate returns a GenerateOutput — a job handle. The caller can either Complete() it and pull the layers via GetGround / GetNature, or compare several GenerateOutputs via CompareTo (sorted by score) and keep the best.
The second stage - mesh. BuildMesh(layer, biome, …) takes the already classified tile layer and builds geometry. Inside MeshJob.Execute() it walks the four diagonals of each cell (Diagonal), classifies corners (CornerKind), and assembles the PolygonMesh. Height differences between neighboring cells become vertical walls via WallHorizontalStep and WallVerticalStep. The result is MeshOutput: the PolygonMesh itself and the HeightMap.
Both stages are fully deterministic: the same seed and Parameters produce the same island.
Parameters
Example mode:
IslandConfig - adds configuration for island generation parameters
IslandGeneration.Parameters carries every tunable value.
[Serializable]
public struct Parameters
{
[FieldOffset(0)] public HardLimit hardLimit;
[FieldOffset(8)] public Altitude altitude;
[FieldOffset(36)] public Moisture moisture;
[FieldOffset(44)] public Sand sand;
[FieldOffset(52)] public InnerCliffs innerCliffs;
[FieldOffset(76)] public Nature nature;
public struct HardLimit { /* section 1 */ }
public struct Altitude { /* section 2 */ }
public struct Moisture { /* section 3 */ }
public struct Sand { /* section 4 */ }
public struct InnerCliffs { /* section 5 */ }
public struct Nature { /* section 6 */ }
public struct Gaussian { public float average; public float deviation; }
}
1. HardLimit
public struct HardLimit
{
public int radius;
public int power;
public bool IsWithin(int2 size, int2 position);
}
| Field | Value | Meaning |
|---|---|---|
radius |
22 | Radius of the hard circular boundary (in tiles). Beyond it there is no land. |
power |
3 | no info |
IsWithin(size, position) is a predicate called for every cell. It cuts off everything outside the radius circle before any altitude thresholds kick in.
2. Altitude
public struct Altitude
{
public float noiseHorizontalOffsetScale;
public float noiseHorizontalScale;
public float distancePower;
public float noiseVerticalScale;
public float distanceVerticalScale;
public float verticalOffset;
public int islandMinSize;
}
| Field | Value | Meaning |
|---|---|---|
noiseHorizontalOffsetScale |
5 | noise offset — determines where hills and hollows land. |
noiseHorizontalScale |
0.05 | Horizontal noise scale. Larger - smoother terrain features. |
distancePower |
3.5 | Exponent of distance from the center. Larger it is, the flatter the interior and the steeper the shore. |
noiseVerticalScale |
0.4 | Amplitude of the vertical noise. |
distanceVerticalScale |
1.4 | How quickly height drops toward the island edge. |
verticalOffset |
0.61 | Global vertical shift. |
islandMinSize |
6 | Minimum island size in cells, smaller ones are rejected. |
3. Moisture
public struct Moisture
{
public float horizontalOffsetScale;
public float horizontalScale;
}
| Field | Value | Meaning |
|---|---|---|
horizontalOffsetScale |
1 | Moisture noise offset - shifts drought and humidity zones. |
horizontalScale |
0.05 | Moisture noise scale. Larger - bigger and smoother zones. |
4. Sand
public struct Sand
{
public float margin;
public int minSize;
}
| Field | Value | Meaning |
|---|---|---|
margin |
2 | Width of the sand strip along the shore (offset from water). |
minSize |
8 | Minimum size of a sand patch - prevents micro‑beaches. |
Sand is applied to cells whose height sits just above the water level within margin of the shore. minSize prunes isolated sand tiles.
5. InnerCliffs
public struct InnerCliffs
{
public int2 numberMains;
public int2 numberExtensions;
public float minDistance;
public float minSpacing;
}
| Field | Value | Meaning |
|---|---|---|
numberMains |
[2..3] |
(min, max) number of main cliff formations. |
numberExtensions |
[1..3] |
(min, max) number of branches per main cliff. |
minDistance |
8 | Minimum distance between neighboring cliffs. |
minSpacing |
5 | Minimum gap between branches within a single cliff. |
6. Nature
public struct Nature
{
public Blobs trees;
public Blobs rocks;
public Dots fish;
public float flowerProbability;
[Serializable] public struct Blobs { /* 6.1 */ }
[Serializable] public struct Dots { /* 6.2 */ }
}
6.1. Blobs
[Serializable]
public struct Blobs
{
public float2 moistureRange;
public float minDistance;
public Gaussian count;
public Gaussian size;
}
| Field | Value (Trees) | Value (Rocks) | Meaning |
|---|---|---|---|
moistureRange |
[-0.5 .. 1] |
[-1 .. 0.5] |
Humidity range in which this type can appear. |
minDistance |
4 | 5 | Minimum distance between neighboring blobs. |
count |
N(3.3, 0.4) |
N(8.5, 1) |
Number of blobs: mean and sigma of a Gaussian |
size |
N(30, 10) |
N(4, 0.8) |
Blob radius: mean and sigma. |
Trees and rocks are generated as blobs — clusters of same‑type cells rather than single tiles. count and size are Gaussian, so a single run produces natural variety. The moistureRange of trees and rocks overlaps — formally, both types may land on the same cell; PlaceBlobs resolves overlaps by iteration order.
6.2. Dots
[Serializable]
public struct Dots
{
public float idealMoisture;
public float maximumMoistureAmplitude;
public float probabilityAtIdealMoisture;
public float probabilityAtMaximumMoistureAmplitude;
public float probabilityPower;
public float maximumDistance;
public int validMarginDistance;
public int maxCount;
}
| Field | Value | Meaning |
|---|---|---|
idealMoisture |
0.4 | Humidity at which fish probability is maximal. |
maximumMoistureAmplitude |
0.3 | Maximum deviation from idealMoisture where fish is still possible. |
probabilityAtIdealMoisture |
0.6 | Fish probability at the ideal point (0..1). |
probabilityAtMaximumMoistureAmplitude |
0 | Probability at maximum deviation. |
probabilityPower |
1.5 | Curve exponent: how fast probability falls off with deviation. |
maximumDistance |
22 | Maximum distance from the shore where fish may appear. |
validMarginDistance |
2 | Allowed offset from the water edge. |
maxCount |
20 | Hard cap on the number of fish dots. |
6.3. Flowers
| Field | Value | Meaning |
|---|---|---|
flowerProbability |
0.05 | Probability of flowers on a suitable cell (0..1). |
Flowers are neither blobs nor dots — just a simple per‑tile probability. It applies to cells whose ground type is suitable.
7. Gaussian
public struct Gaussian
{
public float average;
public float deviation;
}
| Field | Meaning |
|---|---|
average |
Mean value. |
deviation |
Standard deviation (σ) of the normal distribution. |
A helper struct for count and size in Blobs. Sampling goes through Unity.Mathematics.Random so it works in Burst.
NoiseLayers
NoiseLayers is the deterministic sampler consumed by both stages.
[StructLayout(LayoutKind.Explicit)]
public struct NoiseLayers
{
[FieldOffset(0)] public readonly Parameters parameters;
[FieldOffset(168)] public readonly float2 center;
[FieldOffset(176)] public readonly int2 size;
[FieldOffset(184)] public readonly float2 altitudeOffset;
[FieldOffset(192)] public readonly float2 moistureOffset;
public float Distance(float2 p);
public float Altitude(float2 position);
public float Moisture(float2 position);
public static NoiseLayers Generate(
ref Parameters parameters, float2 center, int2 size,
ref Unity.Mathematics.Random random);
}
| Field | Meaning |
|---|---|
parameters |
A copy of Parameters |
center |
Island center in tile coordinates. |
altitudeOffset |
Random altitude noise offset, chosen once in Generate. |
moistureOffset |
Random moisture noise offset. |
GenerateOutput, GenerateJob and ScoreData
public sealed class GenerateOutput : Il2CppSystem.ValueType
{
public GenerateJob job;
public JobHandle handle;
public Il2CppStructArray<InnerCliff> InnerCliffs { get; }
public ScoreData Score { get; }
public GenerateOutput(GenerateJob job, JobHandle handle);
public void Complete();
public void GetGround(IslandGrid.Layer<GroundTile> ground);
public void GetNature(IslandGrid.Layer<Nullable<NatureTile>> nature);
public virtual int CompareTo(GenerateOutput other);
}
| Member | Meaning |
|---|---|
Complete() |
Wait for the job to finish. |
GetGround(ground) |
Copy the job result into the ground layer. |
GetNature(nature) |
Copy the job result into the nature layer. |
CompareTo(other) |
Compare by score — allows iterating several GenerateOutputs and keeping the best. |
ScoreData is a component‑wise quality estimate:
public struct ScoreData
{
public float buildSpace; // room for building
public float naturalResources; // trees, rocks, fish
public float cliffs; // inland cliffs
public float Total { get; } // sum of the three
}
GenerateJob is the Burst executor. Besides its fields (parameters, references, seed, ground, nature, innerCliffs, score) it carries a set of static helpers:
IsInIsland(distance, moistureRange)
Gaussian(ref displayClass)
Height(int2 position, ref displayClass)
Moisture(int2 position, ref displayClass)
GetGroundTile(GroundTile a, GroundTile b) // blending neighboring types
AssignGround(...) // ground type from alt/moisture/limit
PlaceInnerCliffs(...)
PlaceBlobs(...)
PlaceDots(...)
ScoreTerritory(...)
AssignGround is where a concrete GroundTile is picked from altitude, moisture, and HardLimit. It runs first because all subsequent nature passes filter by ground type.
BuildMesh, MeshOutput and MeshJob
public static MeshOutput BuildMesh(
IslandGrid.Layer<GroundTile> layer,
Biome biome,
int heightMapSubdivide,
PolygonMesh lavaTileMesh,
PolygonMesh pierTileMesh);
BuildMesh takes the classified tile layer and builds 3D geometry.
public sealed class MeshOutput : Il2CppSystem.ValueType
{
public MeshJob job;
public JobHandle handle;
public bool IsCompleted { get; }
public PolygonMesh Mesh { get; }
public HeightMap HeightMap { get; }
public MeshOutput(MeshJob job, JobHandle handle);
public void Complete();
}
| Member | Meaning |
|---|---|
IsCompleted |
Whether the job is ready. |
Mesh |
The built PolygonMesh. |
HeightMap |
The height map extracted from mesh. |
Complete() |
Wait for the job to finish. |
public sealed class MeshJob : Il2CppSystem.ValueType
{
public enum CornerKind : byte { EMPTY, STRAIGHT, CORNER, FULL }
public struct Diagonal
{
public int2 CenterToCornerOffset { get; }
public Angle Angle { get; }
public int X { get; }
public int Y { get; }
public Diagonal Next { get; }
public Diagonal Previous { get; }
public Diagonal Opposite { get; }
public int2 Corner(int2 center);
public int2 Center(int2 corner);
}
public struct Angle
{
public readonly byte value; // 0..255 -> 0..2π
public static Angle ZERO;
public static Angle PI;
public float Sin { get; }
public float Cos { get; }
// +, -, *, /, Lerp, ==, !=
}
public struct WallHorizontalStep { /* ... */ }
public struct WallVerticalStep { /* ... */ }
}
MeshJob walks the four diagonals of each cell. CornerKind classifies a corner by how the cell connects to its neighbors (EMPTY — isolated, FULL — connected on all sides). Angle is packed into a single byte (0..255 → 0..2π) with precomputed sin/cos — compact, cache‑friendly, and Burst‑safe. Vertical geometry on height differences is emitted via WallHorizontalStep and WallVerticalStep.
HeightMap
public sealed class HeightMap : Il2CppSystem.ValueType
{
public struct PositionsEnumerable { /* ... */ }
public int2 Size { get; }
public float this[float2 position] { get; }
public float this[int2 position] { get; set; }
public static HeightMap operator +(HeightMap a, HeightMap b);
public PositionsEnumerable Indexes(float threshold);
public HeightMap Copy(Allocator allocator);
}
| Member | Meaning |
|---|---|
Size |
Map size in cells. |
this[float2] / this[int2] |
Access to the height at a position. |
operator + |
Element‑wise addition of two height maps. |
Indexes(threshold) |
Enumerates all positions above a threshold. |
Copy(allocator) |
A copy of the map with a given allocator. |
Helper types
Bounds
public struct Bounds
{
public float2 min;
public float2 max;
public static Bounds EMPTY;
public Bounds(float2 min, float2 max);
public void Add(float2 point);
}
| Member | Meaning |
|---|---|
min, max |
AABB corners. |
EMPTY |
Empty bounds (for accumulation via Add). |
Add(point) |
Extend bounds so they include the point. |
ConnectedComponents
public sealed class ConnectedComponents<T> : Il2CppSystem.ValueType
where T : new()
{
public NativeList<Component> components;
public sealed class Component : Il2CppSystem.ValueType
{
public UnsafeList<int2> positions;
public void Add(int2 position);
}
public ConnectedComponents(Allocator allocator);
public void Add(Component component);
}
| Member | Meaning |
|---|---|
components |
List of found connected components. |
Add(position) |
Add a position to the component. |
A BFS over UnsafeLayer<T> grouping same‑type cells. Driven by a predicate P - the caller decides what counts as "connectivity". Used, for example, to reject nature blobs that are too small or severed pieces of the island.
References
public sealed class References : Il2CppSystem.ValueType
{
public UnsafeList<InnerCliff.Key> innerCliffKeys;
public References(IEnumerable<InnerCliff.Key> innerCliffKeys, Allocator allocator);
}
| Member | Meaning |
|---|---|
innerCliffKeys |
Cliff prefab keys used by PlaceInnerCliffs to pick a cliff type. |