Questions about an ECS Spawner & Workflows

So … after an evening of working from a number of examples I’ve finally gotten a spawner working in ECS.

It has a SPAWNER component

[Serializable]
[GenerateAuthoringComponent]
public struct Spawner : IComponentData
{
    public Entity entity;
    public Entity point;
    public float delay;
    public float timeSinceLastSpawn;
}

and a ComponentSystem that works on it:

public class SpawnerSystem : ComponentSystem
{
    private float spawnTimer;
    protected override void OnUpdate()
    {
        float dt = Time.DeltaTime;
        Entities.ForEach((ref Spawner spawner) => {
            spawner.timeSinceLastSpawn += dt;
            if (spawner.timeSinceLastSpawn > spawner.delay) {
                spawner.timeSinceLastSpawn = 0f;
                Entity e = EntityManager.Instantiate(spawner.entity);
                EntityManager.SetComponentData<Rotation>(e, EntityManager.GetComponentData<Rotation>(spawner.point));
                EntityManager.SetComponentData<Translation>(e, EntityManager.GetComponentData<Translation>(spawner.point));
            }
        });
    }
}

In the editor we can see the following

Some questions:

  • the spawn point of the bullets seem to be coming out of 0,0 in world space rather than taking the Barrel_End position that I am trying to give it. Is there something fundamental that I’m missing? A “parent to XYZ” thing I should be doing so I can get the correct translation and rotation?
    expected spawn origin / actual spawn origin:
    5537407--569398--upload_2020-3-1_0-59-33.png / 5537407--569401--upload_2020-3-1_1-0-22.png

  • there seems to be a recurring hiccup in the steady stream of bullets is there something obvious I’m doing that is causing a garbage-collection? I’d read that we shouldn’t have to use object pooling on ECS, was that incorrect? (I should note that each Bullet has a MoveForward system and a TimedDestroy system on it, both taken from Angry_DoTS samples)
    the hiccup:
    5537407--569404--upload_2020-3-1_1-1-42.png

  • is there a way that we can edit entity values at runtime in the editor? I feel like fine-tuning things like weapon fire-rates will really need that capability.

EDIT: just to test a few more theories out … it may be the case that I still don’t understand ECS in the least. I thought I was doing so well … but if I duplicate the weapon, and rotate the original & clone I appear to still get just one stream of bullets and it’s on the same line as before.

Translation and Rotation are local space, not world-space.

Your spawn algorithm is incorrect. For a quick fix (this is not the best spawning algorithm by any means), change your if to a while and instead of
spawner.timeSinceLastSpawn = 0;
try
spawner.timeSinceLastSpawn -= spawner.delay;

You can do this with Live Link but not right in the editor as far as I’m aware.

Nah. It is just some weird nuances that are biting you. You may want to switch from ComponentSystem to SystemBase. The Entities.ForEach will also change to a Entities.WithStructuralChanges.ForEach.Run. SystemBase is new and replaces ComponentSystem and JobComponentSystem and has a bunch of nice benefits. The main benefit for you is that ComponentSystem’s Entities.ForEach generates GC whereas SystemBase’s does not.

Ooooh. I was trying SystemBase as seen in the latest GitHub - Unity-Technologies/EntityComponentSystemSamples and could not find the namespace.

I bet my entities package is out of date!

I’ve got to go do a Costco run with the wife & kid, but am really excited to give these fixes a try! Thanks :slight_smile:

public class SpawnerSystem : SystemBase
{
    private float spawnTimer;
    protected override void OnUpdate()
    {
        float dt = Time.DeltaTime;
        Entities.WithStructuralChanges().ForEach((ref Spawner spawner) => {
            spawner.timeSinceLastSpawn += dt;
            if (spawner.timeSinceLastSpawn > spawner.delay) {
                spawner.timeSinceLastSpawn -= spawner.delay;
                Entity e = EntityManager.Instantiate(spawner.entity);
                LocalToWorld l = EntityManager.GetComponentData<LocalToWorld>(spawner.point);
                EntityManager.SetComponentData(e, new Rotation { Value = l.Rotation } );
                EntityManager.SetComponentData(e, new Translation { Value = l.Position });
            }
        })
        .Run();
    }
}

So progress! The localtoworld fix got the barrel end in the right position and rotation!

I’m not sure I understand the while loop change. If OnUpdate runs every frame, wouldn’t that mean every frame would eventually spawn a bullet? I’m not sure that is right.

What other spawning algorithms have you seen? Or where might I read more on what better ones you’re talking about? I still seem to be experiencing a GC round even with the spawn with structural changes … maybe I will try a pool.

but this is already WAY better :slight_smile:

Nope. The first time around it is exactly the same as what you have with the if. But if you spawn a bullet, then the loop continues. This accounts for firing rates higher than your frame rates. That probably isn’t useful to you for this use case, but it will help you in the future.

More advanced spawning algorithms take this a step further and calculate how many things to spawn associated with each spawner. Then the algorithm bulk instantiates the prefabs. Then it sends the new instances to a job to initialize the values.

1 Like