Spawn an Entity with a ISystem as Job, which gets (Activated) from a MonoBehaviour

Hey Hey everyone,

i decided recently with the release of 1.0.1 to dig now deeper into Unity DOTS and ECS. So far so good im getting good results and nice Progress and compared to previous Versions this seems now more reliable to work with

I’ve testet different approaches to get the Player Prefab spawned but the most failed through i cannot Access non readable static variables from MonoBehaviour and wanted now to ask here for some tipps maybe how to deal with such “Mechanics”.

However i’ve got it with one approach to work, which doesn’t feel very “handy” to me, but it works.

What would you think about this approach to Spawn a Player? Is it “Wrong” or what would be better solutions, which i couldn’t figure out so far?

Here’s how i Spawn my PlayerPrefab Object:

Got an Authoring Object that contains the Prefab as Entity
class Baker : Baker<ConfigAuthoring>

Got an IJob as CreatePlayerJob which get passed a commandbuffer and the prefab to Execute the Job and Instantiate the Player.

[BurstCompile]
public partial struct CreatePlayerJob : IJob
{
    public EntityCommandBuffer commandBuffer;
    public Entity prefab;

    public void Execute()
    {
        Entity instance = commandBuffer.Instantiate(prefab);
    }
}

i gonna make a Call from a GameManager : MonoBehaviour which gets called by the Start Button

public void SpawnPlayer()
{
entity = World.DefaultGameObjectInjectionWorld.EntityManager.CreateEntity(typeof(SpawnPlayer));
}

To Create a IComponentData of SpawnPlayer (which contains no Data)
public struct SpawnPlayer : IComponentData { }

once the SpawnPlayer Entity Exists we’re running through the ISystem, CreatePlayerJobSystem

public partial struct CreatePlayerJobSystem : ISystem
{
    public bool playerSpawned;

    [BurstCompile]
    public void OnCreate(ref SystemState state)
    {
        playerSpawned = false;
    }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {

        SystemAPI.TryGetSingletonEntity<SpawnPlayer>(out Entity value);

        if (value.Index == 0)
        {
            return;
        }

        playerSpawned = true;

        var configEntity = SystemAPI.GetSingletonEntity<Config>();
        var config = SystemAPI.GetComponent<Config>(configEntity);

        CreatePlayerJob job = new CreatePlayerJob
        {
            commandBuffer = new EntityCommandBuffer(Allocator.TempJob),
            prefab = config.CharacterTwoPrefab,

        };

        JobHandle handle = job.Schedule();
        handle.Complete();

        job.commandBuffer.Playback(state.EntityManager);
        job.commandBuffer.Dispose();

        state.EntityManager.RemoveComponent<SpawnPlayer>(value);
    }
}

as you can see im trying to get a SingletonEntity of type Spawn Player, if the value.Index is 0 then there is no Entity. If it is something else there must be an Entity, then i get my Config and create the CreatePlayerJob
handle it and let the commandbuffer do his thing and then i remove the SpawnPlayer Component to prevent another player Object from getting spawned.

-----------Off

My biggest problem is when i change the Scene from a “Menu Scene” to the “Game Scene” im having trouble to get the Game going as SystemBase and ISystem run right from the Start and i cannot access them from MonoBehaviour properly and i wanna do certain things before they even can Run, i tried to work with Disable them and stuff but none of it worked properly the way i was looking for.

as an OOP Software Engineer, i really have difficulties to change my mindset for Unity DOTS and ECS… does anyone else experience that? Like it just doesn’t feel right to do it that way, or is it just me?

At the End, the player is Spawned and can be controlled, now the Enemies can find the “Player : IComponentData” to navigate to the Player. Here are 1015 Enemies turning on the Player and Translate to him, while there is no significant impact on the cpu (Awesome).

Thanks for some Information, sorry if everything is a little confuse, im confused too :face_with_spiral_eyes:

Here are some additional Questions that go through my mind:
Is there a Better way to Instantiate an Entity Prefab after loading a new Scene, once the Player pressed the Start Button?

Are there downsides of doing it my way?

Why i cannot call Managed Stuff from ISystem? And how can i use the SystemBase as a Bridge between MonoBehaviour and ISystem properly?

Lets back way up.

Why are you trying to spawn the player from a MonoBehaviour?

Actually i Instantiate the Player in an IJob, but i wanna trigger it from a MonoBehaviour as example a UI Button (Start Game)

Its about the Scene, i want a Start Scene of the Game, a scene without having as example the Entity Player or any System however SystemBase and ISystem are existing upon loading the Game and OnUpdate always runs.

So im in a “Main Menu Scene” which only has some Buttons and eventually a Play Button. But no Entities like the Player. But the Code will run now and obviously throw errors as the Entities are not preloaded in the Menu nor will they be instantiated. But some SystemBase and ISystems are already looking for as example the Player Entity, or a Config Entity

i also cannot go like something:

        var scene = SceneManager.GetActiveScene();
        if (scene.name == "Main Scene")
            return;

to prevent the Code in OnUpdate from running, as
Burst error BC1016: The managed function `UnityEngine.SceneManagement.Scene.get_name(UnityEngine.SceneManagement.Scene* this)` is not supported

SystemBase and ISystem relies on having the Entities we’re looking for available or atlast 1 right?

i feel like that i barely have control over on SystemBase or ISystem i have the Enabled on SystemBase variable to atlast control it a little, but accessing and changing that variable from MonoBehaviour in a proper way? Or how do i hook up a SystemBase or ISystem to a UI Button to (Make them Run, doing certain Actions) as it is not that easy to achie

The Tutorials mostly work with one Scene having a subscene and everything is already in the Scene before you press start. But when im in other Scenes, those Systems mess up everything.

I think also that i have so much issues moving away from MonoBehaviour, working and learning it for many years… xD

1 Like

You can make systems not execute by calling state.RequireForUpdate or using the [RequireMatchingQueriesForUpdate] attribute on your system.

Assuming I am understanding the problem you are facing (I doubt it), then the solution is to use those attributes to get the systems to not run while on the main menu scene, and then have your button switch scenes like normal. And that switches to a scene with subscenes, where those subscenes contain entities that get the systems to fire up.

Does that sound remotely close to what you are looking for?

yeah this sounds like what im looking for, i will give it a try and come back to tell the results.
Thanks for that information

I was looking back in the Documentation for [RequireMatchingQueriesForUpdate] which i thought i read.
https://docs.unity3d.com/Packages/com.unity.entities@1.0/manual/systems-systembase.html
Somehow i missed that Require, and it gives me additional Options to Control those Systems thats nice and so far the Changes work nicely. Thanks for that hint.

Is it right when i say:
I have the Entity Player
now ive got as example a LevelSpawnSystem : SystemBase which require the Entity Player to Spawn new Enemies
in order to make the LevelSpawnSystem Stop spawning Enemies i just remove the Entity Player from the World?

Or in another Example the Enemy Movement which Require the Entity Player to get the LocalTransform for Position
I Set the Require State and then in order to make the Enemies Stop Moving i just remove the Player Entity and it will not throw errors? Is that the approach to go?

how about a Pause Scenario? I Would like to add as Example the Entity to the World
public struct GamePaused : IComponentData
And now as the Enemy Movement stops from Updating. Is there also the other Way arround like:
[IfHasQuerie] Dont Update?

I was thinking about this approach:

        SystemAPI.TryGetSingletonEntity<GamePaused>(out Entity pause);

        if (pause.Index == 1)
            return;

If he can find the GamePaused Entity the Update will be return and not run until the GamePaused is removed from the World.

So i’ve got my MonoBehaviour Hooked up to a Pause Button and a Resume Button

    void PauseGame()
    {
        World.DefaultGameObjectInjectionWorld.EntityManager.CreateEntity(typeof(GamePaused));

    }

    void ResumeGame()
    {
        World.DefaultGameObjectInjectionWorld.EntityManager.CreateEntity(typeof(GameResume));
    }

Calling those Functions and each one creates its wanted “GameState” as Entity

I’ve added another SystemBase to controll those States

public struct GamePaused : IComponentData { }

public struct GameResume : IComponentData { }

[BurstCompile]
[RequireMatchingQueriesForUpdate]
public partial class PauseStateSystem : SystemBase
{
    [BurstCompile]
    protected override void OnCreate()
    {
        base.OnCreate();

        RequireForUpdate<GameResume>();
    }

    [BurstCompile]
    protected override void OnUpdate()
    {
        SystemAPI.TryGetSingletonEntity<GameResume>(out Entity value);

        if(value.Index == 1)
        {
            var pauseEntity = SystemAPI.GetSingletonEntity<GamePaused>();

            EntityManager.DestroyEntity(value);          
            EntityManager.DestroyEntity(pauseEntity);
        }  
    }
}

When the GamePaused Entity is available, certain Systems will stop running through the return described above. Once the Player hit the Resume Button, it will create the GameResume Entity
In this case the “PauseStateSystem” which require a GameResume Entity to Update will run now get both Entities GameResume and GamePaused and remove those Components

Or im on a wrong Path with this?