- Home /
Change Component Order via Script
Hi all,
Is there any way to change component order via script?
We can do this by right-clicking the component in the inspector, then select move up/down. But I need to do this by code. Is it possible that way?
To make it more specific, I have 2 components that use OnRenderImage(). They need to be called in a specific order, or else the resulting image is wrong. I have tried to set the script execution order but it has no effect.
Any help is appreciated. Thanks!
If you can't do this, should be able to combine the code into one script. For example, rename them doAfterRender and have a new OnRenderImage in some script call both.
See also (undocumented) UnityEditorInternal.ComponentUtility.$$anonymous$$oveComponentUp/Down. (Editor code only, not runtime.)
Answer by vexe · Dec 27, 2013 at 12:30 PM
One way to do this is by attaching a component, that references all the other components attached to the same gameObject, and then via some magical custom editor stuff, you could get something like this (an example of what I'm working on):
You could see those "U" and "D" buttons, which stand for Up/Down. You could use this object to sort the other components, and then when you want to render - you take the order of your components, from that component.
If this solutions works for you, let me know and I'll post the code I used to get this custom editor.
So, here's what I worked out for you:
Here's the sorter component, for which I will make a custom editor for - it just has a list of components:
using UnityEngine;
using System.Collections.Generic;
public class ComponentSorter : MonoBehaviour
{
[HideInInspector]
public List<Component> components;
void OnGUI()
{
for (int i = 0; i < components.Count; i++) {
GUI.Label(new Rect(Screen.width / 2, i * 25, 500, 50), components[i].ToString());
}
}
}
And, here's where the magic happens:
ComponentSorterEditor.cs (put this in your Editor folder, if you don't have one, create it - "Assets/Editor")
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
[CustomEditor(typeof(ComponentSorter))]
public class ComponentSorterEditor : Editor
{
private SerializedProperty components;
private void OnEnable()
{
components = serializedObject.FindProperty("components");
}
public override void OnInspectorGUI()
{
DrawDefaultInspector();
serializedObject.Update();
PopulateComponents();
GUILayout.Label(components.name, EditorStyles.boldLabel);
for (int i = 0; i < components.arraySize; i++) {
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.PropertyField(components.GetAt(i));
if (GUILayout.Button("D", GUILayout.Width(20f))) {
components.MoveDown(i);
}
if (GUILayout.Button("U", GUILayout.Width(20f))) {
components.MoveUp(i);
}
}
EditorGUILayout.EndHorizontal();
}
serializedObject.ApplyModifiedProperties();
}
private void PopulateComponents()
{
components.RemoveNullObjects();
var targetGo = (serializedObject.targetObject as ComponentSorter).gameObject;
var comps = targetGo.GetComponents<Component>();
foreach (var comp in comps) {
if (!components.Contains(comp)) {
components.Add(comp);
}
}
}
I think the code is pretty straight forward. One thing to note here, RemoveNullObjects
is useful so that, if you remove a component, the components list will get populated accordingly in a way that you won't see nulls as the objects values.
See this video, to see what I'm talking about. (I solved it on the fly in a redundant way in the video, there was no need to clear out the whole array)
Here's the SerializedProperty extension methods. Don't be scared, they're just there to make your life easier. Feel free to modify.
One thing you'd probably wanna do is remove the ComponentSorter
reference from the components themselves. That could be easily achieved by adding one more condition to the if that's adding the comps:
if (!components.Contains(comp) && !(comp is ComponentSorter)) {
components.Add(comp);
}
When you want to render your stuff, you just do:
var orderedComps = myStuffGo.GetComponent<ComponentSorter>().components;
Hope you like it :)
Answering your comment:
You'll get serializedObject
for free when you inherit from Editor
which is where its declared. As its name suggests, it's of type SerializedObject
If you don't want to use the custom editor, then you won't get a visual representation of what's going on.
If you insist, you could use the extension methods I have MoveUp
and MoveDown
methods 'inside' ComponentSorter
:
They just need some modifications:
public static List<T> Swap<T>(this List<T> list, int i, int j)
{
T temp = list[i];
list[i] = list[j];
list[j] = temp;
return list;
}
Codes are not tested from this point on **
public class ComponentSorter : MonoBehaviour { public List components; public void MoveUp(int fromIndex) { int previous = fromIndex - 1; if (previous < 0) previous = prop.arraySize - 1; components.Swap(fromIndex, previous); } public void MoveDown(int fromIndex) { int next = (fromIndex + 1) % prop.arraySize; components.Swap(fromIndex, next); } }
(You could actually make the MoveUp
and MoveDown
list extensions as well, I'd go for that...)
If you don't like moving them by index, you could do:
public void Move(Component c, bool up)
{
int index = components.IndexOf(c);
if (index == -1) return; // component not found;
if (up) MoveUp(index);
else MoveDown(index);
}
So... wanna move the Transform
component? you could do:
Move(components.Find(c => c is Transform) as Transform, true); // don't forget System.Linq;
One more thing you could do, write a generic move method:
public void Move<T>(bool up) where T : Component
{
int index = components.FindIndex(c => c.GetType() == typeof(T)); // if you want to get any children of 'T' do "c => c.GetType().IsSubclassOf(typeof(T))
if (index == -1) return; // component not found;
if (up) MoveUp(index);
else MoveDown(index);
}
Use it like:
// for pure convenience
private const bool UP = true;
private const bool DOWN = false;
Move<Transform>(UP);
I suggest you keep the custom editor - it's very useful. If you do remove it, then you have to move the population of the components array, to the sorter as well...
Sorry, but what I need is runtime code, not editor code.
You could change their order at runtime as well, via code. And then call GetOrderdComponents
to get the components, in the order that they're sorted in.
Well, it's worth a shot I suppose. Do you $$anonymous$$d if I asked for the code? :D
It's possible to move the component order with a couple of undocumented methods but this applies to editor only:
UnityEditorInternal.ComponentUtility.$$anonymous$$oveComponentUp (someComponent);
UnityEditorInternal.ComponentUtility.$$anonymous$$oveComponentDown (someComponent);
Thought I'd mention it since the question touches on it, but unfortunately this won't work at runtime, which is the requirement for the OP. It may be helpful to other people. Use carefully, the name strongly indicates it's not meant to be publicly used. It may mean that the API may move or disappear some time in the future. It may also mean that you won't get little support, should the API contain bugs. I've used it for my own purposes and I can tell you at least it solved my problems (used Unity 4.3.1 at the time).
Answer by idbrii · May 17, 2018 at 05:40 PM
Use the undocumented UnityEditorInternal.ComponentUtility.MoveComponentUp/Down
to move components around at edit time. (Thanks to Statement for the tip.)
static void MoveUp()
{
var sel = Selection.activeGameObject;
var target_component = sel.GetComponent<MoveThisKindOfComponent>();
UnityEditorInternal.ComponentUtility.MoveComponentUp(target_component);
}
You could make an Editor with a button to fix the component order on the selected object (or search for objects that need to be fixed).
Answer by Checkman44 · Jan 18, 2021 at 02:24 PM
I wrote a ComponentSorter component that sorts components by name. I used bubble sort because we can only move components up and down. After each component switch, I call GetComponents again so we have the same list order as in editor UI.
public class ComponentSorter : MonoBehaviour
{
public void Sort()
{
var components = GetComponents<Component>().ToList();
components.RemoveAll(x => x.GetType().ToString() == "UnityEngine.Transform");
for (int p = 0; p <= components.Count - 2; p++)
{
for (int i = 0; i <= components.Count - 2; i++)
{
Component c1 = components[i];
Component c2 = components[i + 1];
string name1 = c1.GetType().ToString().Split('.').Last();
string name2 = c2.GetType().ToString().Split('.').Last();
if (string.Compare(name1, name2) == 1)
{
ComponentUtility.MoveComponentUp(c2);
components = GetComponents<Component>().ToList();
components.RemoveAll(x => x.GetType().ToString() == "UnityEngine.Transform");
}
}
}
for (int i = 0; i < components.Count; i++)
{
ComponentUtility.MoveComponentUp(this);
}
}
}
And the custom editor class with the button:
[CustomEditor(typeof(ComponentSorter))]
public class ComponentSorterEditor: Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
ComponentSorter myScript = (ComponentSorter)target;
if (GUILayout.Button("Sort Components"))
{
myScript.Sort();
}
}
}
Answer by miracalious · Apr 22, 2021 at 11:36 PM
For anyone still reading this looking to simply move a component up or down in editor code (for doing something like ensuring background images show up behind text, etc):
UnityEditorInternal.ComponentUtility.MoveComponentUp(yourComponentReference);
UnityEditorInternal.ComponentUtility.MoveComponentDown(yourComponentReference);
Your answer
Follow this Question
Related Questions
2D Animation does not start 1 Answer
iPhone UI Components? 2 Answers
How to get the first component that implements a specific interface ? 4 Answers
Object reference not set to instance of an object 2 Answers
Using static typing for a Component 1 Answer