Skip to content

Game.VFXSystem

Assembly:
Namespace: Game.Effects

Type: class

Base: GameSystemBase, IPreDeserialize

Summary: VFXSystem manages runtime visual-effect instances for Cities: Skylines 2. It enumerates VFX prefabs, creates per-prefab VisualEffect components and GPU-backed instance data textures, and updates instance data via jobs. The system receives source update requests (add/remove/move) through NativeQueue producers, maintains instance-to-enabled-index mappings, schedules a burst job (VFXTextureUpdateJob) to write instance data into a raw texture buffer, and applies textures to VisualEffect assets. It integrates with other world systems such as PrefabSystem, WindTextureSystem, TerrainSystem, EffectControlSystem and RenderingSystem. The system is careful about job dependencies and disposes Native collections when destroyed or before deserialization.


Fields

  • private static class VFXIDs Provides cached Shader.PropertyToID integers used to set properties on VisualEffect (WindTexture, MapOffsetScale, Count, InstanceData).

  • private struct EffectInfo Holds per-prefab runtime data:

  • VisualEffect m_VisualEffect — the created VisualEffect component used to drive the effect.
  • Texture2D m_Texture — GPU texture used as instance data (width = max instances, height = 3).
  • NativeArray<int> m_Instances — array mapping instance slot -> enabled effect index (or -1).
  • NativeParallelHashMap<int,int> m_Indices — mapping enabled effect index -> instance slot.
  • int m_LastCount — last known active count used to trigger updates.
  • bool m_NeedApply — whether texture.Apply() must be called on the main thread.

  • private struct VFXTextureUpdateJob : IJob Burst-compiled job that writes instance data into the raw texture buffer used by VisualEffect. Fields:

  • NativeArray<float4> m_TextureData — raw texture data pointer to write floats.
  • [ReadOnly] NativeArray<int> m_Instances — mapping of slots to enabled indices.
  • [ReadOnly] NativeList<EnabledEffectData> m_EnabledData — source data for enabled effects.
  • int m_Count — number of slots to update.
  • int m_TextureWidth — width of the instance texture used for row offsets. The job writes position/intensity, rotation (in radians), and scale rows into the texture.

  • private Queue<NativeQueue<VFXUpdateInfo>> m_SourceUpdateQueue Queue of producer NativeQueues created by callers via GetSourceUpdateData(). Each NativeQueue is consumed and disposed by this system during OnUpdate/PreDeserialize.

  • private JobHandle m_SourceUpdateWriter Combined JobHandle representing writers that produced data into the NativeQueue(s). Completed before consuming queues to guarantee safety.

  • private EntityQuery m_VFXPrefabQuery Query matching VFX prefabs (VFXData) used to initialize EffectInfo entries.

  • private PrefabSystem m_PrefabSystem Cached reference to PrefabSystem for resolving EffectPrefab instances.

  • private bool m_Initialized Flag indicating that initialization (prefab enumeration and EffectInfo allocation) has been performed.

  • private EffectInfo[] m_Effects Array of per-prefab EffectInfo populated during initialization.

  • private JobHandle m_TextureUpdate JobHandle for scheduled VFXTextureUpdateJob(s); combined across prefabs per frame.

  • private WindTextureSystem m_WindTextureSystem Cached reference used to set wind texture on VisualEffect instances.

  • private TerrainSystem m_TerrainSystem Cached reference used to set map offset/scale on VisualEffect instances.

  • private EffectControlSystem m_EffectControlSystem Used to get the enabled effects data and to register a reader dependency for texture updates.

  • private RenderingSystem m_RenderingSystem Used to compute a playRate for VisualEffect playback based on frame timing.

Properties

  • None. {{ The VFXSystem exposes no public properties. All runtime state is managed via methods and internal fields. }}

Constructors

  • public VFXSystem() Default constructor. The system relies on OnCreate to initialize internal references and data structures.

{{ YOUR_INFO }} Initial setup that depends on world systems occurs in OnCreate rather than the constructor. The constructor only ensures the system instance is created by the ECS world.

Methods

  • public NativeQueue<VFXUpdateInfo> GetSourceUpdateData() Returns a newly created NativeQueue (Allocator.TempJob) and enqueues it into m_SourceUpdateQueue for the system to consume on the next update. Caller must write to the returned queue from jobs and call AddSourceUpdateWriter to provide writer job handles.

  • public void AddSourceUpdateWriter(JobHandle jobHandle) Combine the provided jobHandle with internal m_SourceUpdateWriter to track writer dependencies. The system completes m_SourceUpdateWriter before reading and disposing the queued NativeQueues.

  • [Preserve] protected override void OnCreate() : void Performs runtime initialization:

  • creates the m_SourceUpdateQueue,
  • sets up m_VFXPrefabQuery,
  • gets references to PrefabSystem, WindTextureSystem, TerrainSystem, EffectControlSystem, and RenderingSystem from the world.

  • private bool Initialize() : bool Called by OnUpdate to lazily initialize per-prefab EffectInfo when VFX prefabs are present:

  • enumerates VFX prefab entities and VFXData,
  • creates a GameObject+VisualEffect per prefab,
  • creates a Texture2D to hold instance data (width = component.m_MaxCount, height = 3, R32G32B32A32_SFloat),
  • allocates m_Instances and m_Indices Native containers (Allocator.Persistent),
  • updates VFXData component fields (m_MaxCount, m_Index) and writes them back to the entity. Returns true if initialization was performed, false otherwise.

  • [Preserve] protected override void OnDestroy() : void Cleans up if initialized:

  • destroys created Texture2D objects,
  • disposes Persistent Native containers (m_Instances, m_Indices),
  • clears and disposes any queued NativeQueues via ClearQueue(),
  • calls base.OnDestroy().

  • [Preserve] protected override void OnUpdate() : void Main frame update:

  • ensures initialization is done (lazily) or clears queues and early-exits if not ready,
  • completes pending texture update jobs and source writers,
  • obtains enabled effect data (NativeList) and its dependency,
  • consumes producer NativeQueues: waits for writer jobs, processes VFXUpdateInfo entries (Add / Remove / MoveIndex) to maintain instance and indices maps, and updates VisualEffect Count property,
  • computes playRate from rendering frameDelta and world delta time and sets VisualEffect properties (playRate, wind texture, map offset/scale),
  • prepares VFXTextureUpdateJob for each prefab that needs updating (max of current count and last count),
  • schedules jobs and sets m_Effects[i].m_NeedApply so main thread will apply the Texture2D after jobs complete,
  • registers the texture update job handle with EffectControlSystem as a reader.

Notes: OnUpdate uses JobHandle.CombineDependencies to safely chain and schedule jobs, and marks when a Texture2D.Apply() must be called on the main thread.

  • public void PreDeserialize(Context context) : void Called before deserializing world state:
  • completes m_TextureUpdate,
  • resets VisualEffect Count to 0 and destroys the VisualEffect GameObject instances,
  • disposes Native containers and sets m_Initialized=false so the system will reinitialize after deserialization,
  • clears and disposes producer queues.

  • private void ClearQueue() : void Completes m_SourceUpdateWriter and disposes any remaining NativeQueue items in m_SourceUpdateQueue.

  • public VFXSystem() Empty public constructor (same as listed in Constructors).

Additional nested types: - VFXTextureUpdateJob.Execute() - For each slot i in m_Count: reads m_Instances[i]; if -1 writes zero intensity; otherwise reads EnabledEffectData (position, rotation, scale, intensity) and writes three float4 rows into m_TextureData (position/intensity, rotation in radians, scale). - This is the burst job that writes per-instance data into the texture buffer used by VisualEffect.

Usage Example

// Example: Producer side - create a queue and write updates from a job
var queue = vfxSystem.GetSourceUpdateData();
// schedule a job that writes VFXUpdateInfo into 'queue' (NativeQueue<VFXUpdateInfo> is thread-safe for writers)
// After scheduling that job, register the writer handle with the system:
vfxSystem.AddSourceUpdateWriter(yourWriterJobHandle);

// Example: consumer is automatic - VFXSystem.OnUpdate() will consume the queue, update instance maps,
// schedule the VFXTextureUpdateJob(s), and call Texture2D.Apply() on the main thread when needed.

// Example: ensure proper cleanup before scene load/deserialization
vfxSystem.PreDeserialize(context); // invoked by game during load sequence

{{ YOUR_INFO }} Notes and best practices: - Always call AddSourceUpdateWriter with the JobHandle that writes to the queue so VFXSystem can safely wait for writers before consuming and disposing the queue. - Returned NativeQueue is Allocator.TempJob and will be disposed by the system; producers should not dispose it themselves. - Do not call Texture2D.Apply() from jobs — VFXSystem marks m_NeedApply and calls Apply() on main thread after completing scheduled jobs. - The instance texture format is R32G32B32A32_SFloat and uses three rows: position+intensity, rotation (radians), and scale. Respect this layout when reading in VisualEffect shaders. - When implementing custom VFX sources, use EnabledEffectData indexing conventions (enabled index maps into the EffectControlSystem list) so the system can correctly map instances.