Adding To Array From Editor

I have an editor script that displays a window where you enter dialog for the characters to say, I have another script called dialog controller which displays the dialog in the game. What I want is to be able to type stuff here and then hit an add button and it will add the string as an element in the array in the dialog controller script. By the way the array is a JS array, if that helps any. I’m not getting any errors but the code simply isn’t adding to the array. What am I doing wrong here?

import UnityEditor;

@CustomEditor(DialogController)
var dialog : String = "Enter Dialog";
var player  : SerializedProperty;

class MyPlayerEditor extends Editor {

    function OnEnable () {
        // Setup the SerializedProperties
        player = serializedObject.FindProperty ("playerDialog");
    }

}
class Dialog extends EditorWindow {
    
    
    @MenuItem("Tools/Dialog Editor")
    static function Init() {
        var window = GetWindow(Dialog);
        window.Show();
    }
    
    function OnGUI() {
        dialog = EditorGUI.TextArea(Rect(3,3,position.width - 6, position.height - 65), dialog);
        if(GUI.Button(Rect(0, position.height - 60, position.width, 25), "Add"))
            player.Add (dialog);
        if(GUI.Button(Rect(0, position.height - 30, position.width, 25), "Close"))
            this.Close();
    }    
}

It might be worth you taking a look at the documentation for SerializedObject and SerializedProperty. Here are a few things to consider:

  • call update on your serializedObject before testing and changing properties.
  • work with the serialized property’s methods to make changes: InsertArrayElementAtIndex should be useful to you
  • when you’re finished a round of changes, call ApplyModifiedProperties on the object

EDIT TO EDIT - As also discussed, it does seem a little unnecessary to create a custom editor window for this use case, however for the sake of completeness I have written one (in C#) and added a couple of features. As the answer to this question is now somewhat sprawling I have added the source to github, and it can be found here

EDIT - Added a javascript equivalent. As mentioned below, it really is worth reading Extending the Editor, SerializedObject and SerializedProperty as there is lots of useful information there.

Ok. Here’s something really quick that does something similar to what you are aiming for:

// COMPONENT FILE

using UnityEngine;

public class TextDemo : MonoBehaviour
{
	public string[] messages;
}

// EDITOR FILE

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(TextDemo))]
[CanEditMultipleObjects]
class TextDemoEd : Editor
{
	SerializedProperty _propMessage;

	string _content;

	void OnEnable() {
		_propMessage = serializedObject.FindProperty("messages");
		if (!_propMessage.isArray) {
			// You shouldn't expect to see this.
			Debug.LogError("Property is not an array!");
		}
	}

	public override void OnInspectorGUI() {
		serializedObject.Update();
		base.DrawDefaultInspector();

		bool enlarge = false;

		EditorGUILayout.BeginVertical();
		EditorGUILayout.LabelField(string.Format("Message count = {0}", _propMessage.arraySize));
		_content = EditorGUILayout.TextArea(_content);
		if (!string.IsNullOrEmpty(_content) && GUILayout.Button("Add")) {
			enlarge = true;
		}

		EditorGUILayout.EndVertical();

		if (enlarge) {
			EnlargeArray();
			serializedObject.ApplyModifiedProperties();
			// I'm not in love with setting the array value this way.
			// Can't see an appropriate property method though.
			TextDemo t = target as TextDemo;
			t.messages[t.messages.Length-1] = _content;
			_content = "";
		}
	}

	void EnlargeArray() {
		int enlarged = _propMessage.arraySize;
		_propMessage.InsertArrayElementAtIndex(enlarged);
	}
}

// JAVASCRIPT VERSION

// COMPONENT FILE - TextDemo2.js

#pragma strict

public var messages : String[];

// EDITOR FILE - Editor/TextDemo2Ed.js

#pragma strict

@CustomEditor (TextDemo2)
@CanEditMultipleObjects
class TextDemo2Ed extends Editor {

var _propMessages : SerializedProperty;
var _content : String;

function OnEnable() {
 _propMessages = serializedObject.FindProperty("messages");
 if (!_propMessages){
    Debug.LogError("Could not find messages property");
 }else{
    Debug.Log("Found property" );
 }
}

function OnInspectorGUI() {
 serializedObject.Update();
 DrawDefaultInspector();

 var enlarge : boolean = false;

 if (_propMessages) {
     EditorGUILayout.BeginVertical();
     EditorGUILayout.LabelField(String.Format("Message count= {0}", _propMessages.arraySize));
     _content = EditorGUILayout.TextArea(_content);
     if (!String.IsNullOrEmpty(_content) && GUILayout.Button("Add")) {
      enlarge = true;
     }
     EditorGUILayout.EndVertical();
 }else{
     EditorGUILayout.LabelField("Property was not found");
 }

 if (enlarge) {
     EnlargeArray();
     serializedObject.ApplyModifiedProperties();
     var t : TextDemo2 = target as TextDemo2;
     t.messages[t.messages.Length - 1] = _content;
     _content = "";
 }
}

function EnlargeArray() {
 var enlarged : int  _propMessages.arraySize;
 _propMessages.InsertArrayElementAtIndex(enlarged);
}

}

Your main problem is that you define two classes in one file. Unity need it’s classes in seperate files and the file name has to match the class name. Since you can omit the class construct for classes that are derived from MonoBehaviour, you have to write it out for classes derived from Editor or EditorWindow.

Your two variables “dialog” and “player” are declared outside of both classes. I guess the filename match one of your classes, so the variable will belong to this class.

You can’t just cross access member variables between classes. If you want to access a variable from another class you need a reference to an instance of the class.

It would help to know your script file names and to see how “DialogController” is defined.

To answer your question in the comments, serializedObject is a variable in the Editor class. An Editor is an inspector “plugin” which shows the GUI for a certain class when it’s selected. In the first place an EditorWindow has nothing to do with the inspector or the current selection. However you can use the Selection class in your EditorWindow to get the current selected onject.

I suggest you read the docs on Editor and EditorWindow again. Pay attention to the first paragraph which explains what this classes are good for. There are also examples on the pages.

Also like phodges said, take a look at SerializedObject and SerializedProperty.

edit
Since you want to use your EditorWindow like an inspector you have to obtain a serialized object manually. Something like that:

class Dialog extends EditorWindow
{
    var serObj : SerializedObject;
    @MenuItem("Tools/Dialog Editor")
    static function Init()
    {
        var window = GetWindow(Dialog);
        window.Show();
    }
    function OnSelectionChange()
    {
        var objs = Selection.GetFiltered(DialogController, SelectionMode.Editable);
        if (objs != null && objs.Length > 0)
        {
            serObj = new SerializedObject(objs)
            // setup any SerializedProperties you need here
        }
        else
            serObj = null;
    }


    function OnGUI()
    {
        if (serObj == null)
            return;  //No object selected that contains a DialogController
        serObj.Update();
        // Draw your GUI here
    }
}

I’m not sure why you want this in an Editorwindow. The Inspector is made for editing selected components, so why don’t use extend the inspector for your DialogController?

When you implement the OnInspectorGUI like this you still have the same inspector but with additional GUI elements at the end (or start)

@CustomEditor(DialogController)
class MyPlayerEditor extends Editor
{
    var player  : SerializedProperty;
    function OnEnable ()
    {
        // Setup SerializedProperties
        player = serializedObject.FindProperty ("playerDialog");
    }
    function OnInspectorGUI()
    {
        serializedObject.Update();
        
        // Draw additional GUI before the normal inspector
        
        DrawDefaultInspector();
        
        // Draw additional GUI after the normal inspector
    }
}