Skip to content

Game.GeometrySystem

Assembly:
{{ Likely Assembly-CSharp (game code). Use the assembly your mod references that contains Game.* types (Cities: Skylines 2 runtime). }}

Namespace: Game.Net

Type: class

Base: GameSystemBase

Summary:
GeometrySystem is the main system responsible for building and updating the runtime geometry for network pieces (edges/roads and nodes/intersections) in Cities: Skylines 2. It performs multi-stage geometry calculations using Unity's Jobs/Burst systems: initializing node geometry, calculating per-edge geometry (offsets, middle curves, slopes, sync targets), flattening and finishing edges against terrain, building node intersection geometry, copying results and finally updating NodeGeometry/EdgeGeometry component data and bounds. The system coordinates several high-performance jobs and uses many prefab/ composition lookup tables and buffers to compute accurate bezier curves, heights and bounds for rendering and collision. It also handles special cases such as roundabouts, middle connections, fixed node sizes, outside-connections and temporary/owned edges.
{{ Tips: Use this when inspecting or modifying net geometry logic in mods — avoid altering job logic unless you understand the math (beziers, offsets, slopes). }}


Fields

  • private TerrainSystem m_TerrainSystem
    {{ Holds a reference to the TerrainSystem used to sample terrain heights and height data. }}

  • private EntityQuery m_UpdatedEdgesQuery
    {{ Query matching edges that have changed (Updated) — used to schedule partial updates when the game is running. }}

  • private EntityQuery m_UpdatedNodesQuery
    {{ Query matching nodes that have changed (Updated) — used to schedule partial updates when the game is running. }}

  • private EntityQuery m_AllEdgesQuery
    {{ Query matching all EdgeGeometry entities — used for full rebuilds (e.g., when a save is loaded). }}

  • private EntityQuery m_AllNodesQuery
    {{ Query matching all NodeGeometry entities — used for full rebuilds. }}

  • private bool m_Loaded
    {{ Flag set on OnGameLoaded() to request a full rebuild of geometry on the next update. }}

  • private TypeHandle __TypeHandle
    {{ Internal structure that stores Entity/Component type handles and ComponentLookup/BufferLookup handles used by the jobs. It is assigned in OnCreateForCompiler and used when constructing job structs. }}


Properties

  • None (no public properties on this class).
    {{ GeometrySystem is a systems class that exposes no runtime properties; it coordinates jobs and component lookups internally. }}

Constructors

  • public GeometrySystem()
    {{ Default constructor; the real setup happens in OnCreate / OnCreateForCompiler. The constructor is decorated with [Preserve] in the source (to keep it alive during AOT/stripping). }}

Methods

  • protected override void OnCreate() : System.Void
    {{ Sets up references (gets the TerrainSystem) and prepares EntityQueries used by the system (m_UpdatedEdgesQuery, m_UpdatedNodesQuery, m_AllEdgesQuery, m_AllNodesQuery). This is where the system registers which components it works with. }}

  • protected override void OnGameLoaded(Context serializationContext) : System.Void
    {{ Called when a game is loaded — sets m_Loaded = true to force a full geometry rebuild on the next update cycle. }}

  • private bool GetLoaded() : System.Boolean
    {{ Helper that returns true once after OnGameLoaded has flagged the system; clears the flag so the full rebuild happens only once. }}

  • protected override void OnUpdate() : System.Void
    {{ Core orchestration method. If there are updated edges/nodes (or a full rebuild is requested), OnUpdate schedules a complex pipeline of Burst-compiled Jobs:

    • Gather matching edge entities (ToEntityListAsync)
    • Allocate buffers (AllocateBuffersJob)
    • Initialize node geometry (InitializeNodeGeometryJob)
    • Calculate edge geometry (CalculateEdgeGeometryJob) — heavy per-edge math to compute offsets, cut/cutoffs, conform lengths, limit slopes, account for owners/subnets, fixed node sizes, roundabouts, etc.
    • Flatten node geometry (FlattenNodeGeometryJob) and populate edge-height map for smoothing across temporary/owned edges
    • Finish edge geometry (FinishEdgeGeometryJob) — final slope/height limiting, bounding boxes and sampling terrain where required
    • Iteratively calculate node geometry (CalculateNodeGeometryJob) — executed two iterations to resolve dependencies and middle connections
    • Calculate intersection geometry (CalculateIntersectionGeometryJob)
    • Copy intersection geometry (CopyNodeGeometryJob)
    • Update NodeGeometry bounds (UpdateNodeGeometryJob)
    • Finally add a CPU height reader to the TerrainSystem with the final dependency handle.

    The method builds ComponentLookup/BufferLookup/ComponentTypeHandle parameters from __TypeHandle for each job and composes JobHandle dependencies carefully to ensure correct order. }} {{ Note: This method contains the orchestration pattern you should follow if extending or adding new intermediate jobs: prepare Lookups/Handles, create job structs with those handles, schedule the jobs with correct dependency chaining, and update base.Dependency. }}

  • Nested job structs (private, [BurstCompile])
    {{ The class contains many private nested job structs implementing the core geometry algorithms. High-level summaries:

  • InitializeNodeGeometryJob (IJobChunk): Initializes NodeGeometry entries based on adjacent edges, computes flatness/offset/position for nodes considering smoothing, owner/temp state and merge layers.

  • CalculateEdgeGeometryJob (IJobParallelForDefer): Per-edge heavy geometry calculation. Computes offsets at both ends, builds left/right bezier curves for start/end, conforms lengths, cuts ends, limits height deltas, computes StartNodeGeometry and EndNodeGeometry values for each edge. Contains many helper methods:
    • CalculateOffsets, CalculateFixedOffsets, OffsetAirspaceCurves, CutCurve, CalculateMiddleRadius, GetFixedNodeOffset, LimitHeightDelta, CalculateCutOffset, Cut, ConformLengths, CalculateCornerOffset, GetTopOwner, IntersectBounds, CheckOppositeSide, Position/Tangent helper, Intersect helpers, CheckCrosswalks, CompareLanes, GetLaneLimits, RequireTransition, etc.
  • AllocateBuffersJob (IJob): Resizes temporary lists and pre-allocates hash-map capacity used later in flattening/height mapping.
  • FlattenNodeGeometryJob (IJobChunk): For each node, compares and flattens node geometry against connected edges; produces an edge-height map used to blend heights for temporary/owned edges.
  • FinishEdgeGeometryJob (IJobParallelForDefer): Uses terrain height samples and the edge-height map to finalize edge bezier heights, limits middle heights (straighten or clamp depending on flags), computes bounds for edge geometry.
  • CalculateNodeGeometryJob (IJobParallelForDefer): Builds StartNodeGeometry and EndNodeGeometry for each edge using EdgeGeometry, NodeGeometry and prefab/composition data. Handles middle/side connections, roundabout radii, vertex sync targets and produces segments for nodes. Contains many helper methods for segment calculation, middle curve construction, connection types (side/middle), vertex sync target calculation, and utilities (FindMiddleNodePos, StartOffset/EndOffset, AdjustSegmentWidth, CalculateSegments, CalculateMiddleCurves, etc).
  • CalculateIntersectionGeometryJob (IJobParallelForDefer): For each edge, creates IntersectionData (Start/End middle curves and bounds) using node geometries, composition flags, terrain sampling where required.
  • CopyNodeGeometryJob (IJobParallelForDefer): Copies IntersectionData (computed in CalculateIntersectionGeometryJob) into the StartNodeGeometry/EndNodeGeometry components.
  • UpdateNodeGeometryJob (IJobChunk): After node-edge geometry has been computed, updates NodeGeometry.m_Bounds by combining Orphan bounds or connected edge node geometry bounds, including terrain sampling for lower/raise-to-terrain flags.

These jobs together form a robust and highly optimized pipeline for net geometry generation. }}

Usage Example

[Preserve]
protected override void OnCreate()
{
    base.OnCreate();
    // Example of what GeometrySystem does: get a reference to the TerrainSystem
    m_TerrainSystem = base.World.GetOrCreateSystemManaged<TerrainSystem>();

    // Prepare entity queries used later by OnUpdate
    m_UpdatedEdgesQuery = GetEntityQuery(ComponentType.ReadWrite<EdgeGeometry>(), ComponentType.ReadOnly<Updated>());
    m_UpdatedNodesQuery = GetEntityQuery(ComponentType.ReadWrite<NodeGeometry>(), ComponentType.ReadOnly<Updated>());
    m_AllEdgesQuery = GetEntityQuery(ComponentType.ReadWrite<EdgeGeometry>());
    m_AllNodesQuery = GetEntityQuery(ComponentType.ReadWrite<NodeGeometry>());
}

{{ YOUR_INFO: When modding or inspecting this system, prefer reading and understanding the individual job types before attempting changes. Many methods use low-level math (beziers, tangents, offsets) and composition data from prefabs — a small change in offsets or limits may visibly affect roads, tracks and intersections. If you extend the pipeline, make sure to mirror the scheduling/dependency pattern used in OnUpdate and to use ComponentLookup/BufferLookup created via the TypeHandle to avoid race conditions. }}