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.
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
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:
This does in fact work!
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 + "°";
}
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With