Programming Question

Off topic discussion. Talk about gaming and life in general. Be awesome to each other.
User avatar
LorrMaster42
Posts: 65
Joined: Thu Dec 24, 2015 12:09 am

Programming Question

Post by LorrMaster42 »

Currently working on a UI for my dungeon generator and I've run into a problem when creating a custom window. I want to edit an ArrayList inside of a specific class when I press a button on this window. However, I'm not sure how to tell the window's code which specific class I want to edit (turning the class static isn't an option). Thank you.

User avatar
Interkarma
Posts: 7236
Joined: Sun Mar 22, 2015 1:51 am

Re: Programming Question

Post by Interkarma »

Your custom window should already have a reference to the ArrayList, something like below:

Code: Select all

class MyCustomWindow
{
    ArrayList myArrayList = new ArrayList()
    
    void UpdateList()
    {
        // Change members in myArrayList
    }
    
    void DoClick()
    {
        // Do something with myArrayList
    }
}
You can then just send the class reference directly to whoever needs to process it, for example:

Code: Select all

// In MyCustomWindow class
ListProcessingLogic listLogic = new ListProcessingLogic();

void DoClick()
{
    listLogic.ProcessList(myArrayList)
}

// In ListProcessingLogic class
void ProcessList(ArrayList arrayList)
{
    // Do something with passed reference
}
If you need to change the list reference in MyCustomWindow class, you can either make the field public or use a property (preferred).

Code: Select all

public ArrayList MyArrayList
{
    get { return myArrayList; }
    set { myArrayList = value; }
}
The trick is to cache your reference if you need to use it frequently, and pass that reference to other methods when asking them to operate on it. If the methods operating on list are in same class, you can even use the local field directly. And don't forget some checks (e.g. is the list null or empty, do I need to throw an exception?) before trying to operate on passed reference.

I hope that helps you move towards a solution. If you're still stuck, please post the smallest possible snippet of code that clearly demonstrates your problem (don't post the whole thing), and I'm sure someone will be able to help. :)

User avatar
LorrMaster42
Posts: 65
Joined: Thu Dec 24, 2015 12:09 am

Re: Programming Question

Post by LorrMaster42 »

Here is a piece of the code (first group is from the class I want to edit, second is from the window and is activated by the OnGUI() method).

Code: Select all

        // This code is in the class "DungeonSetEditor : Editor"
        // dunSetList is an Arraylist
        
        if (GUILayout.Button(dunSet.dunSetList[k].designSetName, GUILayout.Width(150)))
        {
            DesignSetEditorWindow.openWindow(dunSet.dunSetList[k]);
        }

Code: Select all

        // This code is in the class "DesignSetEditorWindow : EditorWindow"
        // I want to access "dunSet" so that I can replace a class within the ArrayList it contains with "currentSet"
        
        if (GUILayout.Button("Save", GUILayout.Width(150))){
            DungeonSetEditor.dunSet.dunSetList[k] = currentSet;
            originalSet = currentSet;
        }

User avatar
Interkarma
Posts: 7236
Joined: Sun Mar 22, 2015 1:51 am

Re: Programming Question

Post by Interkarma »

I'm not certain on the design here. An Editor script is used to create a custom Inspector for a target GameObject, and an EditorWindow is for a standalone editor window of some kind - usually opened from the menu in Unity3D.

One of the reasons you're having troubles passing information between Editor and EditorWindow is they both have different purposes and aren't really intended to hand off data directly to each other.

One way you might redesign this is to reference a ScriptableObject containing your data. For example:

MyObject - Is a ScriptableObject holding your data. Use normal Unity serialization to save data with scene.

MyEditorWindow - Exposes a ScriptableObject field for you to drag-drop in the MyObject you want to edit.

You could also forgo the EditorWindow entirely and just build the UI you need into an Editor script targeting your custom object.

I apologise if that's not immediately helpful. :)

User avatar
LorrMaster42
Posts: 65
Joined: Thu Dec 24, 2015 12:09 am

Re: Programming Question

Post by LorrMaster42 »

I'm trying to create a UI that artists can use to import and organize through assets to be used in the dungeon, such as picking between different types of walls and such. Once a list of different combinations is created, I want Unity to save that data so that in can place the assets down upon generating each dungeon.

This is the first Unity project I'm working on, so I still don't have a solid understanding of the purpose of each tool.

User avatar
Interkarma
Posts: 7236
Joined: Sun Mar 22, 2015 1:51 am

Re: Programming Question

Post by Interkarma »

You have three main designs you can choose between:

1. ScriptableObject with EditorWindow

In this design, the ScriptableObject acts as your storage mechanism (and exposes your ArrayList and whatever else). The EditorWindow can interface with the ScriptableObject via a dropped reference. Your EditorWindow is a standalone window you can open/close and drag around independently. This is a good design for creating shared data to be used by many other objects.


2. ScriptableObject with Editor

In this design, the Editor script will let you create a custom UI for an individual object in its Inspector. The Editor script interfaces with object via "target" member. Your custom Editor UI is shown in the Inspector in place of the normal Inspector UI. This is a good design for doing custom stuff on individual objects.


3. Custom serialization with EditorWindow

This is just like the first option, but you're replacing the ScriptableObject with your own serialization mechanism. For example, saving ArrayList to a JSON file, XML, or something similar. Whatever objects consume that list just need to deserialize list data.

User avatar
LorrMaster42
Posts: 65
Joined: Thu Dec 24, 2015 12:09 am

Re: Programming Question

Post by LorrMaster42 »

Trying to edit a ScriptableObject inside an Editor Window. So far so good, but I've been having trouble figuring out how to save the scriptableObject, as nothing I do seems to persist after I close Unity and reload the project.

Code: Select all

    void OnEnable()
    {
        // Load up the list of dungeons
        listContainer = Resources.Load("DungeonTypes") as DungeonTypeList;

        EditorUtility.SetDirty(listContainer);

        if (listContainer == null)
        {
            Debug.Log("Warning: 'Dungeon Types List' not found.");
        }
        removeNullDungeons();
        if (listContainer.dunTypeList.Count == 0) // If there are no dungeon types, create an empty one
        {
            createNewDun();
        }
        currentDun = listContainer.dunTypeList[0];
        currentDunPlace = 0;

        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();

        Debug.Log("Dungeon List Enabled");
    }
    

User avatar
Interkarma
Posts: 7236
Joined: Sun Mar 22, 2015 1:51 am

Re: Programming Question

Post by Interkarma »

To have Unity serialize an object for you, the member will need to be public or have the SerializeField attribute attached. One of the following, for example:

Code: Select all

public int points = 0;

[SerializeField]
int points = 0;
If you need to serialize another class or struct, something that might not normally be serialized, then add the Serializable attribute.

Code: Select all

[Serializable]
struct NameAndAge
{
    public string name;
    public int age;
}
And just to clarify, I was using ScriptableObject generically (e.g. MonoBehaviour inherits from ScriptableObject). ScriptableObjects are usually used as an asset, whereas MonoBehaviour will exist inside the scene. If your object should exist inside the scene (like a factory with serialized settings and data), easiest path is to inherit your class from MonoBehaviour and make any members public that you want saved.

For more advanced use case (e.g. object does not exist in scene), you can roll your own serialization config with JSON, XML, etc. It just depends on what best suits your needs as the developer.

User avatar
LorrMaster42
Posts: 65
Joined: Thu Dec 24, 2015 12:09 am

Re: Programming Question

Post by LorrMaster42 »

Just to clarify, I am trying to save an asset so that any changes made to it in the Editor Window are saved permanently. What is happening is that I am making changes to the ScriptableObject, but despite the changes still being there after the Editor Window is closed, those changes are discarded when I close the project.

User avatar
Interkarma
Posts: 7236
Joined: Sun Mar 22, 2015 1:51 am

Re: Programming Question

Post by Interkarma »

If you're just using a straight editor window, those changes either need to be saved to an object in the scene, or using your own solution. As you've discovered, Unity won't persist them once the editor window is closed. Only objects in the scene will automatically save state (when conditions are setup for it).

For your own solution, Unity has JSON serialization built in now. You can save out data and restore as needed. I haven't spent much time with this, I use FullSerializer in Daggerfall Unity.

Post Reply