- Home /
How do you make a custom inspector for a class or instance?
I have a class that looks like this:
[Serializable]
public class Encounter{
public string[] encounterEnemy;
}
I want custom inspector stuff that shows up when this class is part of other classes, arrays etc (in this case, I want the encounterEnemy to show up as a drop down list, but that is not really important).
I can make custom inspector GUI for variables found in the main body of scripts, but I can't seem to do anything with any other classes, even if they inherit from monobehavior.
Here is an example of something I am trying that does not work:
The script named TestScript
using UnityEngine;
using System.Collections;
using System;
public class TestClass : MonoBehaviour{
public int testInt;
}
The script named TestClassEditor
using UnityEngine;
using UnityEditor;
using System.Collections;
[CanEditMultipleObjects]
[CustomEditor(typeof(TestClass))]//notice that this refers to the class declaration and not the main script
public class TestClassEditor : Editor {
SerializedObject testClass;
SerializedProperty testInt;
string[] junk = {"things","stuff","whatever"};
void OnEnable()
{
testClass = new SerializedObject(target);
testInt = testClass.FindProperty("testInt");
}
public override void OnInspectorGUI () {
testClass.Update();
testInt.intValue = EditorGUILayout.Popup("Enemy",testInt.intValue,junk);
testClass.ApplyModifiedProperties();
}
}
public class TestScript : MonoBehaviour {
public TestClass testClassInst;
}
Other posts (I have searched extensively) seem to suggest that doing this for instances of the class(not the class itself) but I have not been able to figure out how that might be done in an array or as part of another class.
I have no knowledge on this, but maybe there's something in this link that may help :
http://catlikecoding.com/unity/tutorials/star/
I could be entirely missing the point, but that tutorial was my intro to Custom Inspector/Editor.
This is the best Custom Inspector/Editor tutorial I was able to find. It got me to the point where I could even ask this question.
Answer by Loius · Jun 28, 2012 at 09:11 PM
Is this what you're looking for, or did I misinterpret the question? (Maybe it's a C#-specific issue? Unityscript is so lazy by comparison but I don't trust my C#-ing for inspectors yet)
I don't know of a way to write an Inspector for an inner class, but I've been working around it for a while this way:
[SomeEncounters.js]
class SomeEncounters extends MonoBehaviour {
class Encounter {
var monster : int;
function InspectorGUI() {
monster = EditorGUILayout.Popup( ... );
}
}
var encounter : Encounter;
}
[SEEdit.js]
@CustomEditor(SomeEncounters)
class SEEdit extends Editor {
function OnInspectorGUI() {
super.OnInspectorGUI();
target.encounter.InspectorGUI();
}
}
You'd still have to write the custom inspector for any class that uses the encounter (SomeEncounters in this case), but this is how I've been making class-specific inspectors for a while now.
You could have an array of Encounters, just have to set the size with an IntField and call the InspectorGUI on each element.
Hey, I actually don't know what super is. Can your break it down for me? As for setting this stuff up in a function of the class, that is a great way to deal with this. Writing a custom inspector for every script that uses that class is no problem. I actually figured out how to write an inspector for an inner class. I will post the code shortly, but your solution is still probably superior because this thing is several levels deep (you will see when I post the code). Anyway, many thanks.
The equivalent in C# is "base" (I had to go look it up, heh). It's a reference to "this", but as the parent type. So the 'super' in that snippet refers to Editor, meaning it calls Editor.InspectorGUI - which draws the default inspector. It's usually convenient to draw the default inspector first, and if you get varibales showing up in the default inspector that you don't want, you can use @HideInInspector:
[file.cs]
[HideInInspector]
public bool x; // this won't show up in the default inspector, but your custom inspector is certainly free to use it
Answer by SmooveB · Jun 29, 2012 at 12:16 AM
This is what I ultimately did to write an inspector for a class that was part of an array that was part of several other classes. If you don't want to search through the fairly ugly code, the takeaway is that you can use SerializedProperty Methods GetArrayelementAtIndex() and FindPropertyRelative() to and the property arraySize to drill down through Serialized classes and arrays in basically the same way you would in your regular code. Infact, you could use regular array methods if you are just setting things directly with target. Note that this is very ugly in the Inspector because I don't really understand formatting yet.
[Serializable]
public class Enemy{ //this is not the same as enemy in the editor script which is just a string
public string name;
public GameObject prefab;
public int health;
public int curHealth;
public int damage;
public void SetFromPrefab()
{
name = prefab.name;
EnemyBaseStats enemyBaseStats = prefab.GetComponent<EnemyBaseStats>();
health = enemyBaseStats.health;
curHealth = enemyBaseStats.curHealth;
damage = enemyBaseStats.damage;
}
}
[Serializable]
public class Encounter{
public string[] encounterEnemies;
}
[Serializable]
public class Zone{
public string name;
public int encounterPercent;
public Encounter[] zoneEncounters = new Encounter[0];
}
[Serializable]
public class PC{
public string name;
public GameObject PCPrefab;
public Ability[] abilities;
public int damage;
public int health;
public int maxHealth;
}
public class Uni : MonoBehaviour {
public Enemy[] preAllEnemies;
public Zone[] preZones;
}
Then the Editor
[CustomEditor(typeof(Uni))]
[CanEditMultipleObjects]
public class DropDownMenu : Editor {
private int index;
private string[] preAllEnemiesString;
private Uni uniEdNS;
private SerializedObject uniSR;
private SerializedProperty
preZones;
void OnEnable()
{
uniSR = new SerializedObject(target);
preZones = uniSR.FindProperty("preZones");
uniEdNS = (Uni)target;
preAllEnemiesString = new string[uniEdNS.preAllEnemies.Length];
for(int x = 0; x < uniEdNS.preAllEnemies.Length; x++)
{
preAllEnemiesString[x] = uniEdNS.preAllEnemies[x].prefab.name;
}
}
public override void OnInspectorGUI () {
uniSR.Update();
for (int k = 0; k < preZones.arraySize; k++) //SerializedProperty has its own properties and methods for dealing with arrays: arraySize, GetArrayElementAtIndex() are used to drill down through my class to get to the strings
{
EditorGUILayout.LabelField("Zone " + k); //these are just labels to help organize what this looks like in the inspector window. It is really ugly right now because I don't yet understand formating
SerializedProperty zone = preZones.GetArrayElementAtIndex(k);
SerializedProperty encounters = zone.FindPropertyRelative("zoneEncounters");//I did not think this would work but it did. FindPropertyRelative uses the path of the SerializedProperty (in this case whichever zone is currently being delt with) and adds the argument to get a SerializedProperty one level down
for (int l = 0; l < encounters.arraySize; l++)
{
EditorGUILayout.LabelField("encounter " + l);
EditorGUILayout.BeginVertical();//more formatting
SerializedProperty encounter = encounters.GetArrayElementAtIndex(l);
SerializedProperty encounterEnemies = encounter.FindPropertyRelative("encounterEnemies");
for(int m = 0; m < encounterEnemies.arraySize; m++)
{
SerializedProperty enemy = encounterEnemies.GetArrayElementAtIndex(m);
for(int j = 0; j < preAllEnemiesString.Length; j++)//the way I have it set up, the value that is actualy stored is the string. But popups use and return ints. This checks the stored string and sets the popup accordingly
{
if (enemy.stringValue == preAllEnemiesString[j])
{
index = j;
}
}
index = EditorGUILayout.Popup("Enemy",index,preAllEnemiesString);
enemy.stringValue = preAllEnemiesString[index];// this uses the value I get from the popup as an index to get the correct string. I can move elements of preAllEnemies around and no harm will be done to the enemy values, but if a value is not found in the preAllEnemies list, the enemy will revert to 0.
}
EditorGUILayout.EndVertical();
}
}
uniSR.ApplyModifiedProperties();
DrawDefaultInspector ();
}
}
Answer by Eric5h5 · Jun 28, 2012 at 05:30 AM
You can't yet; wait for Unity 4.
That would explain why I have spent the last 2 days failing at this task. Do you have any other suggestions about alternative ways to accomplish my real goal of having drop down menus in my string array (inside an instance of a class)?
Hmm. Not unless I can somehow create the enum from a list of strings. The situation is this: I have monsters for an old school jrpg. They each have a prefab that has their stats template and their art etc (I have also considered just have them be objects and skipping the prefabs) These prefabs go into a Dictionary with a string key (I could make it an array). There could be 500+ monsters by the end of the thing, so maintaining a separate enum is asking for trouble.
Each zone has 5 to 10 pre set encounters. One aspect of each encounter is the enemies in that encounter. I wanted a drop down list to make it easy to select enemies for each position in the encounter. Obviously, enums would work fine for this, I would just have to convert to string when I went to set up for an actual encounter. The problem is just that creating a separate list of enums is out of the question and I don't see a way to mix an enum into the enemy Dictionary (or array) or to create an enum list from that Dictionary. Thoughts?
Your answer
Follow this Question
Related Questions
Inspector button for custom class 1 Answer
Get if inspector field labels are on separate line 1 Answer
Can i extend or customise editor for all MonoBehaviours? 3 Answers
How can I create a list of preset class/objects to be used in the editor 1 Answer
Call inspector on custom window editor 4 Answers