Search Unity

  1. We are migrating the Unity Forums to Unity Discussions. On July 12, the Unity Forums will become read-only.

    Please, do not make any changes to your username or email addresses at id.unity.com during this transition time.

    It's still possible to reply to existing private message conversations during the migration, but any new replies you post will be missing after the main migration is complete. We'll do our best to migrate these messages in a follow-up step.

    On July 15, Unity Discussions will become read-only until July 18, when the new design and the migrated forum contents will go live.


    Read our full announcement for more information and let us know if you have any questions.

Question Callback on AddComponent() impossible in Play mode or build?

Discussion in 'Scripting' started by VRARDAJ, Jun 13, 2024.

  1. VRARDAJ

    VRARDAJ

    Joined:
    Jul 25, 2017
    Posts:
    95
    I'm trying to get a callback upon AddComponent() execution. I know of two UnityEditor callbacks that claim to do this. Only one of them is working, and neither will work in a build since they're both under the editor namespace. Is there no way to call back on AddComponent() in a build?

    Here's what I've tried:

    Code (CSharp):
    1. [InitializeOnLoadMethod]
    2. public static void Init() => ObjectChangeEvents.changesPublished += OnChangesPublished;
    3.  
    4. private static void OnChangesPublished(ref ObjectChangeEventStream stream)
    5. {
    6.     for (int i = 0; i < stream.length; ++i)
    7.     {
    8.         case ObjectChangeKind.ChangeGameObjectOrComponentProperties:
    9.             stream.GetChangeGameObjectOrComponentPropertiesEvent(i, out var changeGameObjectOrComponent);
    10.                 var goOrComponent = EditorUtility.InstanceIDToObject(changeGameObjectOrComponent.instanceId);
    11.                 if (goOrComponent is Component component)
    12.                        Debug.Log($"{type}: Component {component} change properties in scene {changeGameObjectOrComponent.scene}.");
    13.                 break;
    14.     }
    15. }
    This is taken directly from the GitHub example usage for the changesPublished callback. It doesn't call back. Most of the other cases in the changesPublished callback do call back, but not this one for some reason.

    ObjectFactory.componentWasAdded callback does call back, but not in Play mode. I would have guessed it could execute in play mode in Editor, but it doesn't:

    Code (CSharp):
    1. [InitializeOnLoadMethod]
    2. public static void Init() => ObjectFactory.componentWasAdded += OnComponentAdded;
    3.  
    4. private static void OnComponentAdded(Component component)
    5.     => Debug.Log($"{component.GetType().Name} was added to {component.gameObject.name}");
    6.  
    This is in Unity 2022.3 LTS.
     
    Last edited: Jun 13, 2024
  2. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,661
    There is no runtime event or message sent when a component is added or removed. (Barring the messages sent to the component itself.)
     
  3. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    7,263
    You are working with 3rd party code that cannot be modified and uses AddComponent a lot?

    Because in the other cases you can make an event yourself that interested parties can subscribe to. If you need it in many places then create a central class that does nothing but AddComponent and invoking OnComponentAdded.
     
    VRARDAJ likes this.
  4. VRARDAJ

    VRARDAJ

    Joined:
    Jul 25, 2017
    Posts:
    95
    I'm making a plugin that needs to assign default serialized fields on the developer's behalf when they call AddComponent() on certain types of components which derive from those in the plugin. This works automagically w/ the AddComponent button in Inspector, since I've assigned the default references for said scripts prior to them pressing the button, but in Play mode and at runtime, default references aren't available because AFAIK they're only accessible via the MonoImporter class in the UnityEditor namespace.

    The only alternative I can find so far is to lazy-initialize those fields on their first invocation rather than on init, but to do so, I need to either use Resources.Load() or install Addressables into the plugin. I was hoping to not need to do either, but it seems like the only way.
     
  5. VRARDAJ

    VRARDAJ

    Joined:
    Jul 25, 2017
    Posts:
    95
    This was the primary impetus for writing that other thread about wanting actions tied to event functions. UniRx can ape callbacks for OnEnable and OnDisable, but these scripts might have the invocations in Start/OnEnable that'll throw null if I don't get ahead of them, so my first thought was... maybe I can ape my own callback the way UniRx does, but do it for Awake() rather than OnEnable. And in investigating that, I found there's a reason UniRx doesn't have an Awake trigger: it's because it's not possible to make one AFAIK, given the current functionality of MonoBehaviour.