I’m missing a key piece of the pure ECS paradigm and I think it’s time to ask.
In my especific use case I have a JobComponentSystem that move the vertices of many 3d meshes. To reference the fields needed by the jobSystem I created an IComponentData wrapped by a ComponentDataWrapper, so I can attach it to a gameObject. This IComponentData struct should contain a couple of floats and the mesh filter. From this filter I’d get the mesh and two NativeArray Vertices and Normals.
In the hybrid ECS examples I see the fields can be created and initialised on Monobehaviour.Start(), but when I work with IComponentData structs I don’t have that callback available, it could be that some of the fields are serialized in the inspector, others need to be created/filled in the constructor and maybe they both are dependant to each other. Also I can’t set default values in the field’s initializers.
So to wrap it up, I don’t know what the workflow should be.
What’s the right way to initialise IComponentData’s fields? How do I set default field initializers? How well ScriptableObjects work with IComponentData? How would I get the meshfilter from within the IComponentData script (as opposed to link it in the inspector)?
you can have an init component and a system that process that and initializes your main component.
(2) is the recommended approach, so that entities created at runtime can have the init component added and your init system takes care of that (also all data should be in components, systems have only logic)
ps: IComponentData must be blittable, i.e. can’t have array fields (NativeArray included). you need to use ISharedComponentData for that
To confirm I fully understand it, should I use the hybrid ECS paradigm if I require managed objects in my data containers? Or should I aim for the pure ECS approach using ISharedComponentData instead.
I’ve done some research on ISharedComponentData and the documentation says that it’s meant for shared assets between entities but it says nothing how the objects are managed. Is your suggestion a workaround or the proper way to do it?
Also IJobProcessComponentData requires a IComponentData though. Is this temporary (feature missing) or there’s no way for me to work with ISharedComponentData inside a JobComponentSystem.
as ISharedComponentData is shared/can contain shared (reference) fields, it can’t be jobified.
I think the blittable restriction on IComponentData is specifically for them to be used with IJobProcessComponentData.
same way for old components, they are reference types therefore they can’t be jobified.
you need to copy vertices/normals to native arrays on the main thread (until there is an API for that).
For the time being if you really need class types, the recommended approach is to use MonoBehaviour to store that data and use GameObjectEntity on the game object in order to iterate over all entities that have that MonoBehaviour.
I think using ISharedComponent data for it is kind of a workaround. I would keep those type of entities as game objects and use MonoBehaviour for storing the data until we have a real solution.
Lets say I have a Clock component with time values that a ClockSystem increments, and I want that clock to start at Hour = 12 so that the player doesn’t commence gameplay during the night, how should I handle that?
Even an init component wouldn’t work in this case as I can’t have a default value at all with anything deriving from IComponentData, unless I’m wrong of course (I’m new to ECS, and in using only stucts to store data).
People have learned to attack this in a few different ways since this thread was originally active:
Use ‘Proxy’ Monobehaviours in conjunction with your IComponentDatas, so that a representation of your Entity can be built as a prefab GameObject. On the proxy monobehaviors of that prefab, set the default values you’d like your entity to have. Store a reference of that prefab in an ISharedComponentData on the Entity. When you want to instantiate the entity with default values, have a system access that ISharedComponentData to get a reference of the prefab, and instantiate it.
Set up a single system (or group of systems) that’s responsible for spawning instances of each entity type. When another system wants to spawn an instance of that entity, have it create a message entity, with a “spawnEntityInstance” component, or similar. In the system that actually handles the spawning, make a component group for these message entities. When that system actually constructs the new instance, have it assign default values to its components.
People have come up with other ways. Some use ScriptableObjects to store default properties at editor time, and apply them to new entity instances at runtime.
There are creative solutions out there, but to my knowledge, there doesn’t yet exist a “pure entity prefab” that could operate out of the box to fill this need.
M_R suggested adding an init component, and then making special systems that do nothing but look for an init component and a component of whatever type they’re looking for, and then when the find one, they initialize it and remove the init component. and of course those systems need a special ordering so they happen before everything else.
Surely there must be a better way than that… what a mess… I have over 10 different initializer systems and my game isn’t even close to complete… Large complete games would have 100s of initializer systems that clutter up code directories and waste update cycles. I really miss C++'s inline initializers for structs
I do that too when I’m using the gameobject-to-entity workflow. It works really well. When I’m not using that workflow, there are certain situations where the only way to initialize is by doing the initialize tag thing.
The only use case I could imagine you needing this is when you need to add a component via ECB and you want to initialize that component to some set of values only if it doesn’t already exist. If that’s your use case, then I have an idea, but it involves a custom command buffer and playback system and you might not find it any cleaner than writing custom initialization systems.
If that’s not your use case, you should elaborate what your use case is, because there’s probably a better alternative.
public struct Speed : IComponentData
{
public int Value;
public static implicit operator int(Speed b) => b.Value;
public static implicit operator Speed(int v) =>
new Speed { Value = v };
public static Speed Default => 10;
}
Then you just need to call Speed.Default when initializing a speed component, for example. For more complicated logic like adding extra components or something you can just create more static functions that take in an EntityManager/EntityCommandBuffer as an argument.
No voodoo, it’s just to save a bit typing and (imo) make things a bit more readable. I like EntityManager.SetComponent<Speed>(e, 10); instead of EntityManager.SetComponent(e, new Speed{value = 10});.
99.9% of my components have a single value like that so I use this pattern quite a lot. Just a matter of preference.