technical architecture.

Unity: Toying with Scriptable objects and Custom Editors

by on May.18, 2011, under .net, csharp, Scripting, Unity

Expanding on the previous post about Scriptable Objects as an easy way to save/edit some data at runtime, here’s a small extension looking at an example for a simple custom editor that appears when you select the Scriptable Object we built previously in the Project view.

Here’s what you get clicking on the file previously:
Before Custom Editor

And the result with the custom editor:
with editor

Now, this isn’t the sexiest of examples, but hopefully it helps someone along the way!

First a small addition to the data class, lets add a constructor to it, since our input values are going to be basically the same every time (Mimic ‘this’ camera), we’ll add a secondary constructor for the class that just takes a unity Camera as input and records the values for us.

We’ll use the same ‘CameraLocationHolder.cs’ from below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//File: CameraLocation.cs
using UnityEngine;
using System.Collections;
//the Serializable attribute is key to what makes the custom class visible in the editor !
[System.Serializable]
public class CameraLocation
{
    public string name;
    public bool isOrtho;
    public float orthoSize;
    public float fov;
    public Vector3 position;
    public Quaternion rotation;
 
    //base constructor that builds an empty CamLoc
    public CameraLocation()
    {
        name = string.Empty;
        isOrtho = false;
        orthoSize = -1;
        fov = -1;
        position = Vector3.zero;
        rotation = Quaternion.identity;
    }
    //build a CamLoc from a supplied camera
    public CameraLocation(Camera c)
    {
        if (c)
        {
            name = "New Camera";
            isOrtho = c.isOrthoGraphic;
            orthoSize = c.orthographicSize;
            fov = c.fov;
            position = c.transform.position;
            rotation = c.transform.rotation;
        }
    }
}

And we replace the ‘CameraListboxWizard’ that we previously used to edit the Scriptable Object with this (very similar) ‘Custom Editor’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//File: CameraLocationEditor.cs -- Place in an Editor directory under the unity project
 
using UnityEngine;
using UnityEditor;
 
//this limits the editor to running on object that have type CameraLocationHolder
[CustomEditor(typeof(CameraLocationHolder))]
public class CameraLocationEditor : Editor {
 
    //This will just be a shortcut to the target, ex: the object you clicked on.
    private CameraLocationHolder db;
 
	void Awake()
	{
		db=(CameraLocationHolder)target;
	}
 
	public override void OnInspectorGUI()
	{
		GUILayout.BeginVertical();
        if (GUILayout.Button("Add current Camera.main position", GUILayout.Height(50)))
        {
			Camera m = Camera.main;
			if(m)
			{
                //Add the new camera to the list
                db.content.Add(new CameraLocation(m));
                //tell the unity editor that the list has changed and needs to be serialized
                EditorUtility.SetDirty(target);
			}			
        }
 
        GUILayout.Space(5);
        DisplayCurrentCamList();
        GUILayout.Space(5);
        GUILayout.EndVertical();
    }
 
    void DisplayCurrentCamList()
    {
		if (db.content.Count == 0)
			return;
 
        //For each of the cameralocations in the saved scriptable object db
        for (int i = 0; i < db.content.Count; i++ )
        {
            EditorGUILayout.BeginHorizontal();
 
            //A text field to rename the camera
            db.content[i].name = GUILayout.TextField(db.content[i].name, GUILayout.Width(200));
 
            //a few simple buttons to juggle the array order
            if (GUILayout.Button("UP"))
            {
                if (i > 0)
                {
                    CameraLocation item = db.content[i];
                    db.content.RemoveAt(i);
                    db.content.Insert(i - 1, item);
                }
            }
            if (GUILayout.Button("DN"))
            {
                if (i < db.content.Count)
                {
                    CameraLocation item = db.content[i];
                    db.content.RemoveAt(i);
                    db.content.Insert(i + 1, item);
                }
            }
            if (GUILayout.Button("Remove"))
            {
                db.content.Remove(db.content[i]);
            }
 
            //sets the maincamera to be at the selected cameralocation
            if (GUILayout.Button("MoveTo"))
            {
                MoveToView(db.content[i]);
            }
 
            //updates the cameralocation to match up with the Camera.main (Usable at runtime or at edit time)
            if (GUILayout.Button("UPDATE"))
            {
                UpdateView(db.content[i]);
			}	
			EditorUtility.SetDirty(target);
            EditorGUILayout.EndHorizontal();
        }
    }
 
	void MoveToView(CameraLocation cam)
	{
		Camera MainCamera = Camera.mainCamera;
	    Quaternion currentRotation = cam.rotation;
 
		//if we had an ortho cam, lets just set it there before we start moving.
		MainCamera.orthographicSize = cam.orthoSize;
		MainCamera.isOrthoGraphic = cam.isOrtho;
 
	    //a parent check incase we have a 'fpswalker' type script setup also
	    if (MainCamera.transform.parent)
	    {
	        //move the parent object
	        MainCamera.transform.parent.position = cam.position;
 
	        Vector3 ang = currentRotation.eulerAngles;
 
	        //rotate the parent object on the Y axis
	        MainCamera.transform.parent.rotation = Quaternion.Euler(0, ang.y , 0);
 
	        //rotate the child camera on the X and Z axis
	        MainCamera.transform.localRotation = Quaternion.Euler(ang.x , 0, ang.z );
 
	    }
	    else // there is no FPSWalker parent, so just move the camera itself
	    {
	        MainCamera.transform.position = cam.position;
	        MainCamera.transform.rotation = currentRotation;
	    }
 
	    MainCamera.fov = cam.fov;	
	}
 
	void UpdateView(CameraLocation cam)
	{
		Camera MainCamera = Camera.mainCamera;
	    cam.isOrtho = Camera.main.isOrthoGraphic;
		cam.orthoSize = Camera.main.orthographicSize;
		cam.position = Camera.main.transform.position;
    	cam.rotation = Camera.main.transform.rotation;
		cam.fov = MainCamera.fov;
	}	
}

Now having built that up, we can simply click on the asset file that we had created previously and see the editor in the Inspector panel, making it easier to handle multiple lists.


5 Comments for this entry

  • Dave Pentecost

    Dave – thanks for the clear tutorial here. I am developing a Unity-based “operating system” for a digital dome/planetarium in a a new community science center. A tool like this – to store and trigger camera locations – would help a lot. I will take a run at putting this together from your work, but if you had a working unitypackage of this you could send me, you’d save me some struggling! In any case, I’ll be watching for any updates, and let you know how I’m doing. Thanks again!

  • Dave Buchhofer

    Dave, glad to hear its helpful, its a very useful function that is a bit of a pain to describe in the docs for us people coming from a more artist’ey background.

    I don’t currently have it separated into a package yet (Mostly due to having the movement bits as part of the various control scripts currently, instead of a more generalized movement Component)

    If you have any questions feel free to ask, i’m sure there are a few snippets that i didn’t include in the post

    I think we talked briefly in Montreal last year, and i’d be happy to hear how the Dome is coming along!

  • Dave Pentecost

    Thanks, Dave! I’ll give it a try. And I’ll watch for you on Google+.

  • Sébastien 'Cb' Kuntz

    Hi Dave,
    your article made my day and saved me quite some time I’m sure 🙂
    Thanks for sharing and it was great finally meeting you!
    Cheers,
    cb

  • Dave Buchhofer

    Here is another article that was useful and goes into great detail (and a much more recent) about serialization, by Stramit at unity: http://forum.unity3d.com/threads/155352-Serialization-Best-Practices-Megapost?p=1064609

1 Trackback or Pingback for this entry

Leave a Reply

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!