About Me 👈

Selectively Running Systems in Scenes with Unity ECS

Learn how to selectively run jobs only in certain scenes with Unity ECS.

unitycsharptutorial

Created on November 4, 2019. Last updated on January 19, 2020.

Video of projectile motion demonstration with Unity DOTS.

Some of the code in this tutorial is used in my Unity demos on GitHub! 👀 Specifically, this is how I make Projectile components only get added to entities in the demo scene for my Projectile Motion with Unity DOTS tutorial.

One Approach

The most common and idiomatic way to control which systems, whether ComponentSystems or JobComponentSystems, inflict a performance impact upon a given scene, is indirect: component data therefore becomes scene-based. For instance, if a system only processes entities when they include a SceneAComponent, then you could restrict processing to a so-called SceneA. We'll define a data-less SceneAComponent, which effectively serves as a "component tag."

public struct SceneAComponent : IComponentData { }

Now we can restrict processing only to entities that have said tag like so:

[RequireComponentTag(typeof(SceneAComponent))]
struct SomeSceneAJob : IJobForEachWithEntity<SomeComponent>
{
    ...
}

RequireComponentTag does exactly as the naming implies. It's just an expressive way to say that a component is strictly for querying certain entities—this is an inexpensive check for existence. Alternatively, by using the ForEach.Entities syntactic sugar:

return Entities
    .WithAny<SceneAComponent>()
    .ForEach((ref SomeComponent someComponent) =>
    {
        ...
    })
    .WithName("SomeSceneAJob")
    .Schedule(inputDeps);

In that case we process any entity that has a SceneAComponent by way of WithAny. We can even perform negations! We could say WithNone(SceneBComponent), for example, meaning, "I don't want to process any entities that have SceneBComponent."

A Different Approach

If you're still scratching your head, then maybe you have a situation where there are multiple scenes, and you want different variations of similar systems to operate on the same component data. No worries, there's a straightforward way to accomplish this without creating a bunch of data-less components everywhere as tags.

Basically, you can use the SceneManager to get the active scene, and then you conditionally schedule a job based on which scene you find. The import you want is UnityEngine.SceneManagement. Here's some example code of this approach:

class SomeSystem : JobComponentSystem
{
    public static readonly string SCENE_NAME = "SomeScene";

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        if (
            !SceneManager.GetActiveScene().name.Equals(SCENE_NAME)
        ) return inputDeps;

        ...
    }
}

We immediately return the input dependencies (inputDeps) if the SCENE_NAME is not what we want. Change SCENE_NAME to whatever (just make sure it's correct), and then the main thread will only schedule SomeJob if and only if the intended scene is running. You can easily adapt this to handle multiple scenes. This is an effective way to solve the problem with virtually no overhead.

Again, please check out my GitHub repo for working examples of these strategies being employed. Please star it if it helps.