- Home /
Accessing subclass properties in inspector based on enum
First of all, apologies for the poor title. I was having trouble describing the issue in short.
So here's the issue: I have a custom class named TargetArea which has a single Transform variable intended to hold a prefab to instantiate later. I then have a number of classes which inherit TargetArea (CircleTargetArea, LineTargetArea etc.) and each of these classes have their own variables for modifying their particular type of target area (circle has radius, line has width/length etc.).
On top of that I also have abilities which are all individual ScriptableObjects. Currently my abilities have a "Public TargetArea targetArea" variable which allows me to set the prefab, but what I want to do is to be able to select, in the inspector, which child of TargetArea is relevant for a particular ability, and then be able to input the corresponding values for that particular Type of TargetArea.
I've been looking at property drawers but it's the bit about selecting the child class that's throwing me off. I can't currently think of a way to approach this issue.
Any help is very much appreciated.
Also apologies for the lack of actual paragraphs. I didn't realize it would appear like that and I have no idea how to correct it.
Are you talking about type casting? With this you can hold a reference to parent type and then convert it to another type that derives from it.
So for example you can do something like this:
class TargetArea
{ }
class CircleTargetArea : TargetArea
{
public float Radius = 5f;
}
class Ability
{
public TargetArea targetArea;
void DoSomething()
{
CircleTargetArea circleTargetArea;
if(targetArea is CircleTargetArea) //this checks if targetArea can be cast to CircleTargetArea
{
circleTargetArea = targetArea as CircleTargetArea; //this is an actual cast that gives you an object of type CircleTargetArea
Debug.Log(circleTargetArea.Radius);
}
}
}
Thank you for your reply. I guess I should try to be clearer.
I'm primarily concerned with the inspector and modifying it to achieve the desired result. For each of my ability assets I would like to be able to select the relevant TargetArea type (circle, line, etc.) in the inspector. I envision a dropdown menu with all of the available types. After selecting the relevant type, I would like for the inspector to then display the properties for that type. (If I select circle, I would like to be able to see and edit its radius property. If I change my selection to line, radius should be replaced by line/width.)
This would allow me to edit target areas for each of my individual abilities in the inspector.
Ah, so you want to create a custom editor! Sorry, I'm dying of boredom in my work, so I may bi a bit sleepy.
What you want to do is create an enum with values coresponding to your types.
Then in your custom inspector you just have to check what values this enum has and display correct fields.
As for creating custom inspector you can check some Unity tutorials, they are very nice usually
https://unity3d.com/learn/tutorials/topics/interface-essentials/building-custom-inspector
Answer by purplekjw · Sep 03, 2018 at 04:02 PM
This is my solution this far. It almost meets my original criteria, but introduces a new and rather large problem which I'll explain at the bottom.
As can be seen in the images, the displayed properties change when the "Target Area Type" is changed. This was achieved with the follow custom editor:
[CustomEditor(typeof(Ability))]
public class AbilityEditor : Editor {
public override void OnInspectorGUI() {
DrawDefaultInspector();
Ability ability = (Ability)target;
ability.targetAreaType = (Ability.TargetAreaType)EditorGUILayout.EnumPopup("Target Area Type", ability.targetAreaType);
switch (ability.targetAreaType){
case Ability.TargetAreaType.circle:
CircleTargetArea circleTargetArea = ability.targetArea as CircleTargetArea;
if (circleTargetArea == null){
circleTargetArea = new CircleTargetArea();
Debug.Log("Create new circle target area");
}
//Create inspector fields for the properties unique to circle target area.
circleTargetArea.radius = EditorGUILayout.FloatField("Radius", circleTargetArea.radius);
//Stores changes to the ability property. Said property can then be accessed for all the information required to instantiate a target area.
ability.targetArea = circleTargetArea;
break;
case Ability.TargetAreaType.line:
LineTargetArea lineTargetArea = ability.targetArea as LineTargetArea;
if (lineTargetArea == null){
lineTargetArea = new LineTargetArea();
}
lineTargetArea.width = EditorGUILayout.FloatField("Width", lineTargetArea.width);
lineTargetArea.length = EditorGUILayout.FloatField("Length", lineTargetArea.length);
ability.targetArea = lineTargetArea;
break;
}
//Allows the prefab to be set. This property is in the parent class "TargetArea", so all children share it.
ability.targetArea.targetAreaPrefab = EditorGUILayout.ObjectField("Target Area Prefab", ability.targetArea.targetAreaPrefab, typeof(Transform), true) as Transform;
}
}
So I achieved what I originally set out to do, to an extent. However, if I'm understanding things correctly then subclasses cannot be serialized. Since CircleTargetArea and LineTargetArea are being stored on my Ability assets as a public TargetArea, the values that are unique to the subclasses are being lost as soon as I hit play. If anyone knows of a way to address this then any comments would be immensely appreciated. I'm wary of going off topic, but opening a new question seems to be overkill especially with the abundance of similar questions. (Despite said abundance, I have yet to find anything that has helped me in terms of finding a solution).