Options for exposing custom objects in the inspector

Hey folks, I’m posting this here in the scripting forum since I’m not looking so much for specifics on what the Unity editor will do in particular instances as I am how people are exposing particular editable data structures for easy configuration through the inspector.

So, a scenario: I want to encapsulate various features of a texture animation (similar to this tutorial on the Unify wiki). What I want to do is provide a data structure that holds an array of indices, framerate, loop flag, etc. A GameObject could then have a collection of these animation definitions for different game states (run, jump, explode, etc). The ultimate goal here, however, is to be able to add/modify animations to the GameObject’s collection through the editor’s inspector.

I’ve tried a few different approaches and haven’t found one that I’m particularly fond of, so I thought I’d see if other people had settled on a nice solution for doing something similar. The things I’ve tried:

1.) First I tried creating a public class and marking it Serializable so that the inspector will render an editor for it. The class would look something like

[System.Serializable]
public class AnimStruct {
	public string name;
	public int[] frames;
	public int framerate;
	public bool loop;
}

…and the GameObject would have…

public AnimStruct[] animations;

…which would expose this and let me modify the array through the inspector. The problem I found with this method was that certain changes to the AnimStruct class could cause the array field in the GameObject to reset back to empty and loose all previously entered data. This wouldn’t be so much trouble if my system/classes were pretty much solid and I was just using them to build up my game, but until then having erasure as a possibility is annoying. The other issue is that certain types of data (int[ ], for example) are a pain to edit through the inspector - it would be wonderful to have a custom editor for modifying and previewing each animation right there in the inspector.

2.) The issues with the previous method seemed to point to having the AnimStruct extend MonoBehavior and be a full-fledged Component. Once you do this, though, the GameObject’s array no long renders the AnimStruct fields in the inspector (event when keeping the Serializable) - it just creates a drop-target for dropping GameObjects that have an AnimStruct component attached. You could drag multiple AnimStructs onto the GameObject itself though and each would be visible to modify in the inspector. You could write a custom editor and that would be visible for each one, too. Changing the AnimStruct script would just have each of those components update without losing any data (hopefully? I didn’t get quite far enough to test this before running into issues with this method). The problem with this method, though, is it seems like Unity treats Components sort of like facets of a GameObject. There doesn’t seem to be a way to grab references of just one of multiple components in a GameObject. For example, say the GameObject controller wants to hold a reference to one of its attached AnimStruct components as a default animation. Exposing the field and dragging one AnimStruct (of the many now on the GameObject) onto it just puts a reference to the GameObject - not the component! That means dragging AnimStructA is no different that dragging AnimStructB onto the field, and our default animation field is useless.

So, my question is this: If you have complex data that can potentially be collected in an array or list, whats the best way to expose that to the inspector (with bonus points for providing a hook to eventually add a custom editor)? Thanks so much folks.

To post some extra info as I’m fooling around with this - the first option seems the closest to getting something functional so I’m trying to get that to work. One of the nice things about the second method though was that you could use something like

void Start () {
controller = (AnimationController)GetComponent(typeof(AnimationController));
}

to grab a reference to other components on the GameObject for accessing higher-level properties. I’d like each AnimStruct instance to be able to check if the frame indices are out of bounds, but the texture information is housed in the controller script on the GameObject (the same script that declares the AnimStruct[ ] collection). When the AnimStruct isn’t a MonoBehavior/component, it doesn’t have access to the GetComponent() method. Since the user is creating new instances of AnimStruct through the inspector, there isn’t a chance to pass the controller or texture info through a constructor. Any ideas on how I could get a reference to the controller script into each AnimStruct in the array? I geuss worse case scenario I could put a public AnimationController field in the AnimStruct and then manually attach the controller to each new AnimStruct. This sort of sucks just to get some error checking though…

Also, when using AnimStruct as a plain old Serializable class, you get editable objects in the array in the inspector, but then fields you want to be references are editable too (and not drop-targets like I was hoping). I’d really like to open up the ‘AnimStruct[ ] animations’ array in the inspector and edit each animation’s properties. Then I’d like to drag one of the animations in the array onto a field ‘DefaultAnimation’ to save a reference for use in that specific situation. It seems like its either one or the other - is there a way to force the Unity editor to render fields specifically as a drop-target and/or an editable object? Anyways, thanks again for your time.

drop downs appear for where they make sense: enum fields :slight_smile:

don’t understand what you mean with editable object. Whats serializable will be shown in the inspector when a corresponding object is selected in the scene tree (in the project tree the depth of the tree is restricted to 1 basically)

But I guess what you potentially would prefer to do is creating a custom object inspector that is written specifically for your class to give you the best possible experience and highest flexibility.

I’ve got a controller script that I attach to the GameObject that I’d like to animate. The controller looks like the following:

public class AnimController : MonoBehaviour {

	//holds the Texture2D, along with some other info needed to grab a single frame
	public SpriteSheet SpriteSheet;
	
	//The list of this controller's available animations
	public AnimStruct[] anims;
	
	//reference to an AnimStruct in the anims array to use when no other animation is playing
	public AnimStruct idleAnimation;

	//...handle game state changes, select appropriate AnimStruct
	//from anims array, use its data to animate using the SpriteSheet...

}

Given the AnimStruct code I’ve got in the original post, the inspector looks something like the following (after adding some data in there):

This is about halfway to where I want to be - my player GameObject has this controller attached, and I can add a new available animation by changing the Anims size to 4. Then, I can open up the new AnimStruct represented there at the end and change the name and the frame indices that represent the animation.

You can see some of the issues I mentioned in the earlier posts: The Idle Animation shouldn’t be an expandable representation of the AnimStruct, it should be a drop-target that expects an AnimStruct dropped onto it. I want to be able to drag the first element from the Anims array onto Idle Animation and have it keep a reference. If its not possible to have both an editable array and a reference variable of the same type, is there a common way folks handle this situation? If I can’t stop the Idle Animation from being editable, I can live with that, but how can I get it to point to an AnimStruct that already exists in the Anims array?

Essentially, I really like being able to define the animations in the editor/inspector rather adding an additional script to the GameObject to add them programmatically in Start() or something. It sort of exposes the available states game entities have to work with when prototyping something out.