Skip to content

Game.WindSimulationSystem

Assembly:
Namespace: Game.Simulation

Type: public class

Base: GameSystemBase, IDefaultSerializable, ISerializable

Summary:
Simulates 3D wind as a grid of volumetric cells (WindCell) using Unity Jobs / Burst for performance. The system maintains per-cell pressure and velocity, steps the simulation in two alternating job phases (velocity update and pressure update), integrates terrain and water surface heights to affect flow, supports serialization/deserialization, debug save/load to disk, and exposes APIs to read the cell buffer and to set a global wind/pressure. The system is intended for use in Cities: Skylines 2 modding as the engine-level wind field provider for other systems (e.g., weather, particles, and environment effects).


Fields

  • public struct WindCell
    Represents a single cell in the wind volume. Contains:
  • float m_Pressure — scalar pressure value for the cell.
  • float3 m_Velocities — velocity components (x, y, z) for the cell.
  • The struct implements ISerializable with Serialize/Deserialize methods to read/write pressure and velocities.

  • private struct UpdateWindVelocityJob (BurstCompiled)
    Job that updates per-cell velocities based on neighboring cell pressures, terrain/water heights and slowdown factors. Runs over each cell and:

  • Applies slowdown factors (air, terrain, vertical) and change factor to produce new velocity components.
  • Samples water/terrain height information via provided TerrainHeightData and WaterSurfaceData.
  • Writes updated WindCell values back into the NativeArray.

  • private struct UpdatePressureJob (BurstCompiled)
    Job that updates per-cell pressure from velocity divergence and applies boundary/edge driving forces based on the constant wind vector. Runs over every cell and:

  • Subtracts outgoing velocities, adds incoming neighbor velocities.
  • On edges, applies a computed driving pressure that depends on the constant wind, cell position and altitude layer.

  • public static readonly int kUpdateInterval
    Update frequency (in simulation ticks) for this system. Value: 512.

  • public static readonly int3 kResolution
    Grid resolution of the voxel wind field (x,y,z). Uses WindSystem.kTextureSize for x/y and 16 layers for z.

  • public static readonly float kChangeFactor
    Factor controlling how quickly pressure differences change velocities. Value: 0.02.

  • public static readonly float kTerrainSlowdown
    Slowdown multiplier applied near terrain. Value: 0.99.

  • public static readonly float kAirSlowdown
    General air slowdown per step. Value: 0.995.

  • public static readonly float kVerticalSlowdown
    Vertical velocity slowdown multiplier. Value: 0.9.

  • private SimulationSystem m_SimulationSystem
    Cached reference to the SimulationSystem from the World.

  • private TerrainSystem m_TerrainSystem
    Cached reference to the TerrainSystem used to read height data.

  • private WaterSystem m_WaterSystem
    Cached reference to the WaterSystem used to read water surface data.

  • private ClimateSystem m_ClimateSystem
    Cached reference to the ClimateSystem (present but not heavily used in this file).

  • private bool m_Odd
    Toggle used to alternate between velocity update and pressure update phases each frame the system runs.

  • private JobHandle m_Deps
    Current combined job dependency handle for outstanding jobs that operate on the wind cell buffer.

  • private NativeArray<WindCell> m_Cells
    Persistent NativeArray storing the entire wind grid (kResolution.x * kResolution.y * kResolution.z) of WindCell entries.

  • public float2 constantWind { get; set; }
    Public property representing global horizontal wind direction/magnitude used by the boundary pressure driver.

  • private float m_ConstantPressure { get; set; }
    Private property storing the base pressure value used when initializing/resetting cells.

Properties

  • public float2 constantWind { get; set; }
    Global, public horizontal wind vector used to seed and drive wind at the grid edges. Can be changed at runtime (SetWind method also sets pressure and resets cells).

  • private float m_ConstantPressure { get; set; }
    Internal pressure default used when setting/resetting the grid.

Constructors

  • public WindSimulationSystem()
    Default constructor. The system uses OnCreate to initialize subsystems and allocate the NativeArray; the constructor itself is present and marked with [Preserve] at runtime to prevent stripping.

Methods

  • public override int GetUpdateInterval(SystemUpdatePhase phase)
    Returns how often the system should be updated for the given phase. For GameSimulation phase it returns kUpdateInterval (512); for other phases it returns 1.

  • public unsafe byte[] CreateByteArray<T>(NativeArray<T> src) where T : struct
    Utility to copy a NativeArray into a managed byte[] using UnsafeUtility.MemCpy. Useful for debug dumps or serialization helpers.

  • public void DebugSave()
    Completes outstanding jobs, then writes the grid resolution and raw cell bytes to Application.streamingAssetsPath + "/wind_temp.dat" using a BinaryWriter. Useful for offline debugging.

  • public unsafe void DebugLoad()
    Completes outstanding jobs, reads raw bytes from streamingAssetsPath + "/wind_temp.dat" and copies them directly into the m_Cells NativeArray memory. Use with caution because it performs unsafe raw memory writes.

  • public void Serialize<TWriter>(TWriter writer) where TWriter : IWriter
    Writes the number of cells, the cells NativeArray, and the constantWind vector to the writer. Used by the engine save/load streaming.

  • public void Deserialize<TReader>(TReader reader) where TReader : IReader
    Reads saved data back into the system. Handles version checks and compatibility:

  • If reader.context.version <= Version.stormWater then it returns early (older versions unsupported).
  • Supports reading cell array length and contents if versions indicate compatibility, and reads constantWind if available; otherwise sets a default constantWind.

  • public void SetDefaults(Context context)
    Completes outstanding jobs and initializes all cells to the current m_ConstantPressure and velocities using constantWind (z velocity = 0). Called on reset/initialization.

  • public void SetWind(float2 direction, float pressure)
    Completes outstanding jobs, sets constantWind and m_ConstantPressure, then calls SetDefaults to apply the new wind/pressure across the grid.

  • public static float3 GetCenterVelocity(int3 cell, NativeArray<WindCell> cells)
    Compute an interpolated/center velocity for a cell by averaging the cell's velocity with those of negative-direction neighbors (to provide a center-sampled velocity). Helpful for sampling velocity at a voxel center.

  • public static float3 GetCellCenter(int index)
    Get world-space center position of the given flat index cell. Accounts for x/y cell location inside the horizontal CellMapSystem.kMapSize and maps z layers to world heights (100 + 1024 * layerNormalized).

  • public NativeArray<WindCell> GetCells(out JobHandle deps)
    Returns the internal NativeArray and outputs the current dependency JobHandle so callers can add their own dependencies (reader jobs) or complete before reading.

  • public void AddReader(JobHandle reader)
    Combine an external reader job handle with the system's m_Deps to ensure correct job dependency tracking.

  • [Preserve] protected override void OnCreate()
    System initialization:

  • Retrieves references to SimulationSystem, TerrainSystem, WaterSystem, ClimateSystem from the World.
  • Sets default constantWind (0.275, 0.275) and default pressure (40f).
  • Allocates the persistent NativeArray m_Cells sized to kResolution.x * kResolution.y * kResolution.z.

  • [Preserve] protected override void OnDestroy()
    Disposes the persistent NativeArray m_Cells. Ensures no memory leaks.

  • private WindCell GetCell(int3 position)
    Instance helper to read a WindCell from m_Cells using 3D coordinates.

  • public static WindCell GetCell(int3 position, NativeArray<WindCell> cells)
    Static safe lookup that converts 3D coordinates to flat index and returns default(WindCell) when out of bounds.

  • [Preserve] protected override void OnUpdate()
    Main update loop invoked by the engine:

  • Requires terrain heightmap to be present.
  • Alternates m_Odd each frame to schedule either UpdateWindVelocityJob or UpdatePressureJob.
  • When scheduling UpdateWindVelocityJob it fetches TerrainHeightData and WaterSurfaceData and schedules the job over the entire grid. Adds readers to water/terrain systems to manage dependencies.
  • When scheduling UpdatePressureJob it uses constantWind/10f for the driving wind.
  • Updates base.Dependency to the returned job handle so downstream systems in the same frame can depend on the wind results.

  • [Preserve] public WindSimulationSystem()
    Empty preserved constructor (same as the public constructor mentioned earlier).

Usage Example

// Example: initialize and set a new wind direction / pressure at runtime
[Preserve]
protected override void OnCreate()
{
    base.OnCreate();

    // The system will allocate m_Cells and set a default wind/pressure in OnCreate.
    // To change the wind vector and pressure at runtime:
    var windSystem = base.World.GetOrCreateSystemManaged<WindSimulationSystem>();
    windSystem.SetWind(new float2(0.5f, 0.0f), 50f);

    // If you need to read the cell array on the CPU:
    JobHandle deps;
    var cells = windSystem.GetCells(out deps);
    deps.Complete(); // ensure jobs are finished before accessing cells on main thread
    // read cells[index] as needed, then continue...
}