Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question Roslyn source generators and the generation of ECS Authoring like components

Discussion in 'Entity Component System' started by Noek1993, Jul 23, 2022.

  1. Noek1993

    Noek1993

    Joined:
    Jan 22, 2015
    Posts:
    25
    Hello,

    Recently I started digging into the code generator part of the Unity ECS, and I wanted to achieve something similar. Specifically the part where Authoring components are generated from written code and how they can be assigned from the editor.

    The generating of code I did figure out by digging into the ECS source code, together with this unity page:
    https://docs.unity3d.com/Manual/roslyn-analyzers.html

    However I can not figure out how to reproduce the behaviour where when you can drag an IComponentData script to a gameobject that the Authoring MonoBehaviour component gets added to the GameObject.
    Where can I find this in the ECS source? And how can I create a link between the written and generated code like this my self?

    Thanks
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,647
    I'm not sure how you can do this automatically, but I can show you can add them via code using a call to the menu and maybe you can reverse it from there to figure it out

    Code (CSharp):
    1. /// <summary>
    2.         /// Add a component to of GameObject.
    3.         /// This is intended for use on entity components generated via <see cref="GenerateAuthoringComponentAttribute"/>.
    4.         /// </summary>
    5.         /// <remarks> Note this only works on types in an asmdef. </remarks>
    6.         /// <param name="gameObject"> GameObject to add the component type to. </param>
    7.         /// <param name="type"> The component type to add. </param>
    8.         public static void AddAuthoringComponent(GameObject gameObject, Type type)
    9.         {
    10.             AddAuthoringComponent(new[] { gameObject }, type);
    11.         }
    12.  
    13.         /// <summary>
    14.         /// Add a component to a set of GameObjects.
    15.         /// This is intended for use on entity components generated via <see cref="GenerateAuthoringComponentAttribute"/>.
    16.         /// </summary>
    17.         /// <remarks> Note this only works on types in an asmdef. </remarks>
    18.         /// <param name="gameObjects"> GameObjects to add the component type to. </param>
    19.         /// <param name="type"> The component type to add. </param>
    20.         public static void AddAuthoringComponent(GameObject[] gameObjects, Type type)
    21.         {
    22.             var executeMethod = typeof(EditorApplication).GetMethod("ExecuteMenuItemOnGameObjects", BindingFlags.Static | BindingFlags.NonPublic);
    23.             Assert.IsNotNull(executeMethod, "ExecuteMenuItemOnGameObjects has been removed");
    24.  
    25.             var assemblyName = type.Assembly.GetName().Name;
    26.             var componentName = type.Name.ToSentence();
    27.             var file = $"Component/Scripts/{assemblyName}/{componentName}";
    28.  
    29.             executeMethod.Invoke(null, new object[] { file, gameObjects });
    30.         }
    For example if you had this code

    Code (CSharp):
    1.     namespace MyNameSpace.Generate
    2.     {
    3.         [GenerateAuthoringComponent]
    4.         public struct TestComponent : IComponentData
    5.         {
    6.             public int Value;
    7.         }
    8.     }
    You could add it to a gameobject calling the above method.

    The file string would look like this -> Component/Scripts/MyNameSpace.Generate/Test Component
    The conversion to sentence is required.
     
    PhilSA likes this.
  3. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I'm having similar difficulties, but not exactly the same. I'm trying to codegen authoring components for special structs in my project, but the generated Monobehaviours never appear as options in the "AddComponent" button. I made sure the generated filename corresponds exactly to the generated class name too (it's a requirement for monobehaviours to work)

    _____

    If it can help, given the following component in user code:
    Code (CSharp):
    1. [Serializable]
    2. [GenerateAuthoringComponent]
    3. public struct AuthTest : IComponentData
    4. {
    5.     public int a;
    6.  
    7. }
    Here's what gets generated by Unity's built-in [GenerateAuthoringComponent] source generators:
    Code (CSharp):
    1. #line 1 "Temp\GeneratedCode\MyAssemblyName"
    2. [UnityEngine.DisallowMultipleComponent]
    3. [global::System.Runtime.CompilerServices.CompilerGenerated]
    4. [System.SerializableAttribute]
    5. public class AuthTestAuthoring : UnityEngine.MonoBehaviour, Unity.Entities.IConvertGameObjectToEntity
    6. {
    7.     [Unity.Entities.RegisterBinding(typeof(AuthTest), "a")]
    8.     public int a;
    9.     public void Convert(Unity.Entities.Entity __entity, Unity.Entities.EntityManager __dstManager, GameObjectConversionSystem __conversionSystem)
    10.     {
    11.         AuthTest component = default(AuthTest);
    12.         component.a = a;
    13.         __dstManager.AddComponentData(__entity, component);
    14.     }
    15. }
    The sourcegenerator code for this is in "Library\PackageCache\com.unity.entities@0.51.0-preview.32\Unity.Entities\SourceGenerators\Source~\AuthoringComponent\AuthoringComponentGenerator.cs"

    I've tried to copy this exactly, but still no success so far. I'm guessing maybe there might be some specific path/naming requirements for this to work
     
    Last edited: Jul 24, 2022
  4. Noek1993

    Noek1993

    Joined:
    Jan 22, 2015
    Posts:
    25
    @tertle the code show seems to just call the add component of that file.
    What I want to is make sure that add adds my custom generated file, not a Unity generated authoring component.

    This sounds quite similar to what I want to do, just for other types of script.
    I have been using that exact sourcecode generator as an example, with no succes.

    I have been looking at this method and actually copied it to my own custom generator. Sadly using that generator and then using my own generator name does not seem to make my Roslyn generated monobehaviour addable in any way.

    I have been looking for usages of the GenerateAuthoringComponent in the Enties code to see what I could find, but the only use was really the source generators and a lot of tests. What I did find was the binding of variables `Unity.Entities.RegisterBinding` but that does not seem to be related to this.

    An option would be to use C#'s partial class system, make a MonoBehaviour myself and generate half the code, but that does still require me to make all the class myself. I have tested it but this seems to what is done with the job interface and code generation.
     
    Anthiese likes this.
  5. Noek1993

    Noek1993

    Joined:
    Jan 22, 2015
    Posts:
    25
    A 'hack' I found was to add an `UnityEngine.AddComponentMenu` attribute to the generated code, this way it will popup in the editor and it will be addable, however its not addable by dragging the source script.
     
    PhilSA likes this.
  6. ZaneLou2020

    ZaneLou2020

    Joined:
    Oct 2, 2020
    Posts:
    11
    How about this?

    Code (CSharp):
    1.  
    2.  
    3. public class AuthTestAuthoring : UnityEngine.MonoBehaviour, Unity.Entities.IConvertGameObjectToEntity
    4. {
    5.  
    6.     public Entity destroyEntity;
    7.     public Unity.Entities.EntityManager entityManager;
    8.     public int a;
    9.  
    10.  
    11.     public void Convert(Unity.Entities.Entity __entity, Unity.Entities.EntityManager __dstManager, GameObjectConversionSystem __conversionSystem)
    12.     {
    13.         destroyEntity = __entity;
    14.         entityManager = __dstManager;
    15.  
    16.         Entity euthTestEntity = entityManager.CreateEntity();
    17.         AuthTest component = default(AuthTest);
    18.         component.a = a;
    19.         __dstManager.AddComponentData(euthTestEntity, component);
    20.     }
    21.  
    22.     private void OnDestroy()
    23.     {
    24.          entityManager.DestroyEntity(destroyEntity);
    25.     }        
    26.  
    27. }
    28.  
    29.  
    30.  
    31.  
     
  7. BitWrenge

    BitWrenge

    Joined:
    May 15, 2019
    Posts:
    2
    Any luck with adding components to "AddComponent" button?
    UnityEngine.AddComponentMenu attribute doesn't help.