ScriptableObject
Возможно, более подходящим для Unity было бы использование выделенного ScriptableObject
(также см. Введение в ScriptableObjects )
Вы можете объединить его с [InitializeOnLoadMethod]
, чтобы реализовать метод загрузки, который будет вызываться после открытия редактора и после повторной компиляции для создания ScriptableObject один раз .
// we don't need the CreateAssetMenu attribute since the editor window
// will create the ScriptableObject once by itself
public class CustomMenuData : ScriptableObject
{
public int Id;
}
Обязательно поместите его в отдельный скрипт.
public class CustomMenu : EditorWindow
{
// we can go privtae again ;)
private static CustomMenuData data;
// This method will be called on load or recompile
[InitializeOnLoadMethod]
private static void OnLoad()
{
// if no data exists yet create and reference a new instance
if (!data)
{
// as first option check if maybe there is an instance already
// and only the reference got lost
// won't work ofcourse if you moved it elsewhere ...
data = AssetDatabase.LoadAssetAtPath<CustomMenuData>("Assets/CustomMenuData.asset");
// if that was successful we are done
if(data) return;
// otherwise create and reference a new instance
data = CreateInstance<CustomMenuData>();
AssetDatabase.CreateAsset(data, "Assets/CustomMenuData.asset");
AssetDatabase.Refresh();
}
}
[MenuItem("Custom/CustomMenu")]
private static void Init()
{
// Get existing open window or if none, make a new one:
var window = (CustomMenu)EditorWindow.GetWindow(typeof(CustomMenu));
window.Show();
}
private void OnGUI()
{
// Note that going through the SerializedObject
// and SerilaizedProperties is the better way of doing this!
//
// Not only will Unity automatically handle the set dirty and saving
// but it also automatically adds Undo/Redo functionality!
var serializedObject = new SerializedObject(data);
// fetches the values of the real instance into the serialized one
serializedObject.Update();
// get the Id field
var id = serializedObject.FindProperty("Id");
// Use PropertyField as much as possible
// this automaticaly uses the correct layout depending on the type
// and automatically reads and stores the according value type
EditorGUILayout.PropertyField(id);
if (GUILayout.Button("someButton"))
{
// Only change the value throgh the serializedProperty
// unity marks it as dirty automatically
// also no Repaint required - it will be done .. guess .. automatically ;)
id.intValue += 1;
}
// finally write back all modified values into the real instance
serializedObject.ApplyModifiedProperties();
}
}
Огромное преимущество этого:
- Это намного быстрее / эффективнее, чем использование FileIO для записи и сохранения, поскольку Unity автоматически выполняет (де) сериализацию этого объекта ScriptableObject.
- Вам не нужно «вручную» загружать и сохранять данные ... это делается автоматически, поскольку ScriptableObject ведет себя как любой другой префаб.
- Вы можете просто щелкнуть экземпляр ScriptableObject в ваших активах и напрямую изменить значения!
Использование простого текстового файла
Простым, но не очень эффективным альтернативным решением было бы сохранить его в файл, например. как JSON, как это
using System.IO;
using UnityEditor;
using UnityEngine;
public class CustomMenu : EditorWindow
{
private const string FileName = "Example.txt";
// shorthand property for getting the filepath
public static string FilePath
{
get { return Path.Combine(Application.streamingAssetsPath, FileName); }
}
private static int id = 10000;
// Serialized backing field for storing the value
[SerializeField] private int _id;
[MenuItem("Custom/CustomMenu")]
static void Init()
{
// Get existing open window or if none, make a new one:
CustomMenu window = (CustomMenu)EditorWindow.GetWindow(typeof(CustomMenu));
if (File.Exists(FilePath))
{
// read the file content
var json = File.ReadAllText(FilePath)
// If the file exists deserialize the JSON and read in the values
// for only one value ofcourse this is overkill but for multiple values
// this is easier then parsing it "manually"
JsonUtility.FromJsonOverwrite(json, window);
// pass the values on into the static field(s)
id = window._id;
}
window.Show();
}
private void OnGUI()
{
id = EditorGUILayout.IntField(id);
if (GUILayout.Button("someButton"))
{
id++;
Repaint();
EditorUtility.SetDirty(this);
// do everything in oposide order
// first fetch the static value(s) into the serialized field(s)
_id = id;
// if not exists yet create the StreamingAssets folder
if (!Directory.Exists(Application.streamingAssetsPath))
{
AssetDatabase.CreateFolder("Assets", "StreamingAssets");
}
// serialize the values into json
var json = JsonUtility.ToJson(this);
// write into the file
File.WriteAllText(FilePath, json);
// reload the assets so the created file becomes visible
AssetDatabase.Refresh();
}
}
}
В настоящее время он читает файл каждый раз, когда вы открываете окно, и записывает его каждый раз, когда нажимаете кнопку. Это все еще можно улучшить.
Опять же, вы можете использовать [InitializeOnLoadMethod]
, чтобы прочитать файл только один раз - а именно, когда вы открываете редактор или перекомпилируете, как
public class CustomMenu : EditorWindow
{
// Instead of having the field values directly as static fields
// rather store the information in a proper serializable class
[Serializable]
private class CustomMenuData
{
public int Id;
}
// made this publlic for the saving process (see below)
public static readonly CustomMenuData data = new CustomMenuData();
// InitializeOnLoadMethod makes this method being called everytime
// you load the project in the editor or after re-compilation
[InitializeOnLoadMethod]
private static void OnLoad()
{
if (!File.Exists(FilePath)) return;
// read in the data from the json file
JsonUtility.FromJsonOverwrite(File.ReadAllText(FilePath), data);
}
...
}
Чтобы оптимизировать сохранение и выполнять запись файлов только при сохранении в UnityEditor, вы можете реализовать выделенный AssetModificationProcessor
, как
public class CustomMenuSaver : SaveAssetsProcessor
{
static string[] OnWillSaveAssets(string[] paths)
{
// do change nothing in the paths
// but additionally store the data in the file
// if not exists yet create the StreamingAssets folder
if (!Directory.Exists(Application.streamingAssetsPath))
{
AssetDatabase.CreateFolder("Assets", "StreamingAssets");
}
// serialize the values into json v That's why I made it public
var json = JsonUtility.ToJson(CustomMenu.data);
// write into the file v needs to be public as well
File.WriteAllText(CustomMenu.FilePath, json);
return paths;
}
}