Game.Areas.RaycastJobs
Assembly:
Namespace: Game.Areas
Type: static class
Base: None
Summary:
Contains Burst-compiled jobs used by the area raycasting system. Provides:
- FindAreaJob (IJobParallelFor) — finds area entities intersected by raycasts (ground/overground), validates hits against various filters (placeholders, upgrades, prefab flags, area type masks) and accumulates valid RaycastResult entries.
- RaycastLabelsJob (IJobChunk) — tests label quads in area geometry against ray lines and accumulates hits for label-type raycasts.
These jobs are designed to run inside the ECS/jobified raycast pipeline used by Cities: Skylines 2 modding, leveraging NativeQuadTree searches, component lookups and Burst for high performance.
Fields
- None at the top-level static class.
This type is a static container for nested job structs (FindAreaJob and RaycastLabelsJob). All important state lives in the nested job structs' public fields.
Properties
- None.
Constructors
- None.
Methods
-
FindAreaJob.Execute(int index) : void
Entry point for the IJobParallelFor job that performs area raycasting for the input at the given index. It picks the corresponding RaycastInput, early-exits if the TypeMask doesn't include areas, prepares a ValidationData helper and either constructs a GroundIterator (if a terrain result exists for that index) or an OvergroundIterator, then iterates the NativeQuadTree to accumulate matching area RaycastResult values. -
ValidationData.ValidateResult(ref RaycastResult result) : bool (private nested method inside FindAreaJob)
Validates a candidate RaycastResult against multiple rules: - If not in editor mode, excludes prefabs flagged HiddenIngame.
- Walks owner chain using Owner components to determine whether main/sub-elements or upgrades should be considered according to flags in RaycastInput.
- Enforces TypeMask (Areas, StaticObjects, Labels, etc.) and the input's AreaTypeMask for areas.
- Handles sub-elements and NoMainElements flags.
-
For StaticObjects, delegates to CheckPlaceholder for placeholder/attachment resolution. Returns true when the result passes validation and should be accumulated.
-
ValidationData.CheckPlaceholder(ref Entity entity) : bool (private nested method inside FindAreaJob)
If Placeholders are allowed by RaycastInput flags, returns true. Otherwise, if the entity has a Placeholder component and an Attachment component, resolves the attachment -> attached prefab (if PrefabRef exists) and returns true; otherwise returns false. Used to exclude pure placeholder entities from results unless allowed. -
GroundIterator.Intersect(QuadTreeBoundsXZ bounds) : bool (private nested struct method)
Tests whether the 2D XZ bounds intersect the hit position of the stored RaycastResult (used for pruning quad-tree nodes). -
GroundIterator.Iterate(QuadTreeBoundsXZ bounds, AreaSearchItem areaItem) : void
Called for each area-triangle in the searched quad-tree node. It: - Skips areas that are in-space (using Space component).
- Tests the 2D triangle against the hit position.
- Constructs a RaycastResult for the area and calls ValidationData.ValidateResult.
-
If valid, accumulates the result via NativeAccumulator
.ParallelWriter. -
OvergroundIterator.Intersect(QuadTreeBoundsXZ bounds) : bool (private nested struct method)
Intersects the quad-tree bounds with the ray line segment. -
OvergroundIterator.Iterate(QuadTreeBoundsXZ bounds, AreaSearchItem areaItem) : void
For overground raycasts it: - Skips areas that are not in-space.
- Intersects the full triangle (Triangle3) with the ray line.
- Computes hit position, hit direction, normalized distance, and other hit metadata.
-
Runs ValidationData.ValidateResult and accumulates valid hits.
-
RaycastLabelsJob.Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) : void
IJobChunk implementation that iterates Geometry entities (one per area label), computes label rotation and transformed label quad vertices for each LabelExtents entry, and tests intersection between the label quad and each RaycastInput line. On hit it constructs a RaycastResult with proper normalized distance adjusted for label size/scale and accumulates it. -
RaycastLabelsJob.IJobChunk.Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) : void
Explicit interface implementation — delegates to the strongly typed Execute method above.
Usage Example
// Example: scheduling FindAreaJob to test areas against a set of ray inputs.
// Note: this is illustrative. The real production setup requires valid ECS lookups,
// a populated NativeQuadTree<AreaSearchItem, QuadTreeBoundsXZ>, terrain results array, etc.
int inputCount = ...;
var inputs = new NativeArray<RaycastInput>(inputCount, Allocator.TempJob);
// fill inputs[i] ...
// Prepare necessary ComponentLookup/BufferLookup handles from the System or SystemBase:
// ComponentLookup<Owner> ownerLookup = System.GetComponentLookup<Owner>(false);
// ComponentLookup<Space> spaceLookup = ...
// BufferLookup<Node> nodesLookup = ...
// BufferLookup<Triangle> trianglesLookup = ...
// ComponentLookup<PrefabRef> prefabRefLookup = ...
// ComponentLookup<AreaGeometryData> prefabAreaLookup = ...
// BufferLookup<InstalledUpgrade> installedUpgradesLookup = ...
// Prepare search tree and (optional) terrain results
NativeQuadTree<AreaSearchItem, QuadTreeBoundsXZ> searchTree = ...;
var terrainResults = new NativeArray<RaycastResult>(inputCount, Allocator.TempJob); // or sized appropriately
// Prepare accumulator for results (one accumulator used across job invocations)
var accumulator = new NativeAccumulator<RaycastResult>(inputCount, Allocator.TempJob);
var accumulatorWriter = accumulator.AsParallelWriter();
var findJob = new RaycastJobs.FindAreaJob
{
m_EditorMode = false,
m_Input = inputs,
m_OwnerData = ownerLookup,
m_SpaceData = spaceLookup,
m_PlaceholderData = placeholderLookup,
m_AttachmentData = attachmentLookup,
m_BuildingData = buildingLookup,
m_ServiceUpgradeData = serviceUpgradeLookup,
m_PrefabRefData = prefabRefLookup,
m_PrefabAreaData = prefabAreaLookup,
m_Nodes = nodesLookup,
m_Triangles = trianglesLookup,
m_InstalledUpgrades = installedUpgradesLookup,
m_SearchTree = searchTree,
m_TerrainResults = terrainResults,
m_Results = accumulatorWriter
};
// Schedule the job. The job internally uses the length of m_Input for modulo indexing,
// but schedule parallelism using the number of raycast invocations you want:
JobHandle handle = findJob.Schedule(inputCount, 64, default);
handle.Complete();
// After completion, read accumulated results from 'accumulator' using whatever API
// the NativeAccumulator provides (e.g., enumerate or copy to NativeArray). Then dispose:
// accumulator.Dispose();
// inputs.Dispose();
// terrainResults.Dispose();
// RaycastLabelsJob is a chunk job and typically constructed with component type handles
// and scheduled by the system that has an EntityQuery. Example pattern (conceptual):
var labelsJob = new RaycastJobs.RaycastLabelsJob
{
m_Input = inputs,
m_CameraRight = cameraRightVector,
m_EntityType = GetEntityTypeHandle(),
m_GeometryType = GetComponentTypeHandle<Geometry>(true),
m_LabelExtentsType = GetBufferTypeHandle<LabelExtents>(true),
m_Results = accumulatorWriter
};
// Then schedule as a chunk job using your EntityQuery:
// var labelsHandle = labelsJob.Schedule(query, default);
// labelsHandle.Complete();
Notes:
- Both jobs use many read-only component and buffer lookups (ComponentLookup