Initializing Component that uses GenerateAuthoringComponent

Currently, as far as I know, there’s not a clean way to initialize an component that is added to an entity through GenerateAuthoringComponent.

The best solution at the moment is to create a MonoBehaviour that implements IConvertGameObjectToEntity. The downside of this is obvious: It’s another class to maintain, doesn’t follow the ease of use utility GenerateAuthoringComponent was designed for, and creates a point of failure via maintenance.

Another solution is to create a Component Tag that is something like “NeedsToInitialize”, but the downsides of this is that it needs to be searched every frame for every specific type, needs to have a specific tag for specific types, needs to be managed to be called before the actual Component, needs to be removed which causes a chunk move, and is another point of failure.

A third solution is to create a flag value in the Component struct that something like Initialized = false, that is processed on the first few lines of the System. The downside of this is that even if the ForEach didn’t need the component that has the value, it needs it now - forever. Many init flags require one for each type, which gets passed around pointlessly later. Also, it introduces a branch when many of us are working hard to avoid “if” and “switch” statements in our ECS Systems. Again, also needs to be checked for every Component of that system, every frame.

Why?
An example of this need, is a Component that needs to cache the previous position of the Entity for something like motion vectors. The first run of the System, if the cached previous position is (0,0,0), but the object was at a different position it invalidates the first pass of the system and leads to unexpected behavior.

My recommendation is to either add more markup for attributes (such as [InitializeFrom], or an interface that replaces GenerateAuthoringComponent that serves the same purpose on the Component struct.

Interface:
Through an interface, we could have standard function calls such as Convert (with a reference to the GameObject) but also editor specific ones such as DrawEditorPanel, etc. If these interface methods were given permission to access referenced memory, then developers could add whatever init logic they needed. They would also be called only once: When the Entity was created as part of the Runtime > World conversion.

Ex:
Convert(GameObject gameObj, Entity entity, EntityManager dstManager, GameObjectConversionSystem cs)

Markup:
The markup option, may be more limited due to it’s nature, it would be easier for developers to implement common cases such as reading from Transform or in more advanced cases, from a static class is delegated to handle the logic.

Ex:

struct MyComponent {
[InitializeFrom( typeof(Transform), "position" )]
float3 InitialPosition;

[InitializeFrom( typeof(MySingleton), "GetPlayerPosition" )]
float3 PlayerOriginalPosition;
}```

May I ask what this use case has to do with the authoring component? It sounds like you really just want a reactive system to initialize the values at runtime. Authoring components won’t help you with that.

In this particular hybrid ECS workflow I am imaging GameObject prefabs, marked up with a number of Components that are GenerateAuthoringComponent, and then instantiated at runtime

The editor side of things is just because of that structure, for that specific workflow.

If I was making the entities by script, without a GameObject in the hybrid worklow, then initialization is not a problem, correct?

Hence…

I would really reconsider that workflow. There’s no need to use authoring components directly at runtime like that. You can author a prefab, and convert it once, and then instantiate the entity multiple times instead. Besides, generating the authoring component does not actually initialize the values. It just exposed the values to be initialized by someone working in the editor. That doesn’t help you with runtime initialization.

Yes. That’s the plan. Author prefabs and variants easily in the editor, that have components for ECS.

The point is… If that’s the plan, then initializing values in Components with runtime specific data, then have issues and trade offs (see OP).

Considering how often initializing a runtime instance is needed, and that’s there’s a number of ways to hack around this that have clear trade offs in maintenance, code reliability, and performance … That is a warning flag that something is missing in the API.

Example:

I have a BallisticMotion Component that I use this for slow arcing lobbing things, and fast moving arrows or bullets.

So, I make a bunch of prefabs with their graphics and in the editor add BallisticMotion authoring component, and use that to tune in the properties of the prefab for speed, drag, etc. Each prefab is then a self-contained type of arrow that I can then instantiate at runtime.

But, as part of the BallisticMotion System, I track motion vectors and need to know the previous position of the arrow. If the previous position is always 0,0,0, then the first run of the system is incorrect,. That runtime “first” position value needs to be initialized in some way.

Loop back up to the the OP and look at the tradeoffs for different techniques of initializing values at runtime for prefab generated instances.

And what I am saying is that this initialization is not the responsibility of the authoring component. What you really want here is a reactive system to initialize the runtime-initialized data.

Exactly, but and more streamlined and less error prone for the Prefab-based workflow.

To me, it makes sense to make it a part of the authoring component mechanic, solely because if the Components were added to entities only via code, then initialization is “free”. It’s a non-issue when Entities and their whole Component structure is formed via code.

Initialization only an issue when you’re using the workflow of authoring components.

Think about it this way: You’d be initializing in code in MonoBehaviours using an Awake or Start method for this particular piece of data. The authoring component cannot author runtime data. That makes no sense. You have authoring-initialized data, and you have runtime initialized data. They are not the same, and need to be initialized differently. Split them into separate components if you have to.

And [GenerateAuthoringComponent] is really just a prototyping tool for programmers. If you are working with a team of designers, you really want to give them custom-made authoring components that are intuitive to them.

2 Likes

Where is it officially documented/stated that GenerateAuthoringComponent is a tool for programmers to prototype?

Considering that in the latest releases, Unity is investing specifically into tools to assist the Hybrid workflow, I feel that Unity is steering ECS to be more tightly integrated into the editor rather than a purely coding exercise.

I feel GenerateAuthoringComponent is just a baby step into making a work flow (the implicit topic of this thread) that more tightly integrates with the way people make things in Unity.

If that is the case, and Unity is working on this work flow, then runtime initialization will need to be addressed.

Edit, evidence…
SubScene
GenerateAuthoringComponent
GameObjectConversionUtility
IDeclareReferencedPrefabs
DeclareReferencedAsset
GameObjectExportGroup
ConvertToEntity
AddHybridComponent
… and more

All that [GenerateAuthoringComponent] does is create something like (be aware of many typos):

public class SomeComponentAuthoring : MonoBehaviour, IConvertToEntity
{
    [SerializeField] private SomeComponentData someComponent;

    public void Convert(Entity e, EntityManager em, ConversionSystem system)
    {
        em.AddComponentData(e, someComponent);
    }
}

If you want anything more specialized than that, you should create your own MonoBehaviour with IConvertToEntity.

But, as @DreamingImLatios pointed out, the conversion system is not meant for things that rely on runtime data, it runs at conversion time, which usually means “when building the application or when clicking play inside the editor”. You can, however, read from ScriptableObjects from there (which is a good practice btw).

About GenerateAuthoringComponent being a tool for programmers and/or prototyping, this is not officially stated anywhere, but I mostly agree with that. A designer doesn’t want to put 10 components to get some functionality working (nor you want to remember those dependencies when creating a new prefab/gameobject) so you will want to create your own authoring components that are 1-to-many instead of relying purely on GenerateAuthoringComponent for every single component.

2 Likes

How do you imagine that working without much error?

Lets consider the existence of RequireComponent. Why does it exist?

We could all just write in our Start that if GetComponent() is null, then OK, we’ll go out and make it. But, the issue there is that it’s error prone. It’s also not something you can just search for, and then you’re back to making lots of boilerplate.

That’s my point: Making boilerplate is error prone, and distances the initialization into something else.

Lets imagine a hypothetical bullet hell game, where your unique feature is that there’s dozens of different types of enemies that have different bullets and attacks. Some enemy bullets move in straight lines, some zigzag around, others seek towards the player, or maybe explode within a radius of the player, etc etc etc, or even better with ECS in mind, a combination of these things.

Now, someone needs to make all of these dozens/hundreds of different bullet types/combinations, and test them.

Instead of just making prefabs for each bullet, with the added GenerateAuthoringComponent to define the behavior of the bullet… with your recommendation now you have to write a custom way to define sets of Components, set values that dictate their behavior, set which Component struct properties are runtime set (and hide those in the editor), store them, initialize them, and all the UI workflow to do that. (things that Prefabs and GenerateAuthoringComponent handle for you!)

All of that new code introduces a points of failure. Updates can’t exist in one place, they now exist in a number of places and instead of making your Bullet Hell game you’re now working with Boilerplate Hell software

Using GenerateAuthoringComponent is exactly the same as creating one MonoBehaviour with IConvertToEntity for each of your components already. As both are the same, both works with Prefabs in the same way.

If you created Authoring components that setups multiple components at once (like Unity.Physics components do) it is much less error-prone as you are gonna be centralizing the initialization of multiple dependent components in one place.

But, from what you are saying, seems like you do want to rely on runtime data (as you want to have optional components that should affect other components initialization), for that a reactive system fits better for your use case, but the complexity is the same as if you were gonna do that at conversion time (but instead of a using MonoBehaviour + IConvertToEntity, you will need to create a System + some way to diff unitialized data from already intialized ones).

No, I don’t want optional components.

I want clear, strongly typed markup that eliminates error prone boilerplate, that doesn’t pollute the project with extra files and structs/classes to maintain, and makes it clear that some (and which) Component types require runtime data to initialize.

Unfortunately, what you’re asking for is idealistic, not realistic.
You simply cannot have everything you’re asking for without sacrificing something. (Unless perhaps you did a ton of custom coding to create exactly the authoring system you needed… But then you’d have to maintain it, and BANG, sacrifices!).

Unity’s Authoring Components are pretty much the perfect middle ground when it comes to authoring Entities, and they offer a ton of flexibility when it comes to how you’re allowed to do it, too.

That being said — there could be some improvements. But making them would require a pretty serious rewrite of how authoring works (breaking all of the current authoring workflow for users) and a BIG paradigm shift in order for the changes to be made… So I doubt they would be made.

But hey, once a fully Pure DOTS Editor exists and is fully featured, there’s a chance the authoring workflow might be overhauled completely. I just wouldn’t bet on it happening anytime soon.

If you have any pratical example, it would be easier to help. To the example shown in the OP it is clearly a situation where creating your own authoring component (MonoBehaviour, IConverToEntity) is the right solution.

struct MyComponent
{
     float3 InitialPosition;
     float3 PlayerOriginalPosition;

}

public class MyComponentAuthoring : MonoBehaviour, IConvertToEntity
{
    public void Convert(Entity e, EntityManager em, ConversionSystem system)
    {
        em.AddComponentData(e, new MyComponent
        {
            InitialPosition = transform.position,
            PlayerOriginalPosition = MySingleton.GetPlayerPosition,
        });
    }
}

Yes, that’s what I have at the moment.

Tell me, how do I find out which Components need runtime instantiation data using this method?

How many extra classes will I need to maintain? (remember, MonoBehaviours and Components need to be one per file, with the same name)

This is the boilerplate I’m looking to get rid of, when really IConvertToEntity is just setting a few values. If it’s doing something more complex, or interacting with other MonoBehaviours, sure

MonoBehaviours need to be one per file, Components don’t (unless they have the [GenerateAuthoringComponent], which makes them have the same limitation as the MonoBehaviours).

The code I sent above is completely valid in a single file.

I keep my conversion pipeline and runtime logic separated so that I don’t need to worry about having 1-to-1 stuff everywhere (which would surely increase the complexity of things). Not sure why you need to find out which Components need “runtime instantiation data” (neither what you really mean with that), but if you have a working example I could think of something.

I’m working in the Hybrid workflow, and taking advantage of GenerateAuthoringComponent

This thread is about initializing data in that particular workflow, and reducing the boilerplate or code maintenance

I am not taking the thread off-topic… I work with the Hybrid workflow too, but GenerateAuthoringComponent has only one purpose, that is to reduce boilerplate in a specific use case (1-to-1 components to be set entirely through the inspector).

What I am trying to do is to understand your use case and try to help you achieve what you want/need. You could create some custom attributes and custom processing for those attributes to make it work like your suggestion in the OP, but this is not the way that the Hybrid workflow works currently, so you would need to maintain that separated system anyway.

Now, if your thread is about a feature request then I was not aware about it and I apologize for the interruption.

The best example is this:

You have an Origin and DistanceFromOrigin components. Both are IComponentData and both are assigned to a Prefab using GenerateAuthoringComponent.

Origin has just one thing, Position, which is set where the object is placed in the world and never set again. It’s simply the data for the creation position in world space.

DistanceFromOrigin has the value Distance, and uses DistanceFromOriginSystem to calculate the distance each frame.

Now, imagine if you looked at your project plan, and out of the 100+ IComponentData classes you’ve identified are needed, 48 of them need some data set at runtime.

Without a MonoBehaviour, or using Tags, or flag properties… Show me how to cleanly set the Origin.Position.

Because otherwise I’m going to have a project littered with boilerplate