Game.ZoneUtils
Assembly: Assembly-CSharp (typical for game code — may vary by game version)
Namespace: Game.Zones
Type: public static class
Base: System.Object
Summary:
Utility helper methods and constants used to work with zone "blocks" and their cell/grid geometry in Cities: Skylines 2. ZoneUtils provides conversions between block-local grid indices and world positions/rotations, computes block corners and bounds, evaluates adjacency/shared-cell relationships between blocks, determines road cell directions, computes color indices for cell rendering, and exposes constants that define cell size and zone limits. These methods operate on game types such as Block, ValidArea, BuildOrder, CellFlags, ZoneType, Quad2 and Bounds2 and rely heavily on Unity.Mathematics and MathUtils helpers.
Fields
-
public const float CELL_SIZE = 8f
Cell side length used by zoning grid. Most position/index math divides or multiplies by this constant. -
public const float CELL_AREA = 64f
Precomputed cell area (CELL_SIZE * CELL_SIZE). Useful for area calculations. -
public const int MAX_ZONE_WIDTH = 10
Maximum zone width in cells used by the zoning system. -
public const int MAX_ZONE_DEPTH = 6
Maximum zone depth in cells used by the zoning system. -
public const int MAX_ZONE_TYPES = 339
Maximum number of zone types supported by the system (used for indexing color tables, etc).
Properties
This static utility class defines no properties.
Constructors
None
ZoneUtils is a static class and has no instance constructors. All members are static.
Methods
-
public static float3 GetPosition(Block block, int2 min, int2 max)
Returns the world-space center position for a sub-cell block area described by the margins (min, max). The algorithm computes an offset from block.m_Position using block.m_Direction and MathUtils.Right(block.m_Direction) scaled by the block size and margins. Useful when you need the world position of a specific cell or a region inside the block. -
public static quaternion GetRotation(Block block)
Returns a rotation quaternion that orients objects to the block's forward direction. Uses quaternion.LookRotation with block.m_Direction projected to XZ plane and math.up() as the up vector. -
public static Quad2 CalculateCorners(Block block)
Calculates the four corner points (as a Quad2) of the rectangular area occupied by the block in XZ space. Accounts for block.m_Position, block.m_Direction and block.m_Size. Useful for collision bounds, gizmos, or rendering overlays. -
public static Quad2 CalculateCorners(Block block, ValidArea validArea)
Variant that calculates corners taking into account a ValidArea (m_Area) margin inside the block. Uses vector shuffle operations to compute the clipped quad corners. Use when you need the inner valid placement area rather than the full block footprint. -
public static Bounds2 CalculateBounds(Block block)
Returns an axis-aligned 2D bounds (Bounds2) that encloses the block's rotated rectangle. The method computes the absolute extents by projecting the size onto both block.m_Direction and its right vector. -
public static int2 GetCellIndex(Block block, float2 position)
Converts a world XZ position to a cell index (int2) relative to the block. Projects the vector from block position to the target position onto the block's right and forward directions, adds an offset based on block size, and divides by CELL_SIZE (8) with floor to get discrete cell indices. -
public static float3 GetCellPosition(Block block, int2 cellIndex)
Inverse of GetCellIndex for a single cell index: returns world-space center position for the specified cell index within the block. Uses block size and direction vectors similarly to GetPosition. -
public static bool CanShareCells(Block block1, Block block2, BuildOrder buildOrder1, BuildOrder buildOrder2)
Determines whether two blocks may share cells given their BuildOrder precedence. If orders differ, it defers to CanShareCells(blockA, blockB) with the earlier build-order block first. If build orders are identical, returns false. -
public static bool CanShareCells(Block block1, Block block2)
Checks whether two blocks can share cells (i.e., not overlapping in a way that prevents shared usage). Steps: - Tests if their directions are nearly perpendicular (by dot product thresholds ~ cos(1°) and sin(1°)); if so, they cannot share.
- Computes relative offsets, adjusts for odd-sized blocks via bit checks, normalizes to cell units, and tests fractional alignment to ensure cells align on a half-cell grid. Returns true if fractional offsets are far enough from 0.5 (with tolerance 0.48), indicating cell boundaries align to allow sharing.
Note: uses thresholds 0.017452406f (≈sin 1°) and 0.9998477f (≈cos 1°) for directional comparisons.
-
public static bool IsNeighbor(Block block1, Block block2, BuildOrder buildOrder1, BuildOrder buildOrder2)
Determines whether two blocks are neighbors given build-order precedence; delegates to IsNeighbor(blockA, blockB) with appropriate order. Returns false if build orders equal. -
public static bool IsNeighbor(Block block1, Block block2)
Checks if two blocks are adjacent in the forward/back direction with aligned orientations. It: - Ensures directions are near-parallel.
- Computes offset along forward and right axes, compensating for block depths (size.y) to check adjacency.
-
Converts offset to cell units and tests whether the right-axis offset equals half the sum of both widths (within tolerance 0.02) and forward offset is within 0.02. If so, blocks are neighbors.
-
public static int GetColorIndex(CellFlags state, ZoneType type)
Maps a cell state (CellFlags) and zone type to an index used for coloring/visualization. Computes a base index from the zone type and visibility/shared flags, then adds 0/1/2 if Occupied/Selected flags are set to yield a small color palette index. Useful for overlay rendering. -
public static int GetCellWidth(float roadWidth)
Returns the width in cells required for a given roadWidth. Performs ceil(roadWidth / CELL_SIZE - 0.01) to avoid floating rounding edge cases. -
public static CellFlags GetRoadDirection(Block target, Block source)
Determines the road direction flags for a source block relative to a target block based only on geometric orientation. Computes dot products between directions and the target/right vectors, picks which pair of flags corresponds to the greater alignment and handles sign to pick the correct direction flag values (uses integer flag constants in code: 4,512,2048,1024). -
public static CellFlags GetRoadDirection(Block target, Block source, CellFlags directionFlags)
Variant that takes an existing set of directionFlags to mask which direction tests are relevant. Uses dot products similarly to the simpler variant, but returns combination of flags from a selected int4 based on signs and comparisons. This method computes a bitset (CellFlags) using math.csum over selected flag elements.
Notes for both GetRoadDirection overloads: they compute which cardinal/relative cell direction (forward/back/left/right) a source block occupies relative to a target, returning CellFlags suitable for marking road cells.
Usage Example
// Example usage inside a mod or zone manager:
Block block = GetSomeBlock(); // your code to obtain a Block
int2 min = new int2(0, 0);
int2 max = new int2(1, 1);
// World position of a sub-area in the block
float3 pos = ZoneUtils.GetPosition(block, min, max);
// Rotation aligned to block direction
quaternion rot = ZoneUtils.GetRotation(block);
// Block corners and AABB
Quad2 corners = ZoneUtils.CalculateCorners(block);
Bounds2 bounds = ZoneUtils.CalculateBounds(block);
// Convert world position -> cell index, and back
float2 worldXZ = new float2(pos.x, pos.z);
int2 cellIndex = ZoneUtils.GetCellIndex(block, worldXZ);
float3 cellCenter = ZoneUtils.GetCellPosition(block, cellIndex);
// Check adjacency/share
bool canShare = ZoneUtils.CanShareCells(blockA, blockB);
bool neighbor = ZoneUtils.IsNeighbor(blockA, blockB);
// Determine color index for rendering overlay
int colorIndex = ZoneUtils.GetColorIndex(cellFlags, zoneType);
// Get road cell width and direction flags
int widthCells = ZoneUtils.GetCellWidth(roadWidth);
CellFlags dir = ZoneUtils.GetRoadDirection(targetBlock, sourceBlock);
Additional notes: - The utility methods use small numeric tolerances (e.g., 0.02, 0.48, and ~1° angle thresholds). These are important when replicating or adjusting logic — changing them can alter adjacency/share detection behavior. - This class depends on game/internal types (Block, ValidArea, BuildOrder, CellFlags, ZoneType, Quad2, Bounds2, MathUtils). Their internals and availability can change across game versions; double-check signatures when updating mods. - All methods are pure/functional with respect to their inputs (no side effects), so they are safe to call from most contexts but be mindful of thread-safety related to the game engine (e.g., do not call Unity API that requires main thread).