How do you initialize data in a component using a forEach?

I’ve been trying to initialize a component with some basic data using a Entities.ForEach() in a OnCreate method but the component doesn’t seem to be populating. How are you supposed to do this?

The component has both:

public float dx;
public float dy;

The following is in a SystemBase:

Entities.ForEach( ( ref CubeData cube ) => {
    cube.dx = 0;
    cube.dy = 0;
}).Run();

Unless you intentionally created an entity before OnCreate is called, entities usually don’t exist yet when OnCreate is called.

Also, UnityEngine.Debug.Log($“{/non string things to print/}”) works in Burst contexts, so you can use that to figure out whether Bursted code is executing or not.

1 Like

I think you’re right about this. What’s the right way to initialize component values? There doesn’t seem to be an OnStart() in SystemBase.

EDIT: Should I be doing it in a MonoBehaviour?

EDIT: Can you set IComponentData variables in a MonoBehaviour?

I typically rely on GameObject Conversion for initializing components. For data that should be initialized to a hardcoded value (like 0 for counters), I will hardcode that initialization in the IConvertGameObjectToEntity.

For random initialization, I either do the randomization right after instantiation, or I use a reactive system.

I’m experimenting with thousands of cubes so it doesn’t make sense to do it manually. How do you update IComponentData variables on instantiation or with a reactive system?

Someone else can probably give you some simplified examples. I will give you some real-world examples.
Example 1: Instantiation - lsss-wip/Assets/_Code/SubSystems/Gameplay/OrbitalSpawnersProcGenSystem.cs at v0.2.0 · Dreaming381/lsss-wip · GitHub
Note that I am procedurally generating an entity from scratch here and then instantiating it. I could have instantiated an authored prefab and that’s usually what I do, but it didn’t make much sense for this particular system. Regardless of if the prefab is authored or generated from scratch, I tag it with a custom tag that I can then use in an Entities.ForEach afterwards. That happens here inside Randomize().

Example 2: ECB - lsss-wip/Assets/_Code/SubSystems/Gameplay/FireGunsSystem.cs at v0.2.0 · Dreaming381/lsss-wip · GitHub
Here I am actually instantiating authored prefabs in a parallel job using an EntityCommandBuffer.ParallelWriter. After instantiating each entity, I also call SetComponent to initialize the Translation and Rotation. If you were to change the git hash for this file, you will see it has changed in later versions to use a custom InstantiateCommandBuffer I wrote which combines the instantiation and initialization and performs quite a bit faster.

Example 3: Reactive System - lsss-wip/Assets/_Code/SubSystems/AI/AiExploreInitializePersonalitySystem.cs at v0.2.0 · Dreaming381/lsss-wip · GitHub
Here, the EntityQuery matches newly instantiated AI ships which still have an AiExplorePersonalityInitializerValues component. This system initializes all of these entities, then removes the AiExplorePersonalityInitializerValues component from all of them. This results in those entities never being processed by the system again.

2 Likes

Thanks, I’ll take a look at those for sure.

I’m surprised no one has done a simple example on how to initialize variables for IComponentData. I’m basically asking for a helloworld here but I can definitely look what you’ve done for ramping up on ECS.

I’ve only been doing ECS for 3 days now but I’ll catch up soon enough. It seems pretty easy for the most part. It’s similar to the MVC design pattern I’ve used for most of my projects. Just unclear on what the best practices are.

Thanks again for sharing code.

I just create a system for it and disable it after running once.
Example code:

    public class DestinationInitializationSystem : SystemBase
    {
        protected override void OnUpdate()
        {
            // Run once and set Destination for all Agents to their initial position, so prevent moving to float3.zero at the start.
            Entities.ForEach((ref Destination destination, in Translation translation) =>
                    {
                        destination.Value = translation.Value;
                    })
                    .ScheduleParallel();

            Debug.Log(nameof(DestinationInitializationSystem) + " run.");
            Enabled = false;
        }
    }
2 Likes

Do you know if there’s a way to have two ForEach loops and only disable one of them within the same class? For example the first ForEach loop being the initialization and the second being the main loop.

I’d just use two different systems for that, one for initialization one for main loop. If you want one system initializing on OnStartRunning and main loop on OnUpdate could work (though I didn’t try this)

1 Like

When using two systems how do you know which one will execute first?

You can add [UpdateBefore(typeof(OtherSystem))] or [UpdateAfter(typeof(OtherSystem))] attributes to your systems to set their update order

1 Like

Amazing! I guess I should be reading the documentation more closely?

or just use the forums as you do right now… even though there is documentation I get confused all the time :stuck_out_tongue:

1 Like

For this setup I would recommend adding [UpdateInGroup(InitializationSystemGroup)] to your initializing system, rather than using UpdateBefore/UpdateAfter. That way you don’t need to add any attributes to other systems that access the data - by default systems run later in SimulationSystemGroup

1 Like

Is there an order of execution chart similar to this one:

But for ECS?

https://docs.unity3d.com/Packages/com.unity.entities@0.16/manual/system_update_order.html#default-system-groups

1 Like