6

Say you have a trivial prefab, "Box", which we'll say is nothing more than a standard meter cube.

  • 1 - The prefab Box is in your Project panel

  • 2 - Drag it to the Scene

  • 3 - Obviously it now appears in the Hierarchy panel also, and probably selected and shown in Inspector

To be clear, game is NOT Play when you do this, you're only in ordinary Editor mode.

Is it possible to make a script (an "Editor script"?) so that,

  • when you do "1" and "2" above, (again this is in Editor mode, not during a game)

  • when 3 happens, we can affect the new Box item in the scene

  • So, simple example: we will set the Z position to "2" always, no matter where you drop it.

In short: Editor code so that every time you drag a prefab P to the scene, it sets the position z to 2.0.

Is this possible in Unity? I know nothing of "editor scripts".

It seems very obvious this should be possible.

11
  • that's not really correct, Jin ... or else, you'd have to show me what you mean in the whole code??
    – Fattie
    Commented Aug 29, 2018 at 7:22
  • 1
    I don't think there is a Unity API for this but I think you should add code of what you've tried to the question. I am saying this because of your last 3 previous questions including this one. Commented Aug 29, 2018 at 22:19
  • I've looked into this and it's possible to do but complicated. It's more than using Selection.activeGameObject and lots of code. You need a way to distinguish between when the object is actually dragged to the hierarchy and when the object is rearranged in the Hierarchy. Both of these triggers one callback function. You would also need to implement saving and loading of variables since this is an Editor plugin and they will reset anytime you click "Play" button leading to a bug
    – Programmer
    Commented Aug 29, 2018 at 22:55
  • Unity should add function for that. There is EditorApplication.hierarchyChanged but it's also called when the object is re-arranged in the scene. Actually, Selection.activeGameObject here is not unrelated because when you drag object from Project to Hierarchy tab, the object is automatically selected, therefore in Selection.activeGameObject. It can be used with EditorApplication.hierarchyChanged
    – Programmer
    Commented Aug 30, 2018 at 18:09
  • why is this so important to be in the scene editor ?
    – user2047029
    Commented Sep 6, 2018 at 12:51

4 Answers 4

10
+100

You can add a custom window editor which implements OnHierarchyChange to handle all the changes in the hierarchy window. This script must be inside the Editor folder. To make it work automatically make sure you have this window opened first.

using System.Linq;
using UnityEditor;
using UnityEngine;

public class HierarchyMonitorWindow : EditorWindow
{
    [MenuItem("Window/Hierarchy Monitor")]
    static void CreateWindow()
    {
        EditorWindow.GetWindow<HierarchyMonitorWindow>();
    }

    void OnHierarchyChange()
    {
        var addedObjects = Resources.FindObjectsOfTypeAll<MyScript>()
                                    .Where(x => x.isAdded < 2);

        foreach (var item in addedObjects)
        {
            //if (item.isAdded == 0) early setup
            
            if (item.isAdded == 1) {
                
                // do setup here,
                // will happen just after user releases mouse
                // will only happen once
                Vector3 p = transform.position;
                item.transform.position = new Vector3(p.x, 2f, p.z);
            }

            // finish with this:
            item.isAdded++;
        }
    }
}

I attached the following script to the box:

public class MyScript : MonoBehaviour {
    public int isAdded { get; set; }
}

Note that OnHierarchyChange is called twice (once when you start dragging the box onto the scene, and once when you release the mouse button) so isAdded is defined as an int to enable its comparison with 2. So you can also have initialization logic when x.isAdded < 1

4
  • This is the correct answer! Hooray! Well maybe there's another way, but this works perfectly. Note that you should check if(item.isAdded == 1) before doing the setup, or, it will do it twice (which may or may not be harmless).
    – Fattie
    Commented Sep 7, 2018 at 8:09
  • Hooray! Sent a bounty!
    – Fattie
    Commented Sep 7, 2018 at 8:14
  • @Fattie thanks for the correction and the bountiful bounty =)
    – Bizhan
    Commented Sep 7, 2018 at 8:15
  • notice also @Bizhan the answer by ikkentim which partially works, it is a useful quick solution I guess in some cases
    – Fattie
    Commented Sep 7, 2018 at 8:25
2

You could thy this:

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

[ExecuteInEditMode]
public class PrintAwake : MonoBehaviour
{
    #if UNITY_EDITOR
    void Awake()  .. Start() also works perfectly
    {
        if(!EditorApplication.isPlaying)
            Debug.Log("Editor causes this Awake");
    }
    #endif
}

See https://docs.unity3d.com/ScriptReference/ExecuteInEditMode.html


Analysis:

  1. This does in fact work!

  2. One problem! It happens when the object starts to exist, so, when you are dragging it to the scene, but before you let go. So in fact, if you specifically want to adjust the position in some way (snapping to a grid - whatever) it is not possible using this technique!

So, for example, this will work perfectly:

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

[ExecuteInEditMode]
public class PrintAwake : MonoBehaviour
{
    #if UNITY_EDITOR
    void Start() {
        if(!EditorApplication.isPlaying) {
            Debug.Log("Editor causes this START!!");
            RandomSpinSetup();
            }
    }
    #endif
    private void RandomSpinSetup() {
        float r = Random.Range(3,8) * 10f;
        transform.eulerAngles = new Vector3(0f, r, 0f);
        name = "Cube: " + r + "°";
    }
}

enter image description here

Note that this works correctly, that is to say it does "not run" when you actually Play the game. If you hit "Play" it won't then again set random spins :)

Great stuff

1
  • When playing in the editior, dragging a prefab with this component into the scene hierarchy will not do the random spins either. Therefore this is not useful for people who have a big project running and would like to sandbox-add existing prefabsto see things work. Am I right?
    – thomsky
    Commented Jul 19, 2023 at 15:09
1

Have a similar issue - wanted to do some stuff after object was dragged into scene (or scene was opened with already existed object). But in my case gameobject was disabled. So I couldn't use neither Awake, nor Start. Solved via akin of dirty trick - just used constructor for my MonoBehaviour class. Unity blocks any attempts to use most of API inside MonoBehaviour constructors, but we could just wait for some time, for example via EditorApplication.delayedCall. So code looks like this:

public class ExampleClass: MonoBehaviour
{

//...
// some runtime logic
//... 
   
#if UNITY_EDITOR

        //Constructor 
        ExampleClass()
        {
            EditorApplication.delayCall += DoSomeStuffForDisabledObjectAfterCreation;
        }

        void DoSomeStuffForDisabledObjectAfterCreation()
        {
            if (!isActiveAndEnabled)
            {
                //Some usefull stuff
            }
        }

#endif

}
0
-1

Monobehaviours have a Reset method, that only gets called in Editor mode, whenever you reset or first instantiate an object.

public class Example : MonoBehaviour
{
    void Reset()
    {
        transform.position =
           new Vector3(transform.position.x, 2.22f, transform.position.z);
        Debug.Log("Hi ...");
    }
}
3
  • 1
    Hmm - good one but ......... that almost works. Unfortunately it does NOT call the script when you add the prefab to the scene. It only calls the script when you add that component to an existing game object (or, explicitly click the small "Reset" button...)
    – Fattie
    Commented Aug 29, 2018 at 7:20
  • (Thus from the manual ... "Reset is called when the user hits the Reset button in the Inspector's context menu or when adding the component the first time." Unfortunately and sadly that is different from what you say, "...or first instantiate")
    – Fattie
    Commented Aug 29, 2018 at 7:21
  • 1
    Oh, good point, that's not a solution to your problem then! I'll leave this (wrong) answer as a warning to others.
    – Paul-Jan
    Commented Aug 29, 2018 at 7:24

Not the answer you're looking for? Browse other questions tagged or ask your own question.