From 510225429d1fc5d9cc9042021f2779be2261e3d9 Mon Sep 17 00:00:00 2001
From: Cyan <69320946+Cyanilux@users.noreply.github.com>
Date: Sun, 9 Aug 2020 17:56:57 +0100
Subject: [PATCH 1/8] ScriptableObject + cross-scene reference support
Added ScriptableObject version of the ToDo list. Works identical to the MonoBehaviour version, just isn't attached to a Scene.
Added ToDo lists to Create menus and "Cyan" heading to AddComponent menu
Added SceneReferencesHandler which is automatically added to a scene when required.
It's now possible to link objects to a task which are cross-scene, or scene-based on a prefab or ScriptableObject ToDo list. This has only been tested in 2020.1 currently.
How it works :
When dragging an object onto a task from a different scene, or from a scene when the ToDo list is a prefab/ScriptableObject, a SceneReferencesHandler object is created and added to the scene - hidden from the Hierarchy but still serialised in scene. The dragged object is registered with the handler, which assigns it a unique ID, adds it to a SerializableDictionary and saves the scene. The task can then reference and serialise the SceneAsset (in element.objectReference) and the ID (in element.objectReferenceID). When the task is visible in-editor, it will attempt to obtain the SceneReferenceHandler from the SceneAsset (if loaded, sidenote : won't work at runtime sadly, would need to save the scene name/path/buildIndex instead). With this it can obtain the object from the ID and show it in the object field. Fields which are serialised in this way are marked with a asterisk.
---
ToDo/Editor/ToDoEditor.cs | 379 ++++++++++++++++++++++++----
ToDo/SceneReferencesHandler.cs | 91 +++++++
ToDo/SceneReferencesHandler.cs.meta | 11 +
ToDo/SerializableDictionary.cs | 34 +++
ToDo/SerializableDictionary.cs.meta | 11 +
ToDo/ToDo.cs | 30 ++-
ToDo/ToDoSO.cs | 17 ++
ToDo/ToDoSO.cs.meta | 11 +
8 files changed, 521 insertions(+), 63 deletions(-)
create mode 100644 ToDo/SceneReferencesHandler.cs
create mode 100644 ToDo/SceneReferencesHandler.cs.meta
create mode 100644 ToDo/SerializableDictionary.cs
create mode 100644 ToDo/SerializableDictionary.cs.meta
create mode 100644 ToDo/ToDoSO.cs
create mode 100644 ToDo/ToDoSO.cs.meta
diff --git a/ToDo/Editor/ToDoEditor.cs b/ToDo/Editor/ToDoEditor.cs
index 44c54ff..603559f 100644
--- a/ToDo/Editor/ToDoEditor.cs
+++ b/ToDo/Editor/ToDoEditor.cs
@@ -1,18 +1,95 @@
-using UnityEngine;
+using System.Collections.Generic;
+using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using System.IO;
using System.Text;
+using UnityEngine.SceneManagement;
+using UnityEditor.SceneManagement;
+using System.Linq;
namespace Cyan.ToDo {
+ [CustomEditor(typeof(ToDoSO))]
+ public class ToDoSOEditor : ToDoEditor {
+
+ protected override void Awake() {
+ base.Awake();
+ todoList = (target as ToDoSO).list;
+ }
+
+ }
+
[CustomEditor(typeof(ToDo))]
+ public class ToDoMBEditor : ToDoEditor {
+
+ protected override void Awake() {
+ base.Awake();
+
+ ToDo todo = target as ToDo;
+ todoList = todo.list;
+ scenePath = todo.gameObject.scene.path;
+
+ PrefabUtility.prefabInstanceUpdated += PrefabUpdated;
+ }
+
+ private void OnDisable() {
+ PrefabUtility.prefabInstanceUpdated -= PrefabUpdated;
+ }
+
+ private void PrefabUpdated(GameObject prefab) {
+ // Prefab created / updated
+
+ // This would usually break all scene GameObject/Component references
+ // But we can convert + serialise an ID, in order to retain references (see SceneReferencesHandler)
+
+ // Note : This isn't called unless the object is SELECTED when the prefab is created since it's in a custom inspector!!
+
+ if (scenePath == null || scenePath == "") return; // Ignore updates to the Prefab asset itself, we only care about scene overrides
+
+ string path = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(prefab);
+ if (path == null || path == "") return;
+
+ ToDo todo = target as ToDo;
+ for (int i = 0; i < todo.list.tasks.Count; i++) {
+ ToDoElement element = todo.list.tasks[i];
+ if (element.objectReference != null) {
+ // Has reference
+ if (element.objectReferenceID == null || element.objectReferenceID == "") {
+ // Has no objectReferenceID, so is a scene-reference or Asset (not cross-scene already)
+ if (!IsReferenceAllowed(element.objectReference, null, out GameObject gameObject)) {
+ // Is in-scene object reference!
+ if (gameObject == prefab) {
+ // It's okay, it's the prefab itself, so we can ignore it!
+ } else {
+ // Is in-scene object reference, need to swap to cross-scene reference
+ Debug.Log("Swapping to Cross-scene Reference!");
+ LinkCrossSceneReference(element, element.objectReference, gameObject);
+ }
+ }
+ }
+ }
+ }
+
+ // Apply Prefab Changes
+ PrefabUtility.ApplyObjectOverride(todo, path, InteractionMode.AutomatedAction);
+ }
+ }
+
+ ///
+ /// Editor / Custom Inspector for the To Do lists.
+ /// Awake method should be overriden, and todoList value should be set.
+ ///
public class ToDoEditor : Editor {
- private ToDo todoList;
+ protected ToDoList todoList;
+ protected string scenePath;
+ // If MonoBehaviour, this is the scene the object is a part of. Used to prevent cross-scene GameObject/Component references.
+ // If ScriptableObject, is null and used to warn about GameObject/Component scene references
+
private ReorderableList reorderableList;
- private static Color textColor;// = new Color(0.8f, 0.8f, 0.8f);
+ private static Color textColor;
private static Color focusColor = new Color(0, 170 / 255f, 187 / 255f, 0.5f);
private static Color activeColor = new Color(0, 170 / 255f, 187 / 255f, 0.3f);
@@ -22,28 +99,50 @@ public class ToDoEditor : Editor {
private GUIStyle style_greyLabel;
private GUIStyle style_textArea;
+ private GUIStyle style_label;
private bool actions;
- private void Awake() {
- todoList = target as ToDo;
+ [MenuItem("GameObject/Create Other/Cyan.ToDo (MonoBehaviour)")]
+ static void Create() {
+ GameObject obj = new GameObject("To Do");
+ obj.AddComponent();
+ Selection.activeGameObject = obj;
}
- public override void OnInspectorGUI() {
- //base.OnInspectorGUI();
+ [MenuItem("GameObject/Cyan.ToDo/Remove Scene References Handler")]
+ static void DeleteSceneReferences() {
+ if (EditorUtility.DisplayDialog("Remove Scene Reference Handler",
+ "Hey! This will remove the hidden GameObject containing the SceneReferences component in the current active scene. It handles : \n\n"+
+ "\u2022 Cross-scene object references for ToDo (MonoBehaviour)\n" +
+ "\u2022 Scene object references for ToDo (ScriptableObject)\n\n" +
+ "This will break any references that tasks have to objects in this scene!", "Delete it!", "Abort!")) {
+ Scene scene = SceneManager.GetActiveScene();
+ SceneReferencesHandler sceneReferencesHandler = SceneReferencesHandler.GetSceneReferencesHandler(scene);
+ if (sceneReferencesHandler != null) {
+ Debug.Log($"Cyan.ToDo : Scene References Handler has been removed from scene \"{scene.name}\"");
+ DestroyImmediate(sceneReferencesHandler.gameObject);
+ }
+ }
+ }
- if (style_textArea == null) {
- style_textArea = new GUIStyle(GUI.skin.label);
- style_textArea.alignment = TextAnchor.UpperLeft;
- style_textArea.wordWrap = true;
- style_textArea.richText = true;
+ [MenuItem("GameObject/Cyan.ToDo/Remove Scene References Handler", true)]
+ static bool ValidateDeleteSceneReferences() {
+ Scene scene = SceneManager.GetActiveScene();
+ return (SceneReferencesHandler.GetSceneReferencesHandler(scene) != null);
+ }
- textColor = style_textArea.normal.textColor;
- }
+ protected virtual void Awake() { }
+
+ public override void OnInspectorGUI() {
+ //base.OnInspectorGUI();
+ CreateTextStyles();
+ ResetTextColor();
+
if (reorderableList == null) {
// Create Reorderable List
- reorderableList = new ReorderableList(todoList.list, typeof(ToDoElement));
+ reorderableList = new ReorderableList(todoList.tasks, typeof(ToDoElement));
reorderableList.drawHeaderCallback = DrawHeader;
reorderableList.elementHeightCallback = ElementHeight;
reorderableList.drawElementCallback = DrawElement;
@@ -66,7 +165,7 @@ public override void OnInspectorGUI() {
if (EditorUtility.DisplayDialog("Remove Completed Tasks",
$"Are you sure you want to remove all completed tasks for the To Do List \"{todoList.listName}\"?", "Yes!", "Nooo!")) {
// Remove Completed Tasks
- Undo.RecordObject(todoList, "Remove Completed Tasks");
+ Undo.RecordObject(target, "Remove Completed Tasks");
todoList.RemoveCompleted();
}
}
@@ -81,7 +180,7 @@ public override void OnInspectorGUI() {
if (GUILayout.Button(new GUIContent("Import Tasks", "Import tasks from Text File (won't affect existing tasks)"))) {
// Import Tasks
- Undo.RecordObject(todoList, "Import Tasks");
+ Undo.RecordObject(target, "Import Tasks");
string path = EditorUtility.OpenFilePanel("Import", "Assets", "txt");
if (path.Length != 0) {
Import(path);
@@ -95,13 +194,13 @@ public override void OnInspectorGUI() {
}
private void OnAdd(ReorderableList list) {
- Undo.RecordObject(todoList, "Task Added");
- todoList.list.Add(new ToDoElement());
+ Undo.RecordObject(target, "Task Added");
+ todoList.tasks.Add(new ToDoElement());
}
private void OnRemove(ReorderableList list) {
- Undo.RecordObject(todoList, "Task Removed");
- todoList.list.RemoveAt(list.index);
+ Undo.RecordObject(target, "Task Removed");
+ todoList.tasks.RemoveAt(list.index);
}
private void DrawHeader(Rect rect) {
@@ -111,11 +210,14 @@ private void DrawHeader(Rect rect) {
style_greyLabel.normal.textColor = Color.grey;
}
EditorGUI.LabelField(rect, "@Cyanilux", style_greyLabel);
-
+
+ CreateTextStyles();
+ ResetTextColor();
+
EditorGUI.BeginChangeCheck();
string listName = EditorGUI.TextField(rect, todoList.listName, style_textArea);
if (EditorGUI.EndChangeCheck()) {
- Undo.RecordObject(todoList, "List Name Change");
+ Undo.RecordObject(target, "List Name Change");
todoList.listName = listName;
}
}
@@ -124,15 +226,38 @@ private float GetWidth() {
return Screen.width - 87;
}
- private float ElementHeight(int index) {
- ToDoElement element = todoList.list[index];
-
- float width = GetWidth();
+ private void CreateTextStyles() {
if (style_textArea == null) {
style_textArea = new GUIStyle(GUI.skin.label);
style_textArea.alignment = TextAnchor.UpperLeft;
style_textArea.wordWrap = true;
+ style_textArea.richText = true;
+ textColor = style_textArea.normal.textColor;
}
+
+ if (style_label == null) {
+ style_label = new GUIStyle(GUI.skin.label);
+ style_label.alignment = TextAnchor.UpperLeft;
+ style_label.wordWrap = false;
+ style_label.richText = false;
+ }
+ }
+
+ private void ResetTextColor() {
+ style_textArea.normal.textColor = textColor;
+ style_textArea.focused.textColor = textColor;
+ style_textArea.hover.textColor = textColor;
+
+ style_label.normal.textColor = textColor;
+ style_label.focused.textColor = textColor;
+ style_label.hover.textColor = textColor;
+ }
+
+ private float ElementHeight(int index) {
+ ToDoElement element = todoList.tasks[index];
+
+ float width = GetWidth();
+ CreateTextStyles();
if (element.editing) {
style_textArea.richText = false;
}
@@ -150,7 +275,7 @@ private float ElementHeight(int index) {
private void DrawElementBackground(Rect rect, int index, bool active, bool focus) {
if (index < 0) return;
- ToDoElement element = todoList.list[index];
+ ToDoElement element = todoList.tasks[index];
Rect elementRect = new Rect(rect.x, rect.y + 1, rect.width, rect.height - 1);
@@ -171,7 +296,7 @@ private void DrawElementBackground(Rect rect, int index, bool active, bool focus
}
private void DrawElement(Rect rect, int index, bool active, bool focus) {
- ToDoElement element = todoList.list[index];
+ ToDoElement element = todoList.tasks[index];
#if !UNITY_2020_1_OR_NEWER
// rect.height is wrong for older versions (tested: 2018.3, 2019.3), so needs recalculating
@@ -195,7 +320,7 @@ private void DrawElement(Rect rect, int index, bool active, bool focus) {
new Rect(rect.x + 2, rect.y + 2, h, h),
element.completed);
if (EditorGUI.EndChangeCheck()) {
- Undo.RecordObject(todoList, "Toggled Task Completion");
+ Undo.RecordObject(target, "Toggled Task Completion");
element.completed = completed;
}
@@ -212,9 +337,7 @@ private void DrawElement(Rect rect, int index, bool active, bool focus) {
style_textArea.focused.textColor = completedTextColor;
style_textArea.hover.textColor = completedTextColor;
} else {
- style_textArea.normal.textColor = textColor;
- style_textArea.focused.textColor = textColor;
- style_textArea.hover.textColor = textColor;
+ ResetTextColor();
}
// If editing, turn off richText
@@ -238,7 +361,7 @@ private void DrawElement(Rect rect, int index, bool active, bool focus) {
style_textArea.richText = true;
if (EditorGUI.EndChangeCheck()) {
- Undo.RecordObject(todoList, "Edited Task Text");
+ Undo.RecordObject(target, "Edited Task Text");
element.text = text;
}
@@ -247,21 +370,62 @@ private void DrawElement(Rect rect, int index, bool active, bool focus) {
GUI.skin.settings.cursorColor = cursorColor;
}
- // Object Field
- if (element.objectReference) {
- EditorGUI.BeginChangeCheck();
- EditorGUI.LabelField(
- new Rect(rect.x + x, rect.y + rect.height + 5 - 25, rect.width - 27, h),
- "Linked Object : ",
- style_textArea);
- x += EditorGUIUtility.labelWidth;
- Object obj = EditorGUI.ObjectField(
- new Rect(rect.x + x, rect.y + rect.height + 5 - 25, rect.width - x, h),
- element.objectReference,
- typeof(Object), true);
- if (EditorGUI.EndChangeCheck()) {
- Undo.RecordObject(todoList, "Changed Task Object");
- element.objectReference = obj;
+ // Object Reference
+ if (element.objectReference) { // either in-scene reference, or is SceneAsset
+ Object objectReference = null;
+ bool isNormalReference = false;
+ bool valid = true;
+ string scenePath = null;
+ if (element.objectReferenceID != null && element.objectReference.GetType() == typeof(SceneAsset)) {
+ // Cross-scene object reference (or scene object refence for ScriptableObject)
+ if (element.tempObjectReference != null) {
+ objectReference = element.tempObjectReference;
+ } else {
+ SceneAsset sceneAsset = element.objectReference as SceneAsset;
+ scenePath = AssetDatabase.GetAssetPath(sceneAsset);
+ Scene scene = SceneManager.GetSceneByPath(scenePath);
+ valid = scene.IsValid();
+ if (valid) {
+ SceneReferencesHandler sceneReferencesHandler = SceneReferencesHandler.GetSceneReferencesHandler(scene);
+ if (sceneReferencesHandler != null) {
+ objectReference = sceneReferencesHandler.GetObjectFromID(element.objectReferenceID);
+ element.tempObjectReference = objectReference;
+ }
+ }
+ }
+ } else {
+ // Normal in-same-scene object reference
+ objectReference = element.objectReference;
+ isNormalReference = true;
+ }
+
+ // Object Field
+ if (valid) {
+ if (objectReference != null) {
+ EditorGUI.BeginChangeCheck();
+ EditorGUI.LabelField(
+ new Rect(rect.x + x, rect.y + rect.height + 5 - 25, rect.width - 27, h),
+ "Linked Object : " + (isNormalReference ? "" : "*"),
+ style_label);
+ x += 100;
+ Object obj = EditorGUI.ObjectField(
+ new Rect(rect.x + x, rect.y + rect.height + 5 - 25, rect.width - x, h),
+ objectReference,
+ typeof(Object), true);
+ if (EditorGUI.EndChangeCheck()) {
+ SetTaskObject(element, obj);
+ }
+ } else {
+ EditorGUI.LabelField(
+ new Rect(rect.x + x, rect.y + rect.height + 5 - 25, rect.width - 27, h),
+ "Linked Object : * (Cross-scene reference lost?)",
+ style_label);
+ }
+ } else {
+ EditorGUI.LabelField(
+ new Rect(rect.x + x, rect.y + rect.height + 5 - 25, rect.width - 27, h),
+ $"Linked Object : * (Object in scene '{scenePath}')",
+ style_label);
}
}
@@ -275,19 +439,121 @@ private void DrawElement(Rect rect, int index, bool active, bool focus) {
DragAndDrop.AcceptDrag();
if (DragAndDrop.objectReferences.Length > 0) {
Object obj = DragAndDrop.objectReferences[0];
- Undo.RecordObject(todoList, "Changed Task Object");
- element.objectReference = obj;
+ SetTaskObject(element, obj);
}
currentEvent.Use();
}
}
+ }
+
+ protected void LinkCrossSceneReference(ToDoElement element, Object obj, GameObject gameObject) {
+ Scene scene = gameObject.scene;
+ SceneAsset sceneAsset = GetSceneAsset(scene);
+ SceneReferencesHandler sceneReferencesHandler = SceneReferencesHandler.GetSceneReferencesHandler(scene, true);
+
+ Undo.RecordObjects(new Object[] { target, sceneReferencesHandler }, "Changed Task Object");
+
+ element.objectReference = sceneAsset;
+ element.objectReferenceID = sceneReferencesHandler.RegisterObject(obj);
+ element.tempObjectReference = null;
+
+ Debug.Log("SceneObjectReference : " + element.objectReferenceID);
+ PrefabUtility.RecordPrefabInstancePropertyModifications(target);
+ }
+
+ protected void LinkObjectReference(ToDoElement element, Object obj) {
+ Undo.RecordObject(target, "Changed Task Object");
+ element.objectReference = obj;
+ element.objectReferenceID = null;
+ PrefabUtility.RecordPrefabInstancePropertyModifications(target);
+ Debug.Log("ObjectReference");
+ }
+
+ private void SetTaskObject(ToDoElement element, Object obj) {
+ if (scenePath == null) {
+ // ToDo is Prefab / ScriptableObject Asset
+ Debug.Log("Prefab / ScriptableObject");
+ if (IsReferenceAllowed(obj, null, out GameObject gameObject)) {
+ // Asset
+ LinkObjectReference(element, obj);
+
+ // Need to mark ScriptableObject as dirty so it saves changes
+ EditorUtility.SetDirty(target);
+ } else {
+ // Scene Reference, Store SceneAsset in objectReference
+ LinkCrossSceneReference(element, obj, gameObject);
+ EditorSceneManager.SaveScene(gameObject.scene);
+ }
+ } else if (!IsReferenceAllowed(obj, scenePath, out GameObject gameObject)) {
+ Debug.Log("MonoBehaviour, Cross-Scene");
+ // ToDo is MonoBehaviour in Scene, but obj is in different scene. Cross-scene reference
+ LinkCrossSceneReference(element, obj, gameObject);
+ EditorSceneManager.SaveScene(gameObject.scene);
+ } else {
+ // ToDo is MonoBehaviour, obj is GameObject/Component in same scene or is an Asset
+ Debug.Log("MonoBehaviour, Same Scene");
+ LinkObjectReference(element, obj);
+ }
+ }
+ private SceneAsset GetSceneAsset(Scene scene) {
+ return AssetDatabase.LoadAssetAtPath(scene.path);
+ }
+
+ /* Unused, but might allow for object references that doesn't dirty the scene.
+ * Rather than registering the object with SceneReferencesHandler
+ * It could just save the object's path in the Hierarchy.
+ * However, renames/changes to that path will break the reference.
+ * If you want to implement this, you'd likely need to use it in the LinkCrossSceneReference method
+ private string GetObjectPath(GameObject gameObject, Object obj) {
+ if (gameObject == null) return null;
+ List path = new List();
+
+ // If component, append component type to path
+ Component component = obj as Component;
+ if (component != null) {
+ path.Add(":" + component.GetType());
+ }
+
+ // Get Hierarchy / Transforms from GameObject to Root
+ Transform t = gameObject.transform;
+ path.Add(t.name);
+ while (t.parent != null) {
+ t = t.parent;
+ path.Add(t.name + "/");
+ }
+
+ // Turn into string (e.g. "Root/Child/GameObject:ComponentType")
+ StringBuilder stringBuilder = new StringBuilder();
+ foreach (string s in Enumerable.Reverse(path)) {
+ stringBuilder.Append(s);
+ }
+ return stringBuilder.ToString();
+ }*/
+
+ protected bool IsReferenceAllowed(Object obj, string scenePath, out GameObject gameObject) {
+ // Get GameObject
+ gameObject = obj as GameObject;
+ if (gameObject == null) {
+ Component component = obj as Component;
+ if (component != null) {
+ gameObject = component.gameObject;
+ }
+ }
+ if (gameObject == null) return true; // Not a GameObject, would be an Asset, so always allowed.
+ Debug.Log("IsReferenceAllowed, Scene : " + gameObject.scene.path);
+ return (gameObject.scene.path == scenePath); // Same scene = allow, Cross-scene = not allowed
}
+ ///
+ /// Exports the To Do tasks list to a txt file at the specifed path.
+ /// Note, doesn't include object references
+ ///
+ ///
private void Export(string path) {
StringBuilder stringBuilder = new StringBuilder();
- for (int i = 0; i < todoList.list.Count; i++) {
- ToDoElement element = todoList.list[i];
+ for (int i = 0; i < todoList.tasks.Count; i++) {
+ ToDoElement element = todoList.tasks[i];
string line = "";
if (element.completed) line += "[Complete]";
line += element.text.Replace("\n", @"\n");
@@ -297,6 +563,9 @@ private void Export(string path) {
File.WriteAllText(path, stringBuilder.ToString());
}
+ ///
+ /// Imports tasks from a txt file at the specified path.
+ ///
private void Import(string path) {
string[] lines = File.ReadAllLines(path);
@@ -316,8 +585,8 @@ private void Import(string path) {
text = text,
completed = complete
};
- todoList.list.Add(element);
+ todoList.tasks.Add(element);
}
}
}
-}
+}
\ No newline at end of file
diff --git a/ToDo/SceneReferencesHandler.cs b/ToDo/SceneReferencesHandler.cs
new file mode 100644
index 0000000..fc5ed18
--- /dev/null
+++ b/ToDo/SceneReferencesHandler.cs
@@ -0,0 +1,91 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.SceneManagement;
+
+namespace Cyan.ToDo {
+ ///
+ /// Stores references to objects in the scene, assigning a GUID.
+ /// That GUID can then be used to obtain the object, allowing for cross-scene references
+ /// (as well as references to scene objects for prefabs + ScriptableObject)
+ ///
+ [AddComponentMenu("Cyan/SceneReferences", 2)]
+ public class SceneReferencesHandler : MonoBehaviour {
+
+ public static bool disallowSceneReferences = false;
+ /*
+ * Set to true if you want to disable this functionality.
+ * Storing scene references dirties the scene, so you might want to disable it if working with collab.
+ * Any existing references won't be lost, but GetSceneReferencesHandler(scene) will return null.
+ *
+ * If you want to remove/break existing references for a scene, use GameObject -> Cyan.ToDo -> Remove Scene References Handler
+ */
+
+ [SerializeField] private SerializableDictionary objects = new SerializableDictionary();
+
+ private void Reset() {
+ if (gameObject.hideFlags != HideFlags.HideInHierarchy) {
+ Debug.LogWarning("Cyan.ToDo : Adding a SceneReferences component to a scene manually is not advised. (see console for more info)\n" +
+ "The ToDo system automatically adds this component when required to handle scene object references " +
+ "for the ScriptableObject version of the To Do list, or cross-scene references for the MonoBehaviour version. " +
+ "Regardless of adding the component manually, SceneReferences.GetSceneReferencesHandler will always use the hidden one, " +
+ "or create a hidden one if it doesn't exist.");
+ }
+ }
+
+ ///
+ /// Obtains the SceneReferences component on a hidden GameObject inside the given Scene.
+ /// If createIfNull is true, and it doesn't exist, it will create and add it to the scene.
+ /// Otherwise returns null.
+ ///
+ public static SceneReferencesHandler GetSceneReferencesHandler(Scene scene, bool createIfNull = false) {
+ if (disallowSceneReferences) return null;
+ GameObject[] roots = scene.GetRootGameObjects();
+
+ SceneReferencesHandler sceneReferencesHandler = null;
+ for (int i = 0; i < roots.Length; i++) {
+ GameObject root = roots[i];
+ if (root.TryGetComponent(out sceneReferencesHandler)) {
+ return sceneReferencesHandler;
+ }
+ }
+
+ // Create Scene Reference Handler in scene
+ if (createIfNull) {
+ GameObject obj = new GameObject();
+ obj.hideFlags = HideFlags.HideInHierarchy;
+ SceneManager.MoveGameObjectToScene(obj, scene);
+ obj.transform.SetSiblingIndex(0);
+ sceneReferencesHandler = obj.AddComponent();
+ }
+ return sceneReferencesHandler;
+ }
+
+ public string RegisterObject(Object obj) {
+ string key = GetKeyFromObject(obj);
+ if (key != null) return key;
+ System.Guid guid = System.Guid.NewGuid();
+ string id = guid.ToString();
+ objects.Add(id, obj);
+ return id;
+ }
+
+ public Object GetObjectFromID(string id) {
+ if (objects.TryGetValue(id, out Object obj)) {
+ return obj;
+ }
+ return null;
+ }
+
+ public string GetKeyFromObject(Object obj) {
+ foreach (KeyValuePair entry in objects) {
+ if (entry.Value == obj) {
+ return entry.Key;
+ }
+ }
+ return null;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/ToDo/SceneReferencesHandler.cs.meta b/ToDo/SceneReferencesHandler.cs.meta
new file mode 100644
index 0000000..75dde7a
--- /dev/null
+++ b/ToDo/SceneReferencesHandler.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 893a10e8f0caf1d4aa9664661a3e455c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/ToDo/SerializableDictionary.cs b/ToDo/SerializableDictionary.cs
new file mode 100644
index 0000000..e81ec5a
--- /dev/null
+++ b/ToDo/SerializableDictionary.cs
@@ -0,0 +1,34 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Cyan.ToDo {
+
+ ///
+ /// A version of Dictionary which is serialisable by converting it to and from Lists
+ ///
+ [System.Serializable]
+ public class SerializableDictionary : Dictionary, ISerializationCallbackReceiver {
+ // Note : TKey and TVaue must be Serializable
+
+ [SerializeField] private List keys = new List();
+ [SerializeField] private List values = new List();
+
+ public void OnBeforeSerialize() {
+ // Convert Dictionary to Lists, so Unity can Serialize it
+ keys.Clear();
+ values.Clear();
+ foreach (KeyValuePair pair in this) {
+ keys.Add(pair.Key);
+ values.Add(pair.Value);
+ }
+ }
+
+ public void OnAfterDeserialize() {
+ // Convert Lists to Dictionary
+ Clear();
+ for (int i = 0; i < keys.Count; i++) {
+ Add(keys[i], values[i]);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ToDo/SerializableDictionary.cs.meta b/ToDo/SerializableDictionary.cs.meta
new file mode 100644
index 0000000..1d8d915
--- /dev/null
+++ b/ToDo/SerializableDictionary.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b56fe249d9db40048ba40c0d193e590f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/ToDo/ToDo.cs b/ToDo/ToDo.cs
index fa9c324..e415eca 100644
--- a/ToDo/ToDo.cs
+++ b/ToDo/ToDo.cs
@@ -3,32 +3,46 @@
namespace Cyan.ToDo {
+ ///
+ /// MonoBehaviour version of the To Do list
+ ///
+ [AddComponentMenu("Cyan/To Do", 1)]
public class ToDo : MonoBehaviour {
- public string listName = "To Do";
- public List list = new List();
+ public ToDoList list = new ToDoList();
public void OnDrawGizmos() { }
+ }
+
+ [System.Serializable]
+ public class ToDoList {
+
+ public string listName = "To Do";
+ public List tasks = new List();
+
public void RemoveCompleted() {
- for (int i = list.Count - 1; i >= 0; i--) {
- ToDoElement element = list[i];
+ for (int i = tasks.Count - 1; i >= 0; i--) {
+ ToDoElement element = tasks[i];
if (element.completed) {
- list.RemoveAt(i);
+ tasks.RemoveAt(i);
}
}
}
-
}
[System.Serializable]
public class ToDoElement {
public bool completed = false;
public string text = "";
- public Object objectReference;
+
+ public Object objectReference; // (if cross-scene reference, this is the SceneAsset instead, but that only works in-editor)
+ public string objectReferenceID; // ID for serializing cross-scene reference (see SceneReferences)
+ [System.NonSerialized] public Object tempObjectReference; // actual object for cross-scene reference
- public bool editing = false;
+ [System.NonSerialized] public bool editing = false;
public ToDoElement() { }
}
+
}
diff --git a/ToDo/ToDoSO.cs b/ToDo/ToDoSO.cs
new file mode 100644
index 0000000..073aaae
--- /dev/null
+++ b/ToDo/ToDoSO.cs
@@ -0,0 +1,17 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Cyan.ToDo {
+
+ ///
+ /// ScriptableObject version of the To Do list
+ ///
+ [CreateAssetMenu(fileName = "To Do", menuName = "Cyan.ToDo (Scriptable Object)", order = 1)]
+ public class ToDoSO : ScriptableObject {
+
+ public ToDoList list = new ToDoList();
+
+ }
+
+}
\ No newline at end of file
diff --git a/ToDo/ToDoSO.cs.meta b/ToDo/ToDoSO.cs.meta
new file mode 100644
index 0000000..50b5bd4
--- /dev/null
+++ b/ToDo/ToDoSO.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3516055b53ac5ba4180637ebba7efd4d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 992609eb7d8267442b2a7b5ce0291eef, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
From 42d954b3f687492f90ce926699efa5a3f7901be2 Mon Sep 17 00:00:00 2001
From: Cyan <69320946+Cyanilux@users.noreply.github.com>
Date: Mon, 10 Aug 2020 00:29:43 +0100
Subject: [PATCH 2/8] SceneReferencesHandler fix
Fixed issue with reference handler not being marked dirty, so wasn't being saved. Thought Undo.RecordObject would handle it, but apparently it doesn't when using HideFlags to hide from hierarchy.
---
ToDo/Editor/ToDoEditor.cs | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/ToDo/Editor/ToDoEditor.cs b/ToDo/Editor/ToDoEditor.cs
index 603559f..98c7cb0 100644
--- a/ToDo/Editor/ToDoEditor.cs
+++ b/ToDo/Editor/ToDoEditor.cs
@@ -459,14 +459,16 @@ protected void LinkCrossSceneReference(ToDoElement element, Object obj, GameObje
Debug.Log("SceneObjectReference : " + element.objectReferenceID);
PrefabUtility.RecordPrefabInstancePropertyModifications(target);
+ EditorUtility.SetDirty(target);
}
protected void LinkObjectReference(ToDoElement element, Object obj) {
Undo.RecordObject(target, "Changed Task Object");
element.objectReference = obj;
element.objectReferenceID = null;
- PrefabUtility.RecordPrefabInstancePropertyModifications(target);
Debug.Log("ObjectReference");
+ PrefabUtility.RecordPrefabInstancePropertyModifications(target);
+ EditorUtility.SetDirty(target);
}
private void SetTaskObject(ToDoElement element, Object obj) {
@@ -476,19 +478,14 @@ private void SetTaskObject(ToDoElement element, Object obj) {
if (IsReferenceAllowed(obj, null, out GameObject gameObject)) {
// Asset
LinkObjectReference(element, obj);
-
- // Need to mark ScriptableObject as dirty so it saves changes
- EditorUtility.SetDirty(target);
} else {
// Scene Reference, Store SceneAsset in objectReference
LinkCrossSceneReference(element, obj, gameObject);
- EditorSceneManager.SaveScene(gameObject.scene);
}
} else if (!IsReferenceAllowed(obj, scenePath, out GameObject gameObject)) {
Debug.Log("MonoBehaviour, Cross-Scene");
// ToDo is MonoBehaviour in Scene, but obj is in different scene. Cross-scene reference
LinkCrossSceneReference(element, obj, gameObject);
- EditorSceneManager.SaveScene(gameObject.scene);
} else {
// ToDo is MonoBehaviour, obj is GameObject/Component in same scene or is an Asset
Debug.Log("MonoBehaviour, Same Scene");
From 1d67722700f568ac5cbe41d4c3e6f1a4a8ce620a Mon Sep 17 00:00:00 2001
From: Cyan <69320946+Cyanilux@users.noreply.github.com>
Date: Tue, 11 Aug 2020 16:05:25 +0100
Subject: [PATCH 3/8] Serialisation fixes for Unity 2018/2019
SceneReferenceHandler is now correctly serialised in 2018/2019 versions.
---
ToDo/Editor/ToDoEditor.cs | 19 +++++++++++++++++--
ToDo/SceneReferencesHandler.cs | 8 ++++++--
ToDo/SerializableDictionary.cs | 9 +++++++++
3 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/ToDo/Editor/ToDoEditor.cs b/ToDo/Editor/ToDoEditor.cs
index 98c7cb0..2df0722 100644
--- a/ToDo/Editor/ToDoEditor.cs
+++ b/ToDo/Editor/ToDoEditor.cs
@@ -70,9 +70,14 @@ private void PrefabUpdated(GameObject prefab) {
}
}
}
-
+
// Apply Prefab Changes
+ PrefabUtility.prefabInstanceUpdated -= PrefabUpdated;
PrefabUtility.ApplyObjectOverride(todo, path, InteractionMode.AutomatedAction);
+ PrefabUtility.prefabInstanceUpdated += PrefabUpdated;
+ // Note, in 2018.3 seems to cause unity to crash/hold due to calling PrefabUpdated
+ // so need to unregister + reregister to prevent infinite loop.
+ // This didn't seem to happen in 2020.1, but should probably do it still just to be safe
}
}
@@ -126,8 +131,18 @@ static void DeleteSceneReferences() {
}
}
+ [MenuItem("GameObject/Cyan.ToDo/Select Scene References Handler")]
+ static void SelectSceneReferences() {
+ Scene scene = SceneManager.GetActiveScene();
+ SceneReferencesHandler sceneReferencesHandler = SceneReferencesHandler.GetSceneReferencesHandler(scene);
+ if (sceneReferencesHandler != null) {
+ Selection.activeGameObject = sceneReferencesHandler.gameObject;
+ }
+ }
+
[MenuItem("GameObject/Cyan.ToDo/Remove Scene References Handler", true)]
- static bool ValidateDeleteSceneReferences() {
+ [MenuItem("GameObject/Cyan.ToDo/Select Scene References Handler", true)]
+ static bool ValidateSceneReferences() {
Scene scene = SceneManager.GetActiveScene();
return (SceneReferencesHandler.GetSceneReferencesHandler(scene) != null);
}
diff --git a/ToDo/SceneReferencesHandler.cs b/ToDo/SceneReferencesHandler.cs
index fc5ed18..4a90be6 100644
--- a/ToDo/SceneReferencesHandler.cs
+++ b/ToDo/SceneReferencesHandler.cs
@@ -9,7 +9,7 @@ namespace Cyan.ToDo {
/// That GUID can then be used to obtain the object, allowing for cross-scene references
/// (as well as references to scene objects for prefabs + ScriptableObject)
///
- [AddComponentMenu("Cyan/SceneReferences", 2)]
+ [AddComponentMenu("Cyan/Scene References Handler", 2)]
public class SceneReferencesHandler : MonoBehaviour {
public static bool disallowSceneReferences = false;
@@ -21,8 +21,12 @@ public class SceneReferencesHandler : MonoBehaviour {
* If you want to remove/break existing references for a scene, use GameObject -> Cyan.ToDo -> Remove Scene References Handler
*/
+#if UNITY_2020_1_OR_NEWER
[SerializeField] private SerializableDictionary objects = new SerializableDictionary();
-
+#else
+ [SerializeField] private SerializableDictionary_StringObject objects = new SerializableDictionary_StringObject();
+#endif
+
private void Reset() {
if (gameObject.hideFlags != HideFlags.HideInHierarchy) {
Debug.LogWarning("Cyan.ToDo : Adding a SceneReferences component to a scene manually is not advised. (see console for more info)\n" +
diff --git a/ToDo/SerializableDictionary.cs b/ToDo/SerializableDictionary.cs
index e81ec5a..3c89990 100644
--- a/ToDo/SerializableDictionary.cs
+++ b/ToDo/SerializableDictionary.cs
@@ -3,6 +3,15 @@
namespace Cyan.ToDo {
+#if !UNITY_2020_1_OR_NEWER
+ // Unity can't serialise generic fields like "SerializableDictionary fieldName" (prior to 2020.1)
+ // But it can serialise a class that inherits a generic class, so we need this for previous versions :
+ [System.Serializable]
+ public class SerializableDictionary_StringObject : SerializableDictionary {
+
+ }
+#endif
+
///
/// A version of Dictionary which is serialisable by converting it to and from Lists
///
From 83a2e2ca17089085cd2a016076a7bfc39fc3434c Mon Sep 17 00:00:00 2001
From: Cyan <69320946+Cyanilux@users.noreply.github.com>
Date: Tue, 11 Aug 2020 16:47:22 +0100
Subject: [PATCH 4/8] Update README.md
---
README.md | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 9e5ff0f..b847d9b 100644
--- a/README.md
+++ b/README.md
@@ -4,15 +4,21 @@ Tested in Unity 2020.1, 2019.3 and 2018.3. Unsure if it'll work in versions prio
![To Do](gif.gif)
-• Attached as a **Component** on a **GameObject**. Can have as many as you want.
-• Uses a **Custom Inspector** based on **UnityEditorInternal.ReorderableList**. Easily reorganise tasks by dragging the left side of each element
+## Features :
+• Attached as a **Component** (on a GameObject in Scene or Prefab), or as a **ScriptableObject** Asset. Can have as many as you want.
+• Uses a **Custom Inspector** based on **UnityEditorInternal.ReorderableList**. Easily reorganise tasks by dragging the left side of each element.
• Title of ReorderableList can be edited.
-• Each task includes : **Completion Tickbox**, **Text Area**, and the option to link a **GameObject**, **Component** or **Asset** by dragging it onto the task. Cross-scene references are not supported and will be cleared when reloading the scene. Once an object is linked, it can be removed by using the **Object Field** that appears and selecting *None* at the top.
+• Each task includes : **Completion Tickbox**, **Text Area**, and the option to link a **GameObject**, **Component** or **Asset** by dragging it onto the task. Once an object is linked, it can be removed by using the **Object Field** that appears and selecting *None* at the top.
• Completed tasks turn green. Use the *"Remove Completed Tasks"* button to remove all completed tasks.
• Supports **Rich Text**, (but is disabled while editing the task, so you can see what you are writing)
• Has **Undo / Redo** Support.
-• **Export** Tasks to a txt file (object references will be lost though).
• **Import** Tasks from a txt file. Each line in the file will be added as a task. Lines starting with *"[Complete]"* are marked as completed.
+• **Export** Tasks to a txt file (object references will be lost though).
+• **Cross-Scene Object References** (and scene object references for ScriptableObject) is supported. Linking an object in the same scene will still use the direct object reference, but linking an object from a *different* scene will automatically create and use a hidden GameObject & SceneReferenceHandler script, which assigns a unique ID for the object. This handler is saved in the scene to keep references to objects in that scene, while the SceneAsset and ID is saved in the ToDo list. Cross-scene linked objects will show an asterisk on the task, and the object can only be obtained if the scene is loaded.
+
+## Known Bugs :
+• Regular object references may be lost if a To Do list component is moved between scenes. (Cross-scene object references are unaffected)
+• Creating a Prefab from a GameObject containing the To Do list will update regular object references to the cross-scene method so they are not lost. However this can only occur if the To Do list is seleted / visible in the inspector currently.
@Cyanilux
:)
From 9d0e9dd6b82e96302b4e34d4741c68d72ab09b94 Mon Sep 17 00:00:00 2001
From: Cyan <69320946+Cyanilux@users.noreply.github.com>
Date: Tue, 11 Aug 2020 20:30:27 +0100
Subject: [PATCH 5/8] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index b847d9b..f1cac8a 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ Tested in Unity 2020.1, 2019.3 and 2018.3. Unsure if it'll work in versions prio
## Known Bugs :
• Regular object references may be lost if a To Do list component is moved between scenes. (Cross-scene object references are unaffected)
-• Creating a Prefab from a GameObject containing the To Do list will update regular object references to the cross-scene method so they are not lost. However this can only occur if the To Do list is seleted / visible in the inspector currently.
+• Creating a Prefab from a GameObject containing the To Do list will update regular object references to the cross-scene method so they are not lost. However this can only occur if the To Do list is selected / visible in the inspector currently.
@Cyanilux
:)
From 28f45d9a24040c9b5e12cc68fdb74946f39b7d69 Mon Sep 17 00:00:00 2001
From: Cyan <69320946+Cyanilux@users.noreply.github.com>
Date: Thu, 13 Aug 2020 20:17:51 +0100
Subject: [PATCH 6/8] Bug fixes
Fixed issues with saving prefab instance modifications
Fixed issue with dark mode text colour. Text cursor also now uses the same colour as the text.
Removed debug messages
---
ToDo/Editor/ToDoEditor.cs | 70 ++++++++++++++++++++++++---------------
1 file changed, 43 insertions(+), 27 deletions(-)
diff --git a/ToDo/Editor/ToDoEditor.cs b/ToDo/Editor/ToDoEditor.cs
index 2df0722..2dcf331 100644
--- a/ToDo/Editor/ToDoEditor.cs
+++ b/ToDo/Editor/ToDoEditor.cs
@@ -63,7 +63,6 @@ private void PrefabUpdated(GameObject prefab) {
// It's okay, it's the prefab itself, so we can ignore it!
} else {
// Is in-scene object reference, need to swap to cross-scene reference
- Debug.Log("Swapping to Cross-scene Reference!");
LinkCrossSceneReference(element, element.objectReference, gameObject);
}
}
@@ -108,7 +107,7 @@ public class ToDoEditor : Editor {
private bool actions;
- [MenuItem("GameObject/Create Other/Cyan.ToDo (MonoBehaviour)")]
+ [MenuItem("GameObject/Create Other/Cyan/To Do List (MonoBehaviour)")] // Cyan.ToDo (MonoBehaviour)
static void Create() {
GameObject obj = new GameObject("To Do");
obj.AddComponent();
@@ -182,6 +181,7 @@ public override void OnInspectorGUI() {
// Remove Completed Tasks
Undo.RecordObject(target, "Remove Completed Tasks");
todoList.RemoveCompleted();
+ RecordPrefabInstancePropertyModifications(target);
}
}
@@ -200,6 +200,7 @@ public override void OnInspectorGUI() {
if (path.Length != 0) {
Import(path);
}
+ RecordPrefabInstancePropertyModifications(target);
}
}
@@ -211,11 +212,13 @@ public override void OnInspectorGUI() {
private void OnAdd(ReorderableList list) {
Undo.RecordObject(target, "Task Added");
todoList.tasks.Add(new ToDoElement());
+ RecordPrefabInstancePropertyModifications(target);
}
private void OnRemove(ReorderableList list) {
Undo.RecordObject(target, "Task Removed");
todoList.tasks.RemoveAt(list.index);
+ RecordPrefabInstancePropertyModifications(target);
}
private void DrawHeader(Rect rect) {
@@ -234,6 +237,7 @@ private void DrawHeader(Rect rect) {
if (EditorGUI.EndChangeCheck()) {
Undo.RecordObject(target, "List Name Change");
todoList.listName = listName;
+ RecordPrefabInstancePropertyModifications(target);
}
}
@@ -259,6 +263,10 @@ private void CreateTextStyles() {
}
private void ResetTextColor() {
+ SetTextColor(textColor);
+ }
+
+ private void SetTextColor(Color textColor) {
style_textArea.normal.textColor = textColor;
style_textArea.focused.textColor = textColor;
style_textArea.hover.textColor = textColor;
@@ -266,6 +274,8 @@ private void ResetTextColor() {
style_label.normal.textColor = textColor;
style_label.focused.textColor = textColor;
style_label.hover.textColor = textColor;
+
+ GUI.skin.settings.cursorColor = textColor;
}
private float ElementHeight(int index) {
@@ -337,24 +347,23 @@ private void DrawElement(Rect rect, int index, bool active, bool focus) {
if (EditorGUI.EndChangeCheck()) {
Undo.RecordObject(target, "Toggled Task Completion");
element.completed = completed;
- }
-
- // This prevents text area from highlighting all text on focus
- bool preventSelection = (UnityEngine.Event.current.type == EventType.MouseDown);
- Color cursorColor = GUI.skin.settings.cursorColor;
- if (preventSelection) {
- GUI.skin.settings.cursorColor = new Color(0, 0, 0, 0);
+ RecordPrefabInstancePropertyModifications(target);
}
// Text Colours
if (completed) {
- style_textArea.normal.textColor = completedTextColor;
- style_textArea.focused.textColor = completedTextColor;
- style_textArea.hover.textColor = completedTextColor;
+ SetTextColor(completedTextColor);
} else {
ResetTextColor();
}
-
+
+ // This prevents text area from highlighting all text on focus
+ bool preventSelection = (UnityEngine.Event.current.type == EventType.MouseDown);
+ //Color cursorColor = GUI.skin.settings.cursorColor;
+ if (preventSelection) {
+ GUI.skin.settings.cursorColor = new Color(0, 0, 0, 0);
+ }
+
// If editing, turn off richText
if (element.editing) {
style_textArea.richText = false;
@@ -368,6 +377,7 @@ private void DrawElement(Rect rect, int index, bool active, bool focus) {
}
EditorGUI.BeginChangeCheck();
GUI.SetNextControlName("TextArea");
+
string text = EditorGUI.TextArea(
new Rect(rect.x + x, rect.y + 2, rect.width - x, textHeight),
element.text, style_textArea);
@@ -378,13 +388,9 @@ private void DrawElement(Rect rect, int index, bool active, bool focus) {
if (EditorGUI.EndChangeCheck()) {
Undo.RecordObject(target, "Edited Task Text");
element.text = text;
+ RecordPrefabInstancePropertyModifications(target);
}
-
- // Reset Cursor Color
- if (preventSelection) {
- GUI.skin.settings.cursorColor = cursorColor;
- }
-
+
// Object Reference
if (element.objectReference) { // either in-scene reference, or is SceneAsset
Object objectReference = null;
@@ -461,6 +467,17 @@ private void DrawElement(Rect rect, int index, bool active, bool focus) {
}
}
+ private void RecordPrefabInstancePropertyModifications(Object target) {
+#if UNITY_2018_3_OR_NEWER
+ bool isPrefabInstance = PrefabUtility.IsPartOfPrefabInstance(target);
+#else
+ bool isPrefabInstance = (PrefabUtility.GetPrefabType(target) == PrefabType.PrefabInstance);
+#endif
+ if (isPrefabInstance) {
+ PrefabUtility.RecordPrefabInstancePropertyModifications(target);
+ }
+ }
+
protected void LinkCrossSceneReference(ToDoElement element, Object obj, GameObject gameObject) {
Scene scene = gameObject.scene;
SceneAsset sceneAsset = GetSceneAsset(scene);
@@ -472,24 +489,26 @@ protected void LinkCrossSceneReference(ToDoElement element, Object obj, GameObje
element.objectReferenceID = sceneReferencesHandler.RegisterObject(obj);
element.tempObjectReference = null;
- Debug.Log("SceneObjectReference : " + element.objectReferenceID);
- PrefabUtility.RecordPrefabInstancePropertyModifications(target);
+ //Debug.Log("SceneObjectReference : " + element.objectReferenceID);
+ RecordPrefabInstancePropertyModifications(target);
EditorUtility.SetDirty(target);
}
protected void LinkObjectReference(ToDoElement element, Object obj) {
Undo.RecordObject(target, "Changed Task Object");
+
element.objectReference = obj;
element.objectReferenceID = null;
- Debug.Log("ObjectReference");
- PrefabUtility.RecordPrefabInstancePropertyModifications(target);
+ element.tempObjectReference = null;
+
+ //Debug.Log("ObjectReference");
+ RecordPrefabInstancePropertyModifications(target);
EditorUtility.SetDirty(target);
}
private void SetTaskObject(ToDoElement element, Object obj) {
if (scenePath == null) {
// ToDo is Prefab / ScriptableObject Asset
- Debug.Log("Prefab / ScriptableObject");
if (IsReferenceAllowed(obj, null, out GameObject gameObject)) {
// Asset
LinkObjectReference(element, obj);
@@ -498,12 +517,10 @@ private void SetTaskObject(ToDoElement element, Object obj) {
LinkCrossSceneReference(element, obj, gameObject);
}
} else if (!IsReferenceAllowed(obj, scenePath, out GameObject gameObject)) {
- Debug.Log("MonoBehaviour, Cross-Scene");
// ToDo is MonoBehaviour in Scene, but obj is in different scene. Cross-scene reference
LinkCrossSceneReference(element, obj, gameObject);
} else {
// ToDo is MonoBehaviour, obj is GameObject/Component in same scene or is an Asset
- Debug.Log("MonoBehaviour, Same Scene");
LinkObjectReference(element, obj);
}
}
@@ -553,7 +570,6 @@ protected bool IsReferenceAllowed(Object obj, string scenePath, out GameObject g
}
}
if (gameObject == null) return true; // Not a GameObject, would be an Asset, so always allowed.
- Debug.Log("IsReferenceAllowed, Scene : " + gameObject.scene.path);
return (gameObject.scene.path == scenePath); // Same scene = allow, Cross-scene = not allowed
}
From 0c079ec6c9be1b00656db8fa343d111194313bfe Mon Sep 17 00:00:00 2001
From: Cyan <69320946+Cyanilux@users.noreply.github.com>
Date: Thu, 13 Aug 2020 20:25:39 +0100
Subject: [PATCH 7/8] Changed create menu names
GameObject Create menu now shows as "To Do List (MonoBehaviour)"
Asset Create menu now shows as "To Do List (ScriptableObject)"
---
ToDo/Editor/ToDoEditor.cs | 2 +-
ToDo/ToDoSO.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/ToDo/Editor/ToDoEditor.cs b/ToDo/Editor/ToDoEditor.cs
index 2dcf331..1836a56 100644
--- a/ToDo/Editor/ToDoEditor.cs
+++ b/ToDo/Editor/ToDoEditor.cs
@@ -107,7 +107,7 @@ public class ToDoEditor : Editor {
private bool actions;
- [MenuItem("GameObject/Create Other/Cyan/To Do List (MonoBehaviour)")] // Cyan.ToDo (MonoBehaviour)
+ [MenuItem("GameObject/Create Other/To Do List (MonoBehaviour)")]
static void Create() {
GameObject obj = new GameObject("To Do");
obj.AddComponent();
diff --git a/ToDo/ToDoSO.cs b/ToDo/ToDoSO.cs
index 073aaae..93ce2fb 100644
--- a/ToDo/ToDoSO.cs
+++ b/ToDo/ToDoSO.cs
@@ -7,7 +7,7 @@ namespace Cyan.ToDo {
///
/// ScriptableObject version of the To Do list
///
- [CreateAssetMenu(fileName = "To Do", menuName = "Cyan.ToDo (Scriptable Object)", order = 1)]
+ [CreateAssetMenu(fileName = "To Do", menuName = "To Do List (ScriptableObject)", order = 1)]
public class ToDoSO : ScriptableObject {
public ToDoList list = new ToDoList();
From 76a59df8030cef7571ec54660335272e83f6ddf4 Mon Sep 17 00:00:00 2001
From: Cyan <69320946+Cyanilux@users.noreply.github.com>
Date: Thu, 13 Aug 2020 20:50:00 +0100
Subject: [PATCH 8/8] Fix for Unity versions prior to 2019.2
Added UNITY_2019_2_OR_NEWER check for TryGetComponent usage in GetSceneReferencesHandler to prevent error in previous versions. Just uses GetComponent and null check instead.
---
ToDo/SceneReferencesHandler.cs | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/ToDo/SceneReferencesHandler.cs b/ToDo/SceneReferencesHandler.cs
index 4a90be6..e3505e1 100644
--- a/ToDo/SceneReferencesHandler.cs
+++ b/ToDo/SceneReferencesHandler.cs
@@ -49,9 +49,16 @@ public static SceneReferencesHandler GetSceneReferencesHandler(Scene scene, bool
SceneReferencesHandler sceneReferencesHandler = null;
for (int i = 0; i < roots.Length; i++) {
GameObject root = roots[i];
+#if UNITY_2019_2_OR_NEWER
if (root.TryGetComponent(out sceneReferencesHandler)) {
return sceneReferencesHandler;
}
+#else
+ sceneReferencesHandler = root.GetComponent();
+ if (sceneReferencesHandler != null) {
+ return sceneReferencesHandler;
+ }
+#endif
}
// Create Scene Reference Handler in scene