Game.Tutorials.TutorialObjectPlacementTriggerSystem
Assembly: Game
Namespace: Game.Tutorials
Type: class
Base: TutorialTriggerSystemBase
Summary:
This ECS system handles tutorial triggers that depend on placing objects in the world. It watches active tutorial triggers (ObjectPlacementTriggerData) and compares them against newly created objects (buildings, edges, connections, routes, trees, waterways, etc.). The system uses parallel IJobChunk jobs (Burst-compiled where appropriate) to:
- Clear per-trigger counts each update,
- Inspect created objects and check network/topology relationships (edges/nodes/connections, owners, placeholders, roads, transformers, sewage outlets, electricity/water/resource connections),
- Increment trigger counts and mark tutorial triggers as pre-completed or completed when required thresholds are reached,
- Produce unlock events via an EntityCommandBuffer and call TutorialSystem.ManualUnlock to apply unlock effects.
It relies heavily on Unity.Entities queries, ComponentLookup/BufferLookup, and temporary native containers (NativeList, NativeParallelHashMap) to traverse graph-like networks when searching for matches. It also considers the current tool/elevation state (HasElevation) when some triggers require elevation.
Fields
-
private NetToolSystem m_NetToolSystem
Provides access to the Net tool state (used to read current elevation when checking elevation-dependent triggers). -
private ToolSystem m_ToolSystem
Reference to the global ToolSystem to determine which tool is active and get tool-related state. -
private EntityQuery m_CreatedObjectQuery
Query used to collect entities that were created this frame (PrefabRef + Created and matching any supported object type). Used by the CheckObjectsJob to compare new objects against active triggers. -
private EntityQuery m_ObjectQuery
Query used to collect existing objects in the world (PrefabRef + many object types), used as an alternative to m_CreatedObjectQuery depending on triggersChanged vs created objects. -
private EntityArchetype m_UnlockEventArchetype
Archetype used to create unlock events (Event + Unlock components) when a trigger is satisfied and an unlock should be emitted. -
private TypeHandle __TypeHandle
Container for component and buffer type handles / lookups used by jobs. Populated in OnCreateForCompiler and used to initialize job data. -
(nested)
private struct ClearCountJob : IJobChunk
Job that resets ObjectPlacementTriggerCountData.m_Count to 0 for the active trigger query each update when triggers change. -
(nested)
private struct CheckObjectsJob : IJobChunk
Main job that iterates active triggers, inspects created/object archetype chunks, and checks whether placement conditions are met. Contains many helper methods for graph traversal and matching. -
(nested)
private struct TypeHandle
Generated container holding ComponentTypeHandle/ComponentLookup/BufferLookup/etc. used to bind data to jobs.
Properties
- (none declared publicly on this class)
This system uses internal component lookups and queries rather than exposing public properties.
Constructors
public TutorialObjectPlacementTriggerSystem()
Default constructor — preserved attribute used for runtime/linker. The system initialization occurs in OnCreate (overridden).
Methods
-
protected override void OnCreate()
Initializes entity queries (m_ActiveTriggerQuery, m_CreatedObjectQuery, m_ObjectQuery), creates unlock event archetype, acquires ToolSystem and NetToolSystem, and registers the active trigger query as required for updates. Also assigns handles in OnCreateForCompiler path. -
protected override void OnUpdate()
Main update loop: - If triggersChanged is true: schedules ClearCountJob to zero per-trigger counts, then schedules CheckObjectsJob (first-time check) using m_ObjectQuery results gathered asynchronously, and marks triggers as TriggerPreCompleted when thresholds are met.
- Else if there are created objects this frame: schedules CheckObjectsJob (regular check) using m_CreatedObjectQuery results, and marks triggers as TriggerCompleted when thresholds are met.
-
The CheckObjectsJob uses a parallel command buffer to add TriggerPreCompleted/TriggerCompleted and calls TutorialSystem.ManualUnlock to produce unlock events.
-
private bool HasElevation()
Returns whether the Net tool is active and currently has non-zero elevation (used to satisfy triggers that require elevation). Uses m_ToolSystem.activeTool and m_NetToolSystem.elevation with a small epsilon. -
protected override void OnCreateForCompiler()
Compiler-time helper invoked to assign queries and populate the __TypeHandle handles for job usage. Calls __AssignQueries and __TypeHandle.__AssignHandles. -
private void __AssignQueries(ref SystemState state)
Generated placeholder to set up EntityQueryBuilder if needed by the compiled code path. -
(nested ClearCountJob)
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
Resets ObjectPlacementTriggerCountData.m_Count = 0 for each element in the chunk. -
(nested CheckObjectsJob)
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
For each active trigger in the chunk: - Calls Check(trigger buffer) to inspect created objects,
-
Increments per-trigger count and, when count >= required, adds TriggerPreCompleted (first-time) or TriggerCompleted and invokes ManualUnlock.
-
(nested CheckObjectsJob)
private bool Check(DynamicBuffer<ObjectPlacementTriggerData> triggerDatas)
High-level check that iterates each triggerData and each created object chunk to find matching prefabRefs, then dispatches to specialized checks depending on whether the created entity is an edge, has water/electricity/resource connections, or is a plain object. -
(nested CheckObjectsJob)
private static bool FlagsMatch(ObjectPlacementTriggerData triggerData, ObjectPlacementTriggerFlags flags)
Utility that checks whether the trigger's m_Flags contains the specified flags. -
(nested CheckObjectsJob)
private bool CheckEdges<T1, T2>(...) where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData
Performs traversal from an edge's start node across connected edges to count matching search components (e.g., road connections). Uses CheckEdgesImpl for BFS/DFS walking. -
(nested CheckObjectsJob)
private int CheckEdgesImpl<T1, T2>(Entity node, ...)
Graph traversal implementation used by CheckEdges. Traverses connected edges, counts matches, pushes/pops nodes from a stack and uses onStack map to avoid infinite loops. Returns number of found matches. -
(nested CheckObjectsJob)
private bool CheckNodes<T1, T2>(...) where T1 : unmanaged, IComponentData where T2 : unmanaged, IComponentData
Performs traversal starting from a node to count matching node components (e.g., sewage outlet, transformer). Uses CheckNodesImpl for traversal. -
(nested CheckObjectsJob)
private int CheckNodesImpl<T1, T2>(Entity node, ...)
Graph traversal implementation used by CheckNodes. Visits nodes and edges via ConnectedEdge and ConnectedNode buffers, counts matches taking into account Owner components, and uses stack/onStack to manage traversal. -
(nested CheckObjectsJob)
private void Push(Entity entity, NativeList<Entity> stack, NativeParallelHashMap<Entity, int> onStack)
Helper to push entity into stack and increment onStack counter for that entity. -
(nested CheckObjectsJob)
private Entity Pop(NativeList<Entity> stack)
Pops last entity from stack (LIFO) using RemoveAtSwapBack; returns Entity.Null if empty. -
(nested CheckObjectsJob)
private bool Check(ObjectPlacementTriggerData triggerData, NativeArray<PrefabRef> prefabRefs)
Simple check to see if any prefabRef in array matches triggerData.m_Object.
Usage Example
// Obtain the system from the default world (typical in Cities: Skylines 2 mod code)
var world = Unity.Entities.World.DefaultGameObjectInjectionWorld;
var placementTriggerSystem = world.GetExistingSystemManaged<Game.Tutorials.TutorialObjectPlacementTriggerSystem>();
// The system runs automatically (OnCreate/OnUpdate).
// To inspect whether elevation-dependent triggers would pass, the system uses the game's ToolSystem and NetToolSystem internally.
// To add a custom trigger you would add Entity components matching ObjectPlacementTriggerData + TriggerActive etc. to entities so this system evaluates them.