I’m struggling to perform a simple task: save List data to asset in the custom editor code. I want to do it only via the script and not via the Unity menu.
Can someone experienced assist ? Many thanks.
The following code:
Defines a list item called ScriptableObjectData
Defines a data container List<> ScriptableObjectDataManager
Creates a simple custom editor window with a button to add items to the list.
What works:
The asset file gets created in the desired folder
When I click on the asset file in the Project window I can see the empty list in the Inspector
What is not working:
After I click the Add button I enter the AddItem() method, the item is added to the list in the memory (debugger) but not to the file.
Additionally I get warning: Assets/Editor/ScriptableObjectData.cs(13,17): warning CS0436: The type `ScriptableObjectData’ conflicts with the imported type of same name’. Ignoring the imported type definition.
What is this ??
The data definition file:
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class ScriptableObjectData {
public int ExecutionOrder;
}
[CreateAssetMenu (fileName = "Assets/Editor/MyData1.asset")]
public class ScriptableObjectDataManager : ScriptableObject {
public List<ScriptableObjectData> scriptableObjectDataList;
private void OnEnable() {
scriptableObjectDataList = new List<ScriptableObjectData>();
}
}
The EditorWindow code:
using UnityEngine;
using UnityEditor;
public class ScriptableObjectUser : EditorWindow {
public ScriptableObjectDataManager dataManager;
bool clickedAdd;
int counter;
[MenuItem("Window/My Window")]
public static void Start() {
ScriptableObjectUser window = GetWindow<ScriptableObjectUser>();
window.Show();
}
private void OnEnable() {
counter = 0;
Init();
}
public void OnGUI() {
clickedAdd = GUILayout.Button("Add item to list");
if (clickedAdd) {
AddItem();
}
}
public void Init() {
dataManager = ScriptableObject.CreateInstance<ScriptableObjectDataManager>();
string[] result = AssetDatabase.FindAssets("MyData1");
Debug.Log(result);
if (result.Length != 0) {
Debug.Log("Found Asset File !!!");
foreach (ScriptableObjectData dataItem in dataManager.scriptableObjectDataList) {
Debug.Log(dataItem.ToString());
}
} else {
Debug.Log("NOT Found MyData.asset :(");
AssetDatabase.CreateAsset(dataManager, "Assets/Editor/MyData1.asset");
}
}
void AddItem() {
Debug.Log("Adding Item !!!");
ScriptableObjectData data = new ScriptableObjectData();
data.ExecutionOrder = ++counter;
dataManager.scriptableObjectDataList.Add(data);
Debug.Log(dataManager.scriptableObjectDataList.ToString());
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
For starters you never utilize the result array thus each time you open the editor it uses a new instance of the dataManager. Change the code to something like this->
public void Init()
{
//dataManager = ScriptableObject.CreateInstance<ScriptableObjectDataManager>(); //remove this
string[] result = AssetDatabase.FindAssets("MyData1");
Debug.Log(result);
if (result.Length != 0)
{
string path = AssetDatabase.GUIDToAssetPath(result[0]);
dataManager =(ScriptableObjectDataManager) AssetDatabase.LoadAssetAtPath(path, typeof(ScriptableObjectDataManager)); //this will only load the first Asset that found with that name
Debug.Log("Found Asset File !!!");
foreach (ScriptableObjectData dataItem in dataManager.scriptableObjectDataList)
{
Debug.Log(dataItem.ToString());
}
}
else {
Debug.Log("NOT Found MyData.asset :(");
dataManager = ScriptableObject.CreateInstance<ScriptableObjectDataManager>();
AssetDatabase.CreateAsset(dataManager, "Assets/Editor/MyData1.asset");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
So you have an active instance of the dataManager. Now to force the editor to save the changes on the AddItem() method add EditorUtility.SetDirty(dataManager); above the AssetDataBase.SaveAssets();
This should do the trick.
Lastly on the ScriptableObjectManager class
public class ScriptableObjectDataManager : ScriptableObject {
public List<ScriptableObjectData> scriptableObjectDataList = new List<ScriptableObjectData>();
}
change it to this otherwise the List will be cleared each time you open the editor unless that is something you want to happen.
Oh and about the warning you obviously have 2 definitions of the same class. Change the name of either one to get it fixed, apparently the editor checks if there are definition conflicts and ignores the one from the import… needless to say it will create a ton of problems when you use a script that needs the second definition but gets the one you created. To resolve it properly change the name of the class/file you created or put it inside a namespace. (i would go with the second one)
Now I’m trying to make an asset containing list of GameObjects. Below is the code changes comparing to the initial one.
What is working:
The asset gets saved with new list item created.
The Execution Order fields gets populated correctly.
What is not working:
When I press the Add button the item is added to the list but in the “Game Object” field I get message: “Type mismatch”
I cannot drag & drop objects from the Hierarchy onto “Game Object” field in the asset opened in the Inspector.
What am I doing wrong ??
Data code:
[System.Serializable]
public class ScriptableObjectData {
public GameObject gameObject;
public int ExecutionOrder;
}
[CreateAssetMenu(fileName = "Assets/Editor/MyData1.asset")]
public class ScriptableObjectDataManager : ScriptableObject {
public List<ScriptableObjectData> scriptableObjectDataList = new List<ScriptableObjectData>();
}
The AddItem() method:
void AddItem() {
Debug.Log("Adding Item !!!");
ScriptableObjectData data = new ScriptableObjectData();
data.ExecutionOrder = ++counter;
data.gameObject = new GameObject("Test");
dataManager.scriptableObjectDataList.Add(data);
Debug.Log(dataManager.scriptableObjectDataList.ToString());
//EditorUtility.SetDirty(dataManager);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}