.. _customRuntimeSegment: Custom Runtime Segment Generation ================================= Overview -------- The **DOTS Traffic City** framework provides a flexible pipeline for generating road network topology completely at runtime. Instead of relying purely on pre-baked authoring assets in the Unity Editor, you can programmatically construct straight road segments, build complex crossroads, and link pedestrian paths on the fly. This approach is ideal for procedural city generation, dynamic map chunk loading, or custom runtime road creation tools. Key Components & Utilities --------------------------- * **RuntimeGenerationUtils**: A static helper class containing low-level path calculations, Bezier curve evaluations, straight segment math, and automated crossroad topology generation. * **TrafficNodeData**: A lightweight runtime data structure representing an entry/exit boundary node of a road segment. It defines lane counts, widths, crosswalk settings, and position/rotation transforms for connecting adjacent roads. * **RuntimeGenerationPool**: A zero-allocation object pool used to pop and recycle ``RuntimeSegmentCustom``, ``TrafficNodeData``, and ``PathData`` instances during runtime operations. * **RuntimeRoadManagerCustom**: The runtime manager responsible for registering generated road segments into the live DOTS simulation world. Segment Connection Principles ----------------------------- Adjacent road segments seamlessly snap together at their boundary ``TrafficNodeData`` nodes. When connecting two segments (e.g., a straight road segment and an intersection): * **Position**: The boundary nodes of both connecting segments must share the exact same world position. * **Rotation**: The boundary nodes must have opposite (inverted) rotations—facing 180 degrees away from each other. This inverse rotation aligns incoming lanes from one segment directly with outgoing lanes of the adjacent segment. Generating Straight Roads ------------------------- Straight road segments connect two boundary traffic nodes (Start & End) and can consist of straight lanes, crosswalks, and optional pedestrian side paths along the segment. To generate a straight road segment, create two boundary nodes, define guide path waypoints, and call ``RuntimeGenerationUtils.GenerateStraightSegment()``: .. code-block:: csharp // 1. Pop or instantiate a new custom segment var newSegment = runtimeGenerationPool.PopSegment(); // 2. Instantiate and configure Start (index 0) and End (index 1) boundary nodes for (int i = 0; i < 2; i++) { var node = runtimeGenerationPool.PopTrafficNode(); node.Owner = newSegment; node.LocalNodeIndex = i; node.Position = nodePositions[i]; node.Rotation = nodeRotations[i]; // Facing opposite to entrance vector node.LaneCount = 2; node.LaneWidth = 3.75f; newSegment.Nodes.Add(node); } // 3. Define path waypoints (e.g. start position, intermediate spline points, end position) List waypoints = new List { startPos, endPos }; // 4. Configure straight segment parameters var straightSettings = new RoadSceneUtils.StraightSegmentSettings() { LaneCount = 2, LaneWidth = 3.75f, IsOneWay = false, IsEndOfOneWay = false, AddCrosswalk = true, AddPedestrianNodes = true, AddPedestrianAlongLine = true, PedestrianNodeSpacing = 5f }; float speedLimit = 60f; // Speed limit in km/h // 5. Generate parallel lane paths, crosswalks, and pedestrian routes RuntimeGenerationUtils.GenerateStraightSegment(newSegment, waypoints, straightSettings, speedLimit); .. note:: Straight road segments always require exactly **two** boundary nodes in ``newSegment.Nodes`` (index 0 for Start, index 1 for End). Generating Auto Crossroads --------------------------- The framework provides automated crossroad generation via ``RuntimeGenerationUtils.GenerateAutoCrossroad()``. There are two primary workflows for generating an intersection segment: 1. Standard Crossroad Setup ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Populate a ``RuntimeSegmentCustom`` instance with pre-configured boundary nodes (``TrafficNodeData``) explicitly defining the intersection's outer entry/exit points, then pass the segment to the generator: .. code-block:: csharp // 1. Pop or instantiate a new custom segment var newSegment = runtimeGenerationPool.PopSegment(); // 2. Instantiate and configure boundary nodes with correct positions, rotations, and lane settings for (int i = 0; i < nodeCount; i++) { var node = runtimeGenerationPool.PopTrafficNode(); node.Owner = newSegment; node.LocalNodeIndex = i; // Assign correct world position and facing direction (opposite to entrance) node.Position = nodePositions[i]; node.Rotation = nodeRotations[i]; // Facing opposite to entrance vector node.LaneCount = 2; node.LaneWidth = 3.75f; // Add boundary node to segment newSegment.Nodes.Add(node); } // 3. Configure shared light state container and enable traffic light signal control for intersection nodes newSegment.LightStateContainer = baseLightContainer; newSegment.AddTrafficLights(); // 4. Generate internal paths, lane connections, U-turns, and crosswalks RuntimeGenerationUtils.GenerateAutoCrossroad(newSegment, generationSettings); .. note:: Calling ``newSegment.AddTrafficLights()`` marks the intersection's traffic nodes to operate under traffic light control modes and assigns light indices to connected lane paths. .. important:: Before calling ``GenerateAutoCrossroad(newSegment, ...)``, the ``newSegment.Nodes`` list **must already be populated** with valid ``TrafficNodeData`` instances containing precise world positions and orientations. 2. Stitching Existing Connected Straight Roads ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can also stitch together an intersection from existing straight road segments whose endpoint nodes already meet at the junction center: .. code-block:: csharp // Collect boundary nodes from straight roads pointing to the junction List connectedStraightRoadNodes = GetConnectedEndpointNodes(); // Generate the crossroad by connecting existing boundary nodes RuntimeSegmentCustom newIntersection = RuntimeGenerationUtils.GenerateAutoCrossroad( connectedStraightRoadNodes, baseLightContainer, generationSettings, areExistsNodes: true ); // Add the newly created intersection segment to your segment list newSegments.Add(newIntersection); .. note:: Setting ``areExistsNodes: true`` tells the utility to reuse the boundary transformations and configuration of existing nodes rather than instantiating new ones. This ensures seamless lane stitching between pre-existing straight roads and the new crossroad. Sample Scene: Runtime Generation -------------------------------- The framework includes a dedicated sample scene demonstrating dynamic road segment generation from raw data and intersection creation at runtime: * **Location**: ``Spirit604/DotsCity/Samples/RuntimeGenerationSample/RuntimeGenerationSample.unity`` * **Script**: ``RuntimeGenerationSample.cs`` .. note:: **Demonstration Notice**: The road creators (``RoadSegmentCreator``) and scene objects present in this sample scene are used **strictly as data containers** to extract raw parameters (such as waypoint positions, lane widths, and connection rules) for API demonstration purposes. You are **not limited** to using these editor components. The runtime generation API is entirely decoupled from scene MonoBehaviours, allowing you to feed your own custom data sources (e.g., procedural grid generators, tilemap databases, external JSON files, or network streams) directly into ``RuntimeGenerationUtils``. How the Sample Works ~~~~~~~~~~~~~~~~~~~~ 1. **Initialization & Pooling Warmup**: On ``Start()``, the sample initializes the ``RuntimeGenerationPool`` and calls ``RuntimeGenerationUtils.Warmup()`` to pre-allocate internal collection capacities, eliminating garbage collector allocations during road generation. 2. **Parsing Raw Road Data**: The sample iterates over a list of reference editor road objects purely to read their underlying structural data (lane count, width, speed limits, pedestrian spacing) as an input source. 3. **Configuring TrafficNodeData Instances**: For each boundary node, a pooled ``TrafficNodeData`` instance is populated with geometric parameters (lane counts, divider widths, crosswalk offsets) and assigned transform data. Node rotations are assigned facing **opposite to the entrance direction** of the node. 4. **Processing Straight Roads vs. Crossroads**: * **Straight Roads**: Evaluates middle waypoint positions, pops nodes from the pool, and calls ``RuntimeGenerationUtils.GenerateStraightSegment()`` to build multi-lane geometry and lateral pedestrian paths. * **Crossroads**: Assigns shared traffic light containers and automatically computes turns, Bezier curves, and pedestrian links via ``RuntimeGenerationUtils.GenerateAutoCrossroad()``. 5. **Registering with Simulation World**: Once segments are generated, they are registered synchronously with the DOTS world using ``runtimeRoadManagerCustom.AddSegmentsSync(newSegments)``. 6. **Traffic Light & Frame Binding**: Upon completion of segment addition, physical traffic light MonoBehaviours in the scene are bound to their respective crossroad IDs via ``RegisterTrafficLights()``. Best Practices -------------- * **Populate Segment Nodes Before Generation**: Ensure ``newSegment.Nodes`` contains fully initialized ``TrafficNodeData`` objects with accurate positions and properties before passing it to ``GenerateAutoCrossroad()``. * **Ensure Correct Node Orientation & Alignment**: Always place boundary ``TrafficNodeData`` nodes at the exact same position with inverted (opposite) rotations so adjacent road segments align and stitch seamlessly. * **Use Custom Data Sources**: Feel free to replace the sample's scene road references with your own custom runtime data structures or procedural algorithms. * **Use Object Pooling**: Always use ``RuntimeGenerationPool.Instance.PopSegment()`` and ``PopTrafficNode()`` instead of instantiating new objects directly to prevent GC spikes during runtime generation. * **Recycle Removed Segments**: When tearing down chunks or removing road segments, return them to the pool using ``runtimeGenerationPool.RecycleSegment(segment)``. * **Pre-allocate with Warmup**: Call ``RuntimeGenerationUtils.Warmup()`` during game startup or scene loading to ensure zero-allocation performance when constructing road networks dynamically.