From c2b2fce12ab2616918d03867294b9d76306700ff Mon Sep 17 00:00:00 2001 From: Patrik Holmberg Date: Sat, 2 Jul 2022 10:14:26 +0200 Subject: [PATCH 1/8] Updated: .gitignore with suggested Unity files and folders to ignore --- .gitignore | 80 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index d2d0774..4a412d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,74 @@ -Library/* -ProjectSettings/* -UnityPackageManager/* -Temp/* -Build/* +# This .gitignore file should be placed at the root of your Unity project directory +# +# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore +# +/[Ll]ibrary/ +/[Tt]emp/ +/[Oo]bj/ +/[Bb]uild/ +/[Bb]uilds/ +/[Ll]ogs/ +/[Uu]ser[Ss]ettings/ + +# MemoryCaptures can get excessive in size. +# They also could contain extremely sensitive data +/[Mm]emoryCaptures/ + +# Recordings can get excessive in size +/[Rr]ecordings/ + +# Uncomment this line if you wish to ignore the asset store tools plugin +# /[Aa]ssets/AssetStoreTools* + +# Autogenerated Jetbrains Rider plugin +/[Aa]ssets/Plugins/Editor/JetBrains* + +# Visual Studio cache directory +.vs/ + +# Gradle cache directory +.gradle/ + +# Autogenerated VS/MD/Consulo solution and project files +ExportedObj/ +.consulo/ *.csproj +*.unityproj *.sln -.vs/* -Logs/Packages-Update.log -Packages/manifest.json +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd +*.pdb +*.mdb +*.opendb +*.VC.db + +# Unity3D generated meta files +*.pidb.meta +*.pdb.meta +*.mdb.meta + +# Unity3D generated file on crash reports +sysinfo.txt + +# Builds +*.apk +*.aab +*.unitypackage +*.app + +# Crashlytics generated file +crashlytics-build.properties + +# Packed Addressables +/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* + +# Temporary auto-generated Android Assets +/[Aa]ssets/[Ss]treamingAssets/aa.meta +/[Aa]ssets/[Ss]treamingAssets/aa/* + +.idea/ From 120469739502bee0c92c693485eb1c1832b71262 Mon Sep 17 00:00:00 2001 From: Patrik Holmberg Date: Sat, 2 Jul 2022 10:23:29 +0200 Subject: [PATCH 2/8] Updated: Gave README a new look --- README.md | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index b9c04d3..4c7aa67 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,19 @@ -Guid Based Reference +## Guid Based Reference +License - Please see [LICENSE.md](LICENSE.md) in this repository -License - Please see LICENSE.md in this repository +### Summary +A component to give a Globaly Unique IDentifier (GUID) to a Game Object. This GUID can then be used to reference an +object even if it is another Scene, not loaded yet, or otherwise not easy to directly reference. -Summary -A component to give a Globaly Unique IDentifier (GUID) to a Game Object. -This GUID can then be used to reference an object even if it is another Scene, not loaded yet, or otherwise not easy to directly reference. - -Maintainers +### Maintainers William Armstrong williama@unity3d.com -To Use: - -Add a GuidComponent to any object you want to be able to reference. - -In any code that needs to be able to reference objects by GUID, add a GuidReference field. - -GuidReference.gameObject will then return the GameObject if it is loaded, otherwise null. - -Look in the CrossSceneReference/SampleContent folder for example usage. - -Load up the LoadFirst scene, and then use the SceneLoader object to load 'LoadSecond' +### How to use +1. Add a GuidComponent to any object you want to be able to reference. +2. In any code that needs to be able to reference objects by GUID, add a GuidReference field. +3. GuidReference.gameObject will then return the GameObject if it is loaded, otherwise null. +### Sample +Look in the Samples folder for example usage. Load up the LoadFirst scene, and then use the SceneLoader object to load ' +LoadSecond'. You should see the CrossSceneReferencer object find the CrossSceneTarget object, and set both of them to start spinning. - From 7594fa9505cec7b23b1e69cb9765cdc50d485088 Mon Sep 17 00:00:00 2001 From: Patrik Holmberg Date: Sat, 2 Jul 2022 10:43:52 +0200 Subject: [PATCH 3/8] Added: Markdown meta files --- CONTRIBUTIONS.md.meta | 7 +++++++ LICENSE.md.meta | 7 +++++++ README.md.meta | 7 +++++++ 3 files changed, 21 insertions(+) create mode 100644 CONTRIBUTIONS.md.meta create mode 100644 LICENSE.md.meta create mode 100644 README.md.meta diff --git a/CONTRIBUTIONS.md.meta b/CONTRIBUTIONS.md.meta new file mode 100644 index 0000000..c218ac3 --- /dev/null +++ b/CONTRIBUTIONS.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: bb86795fa476941459d0e2e9c93ced12 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/LICENSE.md.meta b/LICENSE.md.meta new file mode 100644 index 0000000..2ef145a --- /dev/null +++ b/LICENSE.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 867d74fe67fba224789ece974f48ff88 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md.meta b/README.md.meta new file mode 100644 index 0000000..eaa5c30 --- /dev/null +++ b/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a9412e3a094a4f24a91acb910d64e643 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 8f57544b044c53c3b51604c64aabc53de2cc989d Mon Sep 17 00:00:00 2001 From: Patrik Holmberg Date: Sat, 2 Jul 2022 11:11:08 +0200 Subject: [PATCH 4/8] Restructured folders and files, added ASMDEF files and added namespace to all .cs files --- Assets/CrossSceneReference.meta | 10 - .../Documentation~/guid-based-reference.md | 23 --- .../Editor/GuidComponentDrawer.cs | 19 -- .../Editor/GuidReferenceDrawer.cs | 134 ------------- .../Runtime/GuidComponent.cs | 177 ----------------- .../Runtime/GuidManager.cs | 185 ------------------ .../CrossSceneReference/Samples/LoadScenes.cs | 62 ------ .../Samples/TestCrossScene.cs | 60 ------ .../Tests/Editor/GuidReferenceTests.cs | 107 ---------- Documentation~/guid-based-reference.md | 19 ++ .../Editor.meta => Editor.meta | 0 Editor/GuidComponentDrawer.cs | 17 ++ .../GuidComponentDrawer.cs.meta | 0 Editor/GuidReferenceDrawer.cs | 121 ++++++++++++ .../GuidReferenceDrawer.cs.meta | 0 Editor/com.unity.guid.Editor.asmdef | 18 ++ Editor/com.unity.guid.Editor.asmdef.meta | 7 + .../Runtime.meta => Runtime.meta | 0 Runtime/GuidComponent.cs | 145 ++++++++++++++ .../Runtime => Runtime}/GuidComponent.cs.meta | 0 Runtime/GuidManager.cs | 160 +++++++++++++++ .../Runtime => Runtime}/GuidManager.cs.meta | 0 .../Runtime => Runtime}/GuidReference.cs | 89 ++++----- .../Runtime => Runtime}/GuidReference.cs.meta | 0 Runtime/com.unity.guid.asmdef | 14 ++ Runtime/com.unity.guid.asmdef.meta | 7 + .../Samples.meta => Samples~.meta | 0 .../Samples => Samples~}/LoadFirst.unity | 0 .../Samples => Samples~}/LoadFirst.unity.meta | 2 +- Samples~/LoadScenes.cs | 52 +++++ .../Samples => Samples~}/LoadScenes.cs.meta | 2 +- .../Samples => Samples~}/LoadSecond.unity | 0 .../LoadSecond.unity.meta | 2 +- Samples~/TestCrossScene.cs | 46 +++++ .../TestCrossScene.cs.meta | 2 +- Samples~/com.unity.guid.samples.asmdef | 16 ++ Samples~/com.unity.guid.samples.asmdef.meta | 7 + .../Tests.meta => Tests.meta | 0 .../Tests/Editor.meta => Tests/Runtime.meta | 0 Tests/Runtime/GuidReferenceTests.cs | 99 ++++++++++ .../Runtime}/GuidReferenceTests.cs.meta | 0 Tests/Runtime/com.unity.guid.Tests.asmdef | 22 +++ .../Runtime/com.unity.guid.Tests.asmdef.meta | 7 + 43 files changed, 801 insertions(+), 830 deletions(-) delete mode 100644 Assets/CrossSceneReference.meta delete mode 100644 Assets/CrossSceneReference/Documentation~/guid-based-reference.md delete mode 100644 Assets/CrossSceneReference/Editor/GuidComponentDrawer.cs delete mode 100644 Assets/CrossSceneReference/Editor/GuidReferenceDrawer.cs delete mode 100644 Assets/CrossSceneReference/Runtime/GuidComponent.cs delete mode 100644 Assets/CrossSceneReference/Runtime/GuidManager.cs delete mode 100644 Assets/CrossSceneReference/Samples/LoadScenes.cs delete mode 100644 Assets/CrossSceneReference/Samples/TestCrossScene.cs delete mode 100644 Assets/CrossSceneReference/Tests/Editor/GuidReferenceTests.cs create mode 100644 Documentation~/guid-based-reference.md rename Assets/CrossSceneReference/Editor.meta => Editor.meta (100%) create mode 100644 Editor/GuidComponentDrawer.cs rename {Assets/CrossSceneReference/Editor => Editor}/GuidComponentDrawer.cs.meta (100%) create mode 100644 Editor/GuidReferenceDrawer.cs rename {Assets/CrossSceneReference/Editor => Editor}/GuidReferenceDrawer.cs.meta (100%) create mode 100644 Editor/com.unity.guid.Editor.asmdef create mode 100644 Editor/com.unity.guid.Editor.asmdef.meta rename Assets/CrossSceneReference/Runtime.meta => Runtime.meta (100%) create mode 100644 Runtime/GuidComponent.cs rename {Assets/CrossSceneReference/Runtime => Runtime}/GuidComponent.cs.meta (100%) create mode 100644 Runtime/GuidManager.cs rename {Assets/CrossSceneReference/Runtime => Runtime}/GuidManager.cs.meta (100%) rename {Assets/CrossSceneReference/Runtime => Runtime}/GuidReference.cs (55%) rename {Assets/CrossSceneReference/Runtime => Runtime}/GuidReference.cs.meta (100%) create mode 100644 Runtime/com.unity.guid.asmdef create mode 100644 Runtime/com.unity.guid.asmdef.meta rename Assets/CrossSceneReference/Samples.meta => Samples~.meta (100%) rename {Assets/CrossSceneReference/Samples => Samples~}/LoadFirst.unity (100%) rename {Assets/CrossSceneReference/Samples => Samples~}/LoadFirst.unity.meta (80%) create mode 100644 Samples~/LoadScenes.cs rename {Assets/CrossSceneReference/Samples => Samples~}/LoadScenes.cs.meta (86%) rename {Assets/CrossSceneReference/Samples => Samples~}/LoadSecond.unity (100%) rename {Assets/CrossSceneReference/Samples => Samples~}/LoadSecond.unity.meta (80%) create mode 100644 Samples~/TestCrossScene.cs rename {Assets/CrossSceneReference/Samples => Samples~}/TestCrossScene.cs.meta (83%) create mode 100644 Samples~/com.unity.guid.samples.asmdef create mode 100644 Samples~/com.unity.guid.samples.asmdef.meta rename Assets/CrossSceneReference/Tests.meta => Tests.meta (100%) rename Assets/CrossSceneReference/Tests/Editor.meta => Tests/Runtime.meta (100%) create mode 100644 Tests/Runtime/GuidReferenceTests.cs rename {Assets/CrossSceneReference/Tests/Editor => Tests/Runtime}/GuidReferenceTests.cs.meta (100%) create mode 100644 Tests/Runtime/com.unity.guid.Tests.asmdef create mode 100644 Tests/Runtime/com.unity.guid.Tests.asmdef.meta diff --git a/Assets/CrossSceneReference.meta b/Assets/CrossSceneReference.meta deleted file mode 100644 index bd8323a..0000000 --- a/Assets/CrossSceneReference.meta +++ /dev/null @@ -1,10 +0,0 @@ -fileFormatVersion: 2 -guid: 8013f34f0b656684fbc03322c7682ed3 -folderAsset: yes -timeCreated: 1523920636 -licenseType: Pro -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/CrossSceneReference/Documentation~/guid-based-reference.md b/Assets/CrossSceneReference/Documentation~/guid-based-reference.md deleted file mode 100644 index 08b1869..0000000 --- a/Assets/CrossSceneReference/Documentation~/guid-based-reference.md +++ /dev/null @@ -1,23 +0,0 @@ -Guid Based Reference - -Summary -A component to give a Globaly Unique IDentifier (GUID) to a Game Object. -This GUID can then be used to reference an object even if it is another Scene, not loaded yet, or otherwise not easy to directly reference. - -Maintainers -William Armstrong williama@unity3d.com - -To Use: - -Add a GuidComponent to any object you want to be able to reference. - -In any code that needs to be able to reference objects by GUID, add a GuidReference field. - -GuidReference.gameObject will then return the GameObject if it is loaded, otherwise null. - -Look in the CrossSceneReference/SampleContent folder for example usage. - -Load up the LoadFirst scene, and then use the SceneLoader object to load 'LoadSecond' - -You should see the CrossSceneReferencer object find the CrossSceneTarget object, and set both of them to start spinning. - diff --git a/Assets/CrossSceneReference/Editor/GuidComponentDrawer.cs b/Assets/CrossSceneReference/Editor/GuidComponentDrawer.cs deleted file mode 100644 index a7640ac..0000000 --- a/Assets/CrossSceneReference/Editor/GuidComponentDrawer.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UnityEditor; -using UnityEngine; - -[CustomEditor(typeof(GuidComponent))] -public class GuidComponentDrawer : Editor -{ - private GuidComponent guidComp; - - public override void OnInspectorGUI() - { - if (guidComp == null) - { - guidComp = (GuidComponent)target; - } - - // Draw label - EditorGUILayout.LabelField("Guid:", guidComp.GetGuid().ToString()); - } -} \ No newline at end of file diff --git a/Assets/CrossSceneReference/Editor/GuidReferenceDrawer.cs b/Assets/CrossSceneReference/Editor/GuidReferenceDrawer.cs deleted file mode 100644 index 7a114d6..0000000 --- a/Assets/CrossSceneReference/Editor/GuidReferenceDrawer.cs +++ /dev/null @@ -1,134 +0,0 @@ -using UnityEditor; -using UnityEngine; - -// Using a property drawer to allow any class to have a field of type GuidRefernce and still get good UX -// If you are writing your own inspector for a class that uses a GuidReference, drawing it with -// EditorLayout.PropertyField(prop) or similar will get this to show up automatically -[CustomPropertyDrawer(typeof(GuidReference))] -public class GuidReferenceDrawer : PropertyDrawer -{ - SerializedProperty guidProp; - SerializedProperty sceneProp; - SerializedProperty nameProp; - - // cache off GUI content to avoid creating garbage every frame in editor - GUIContent sceneLabel = new GUIContent("Containing Scene", "The target object is expected in this scene asset."); - GUIContent clearButtonGUI = new GUIContent("Clear", "Remove Cross Scene Reference"); - - // add an extra line to display source scene for targets - public override float GetPropertyHeight(SerializedProperty property, GUIContent label) - { - return base.GetPropertyHeight(property, label) + EditorGUIUtility.singleLineHeight; - } - - public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) - { - - guidProp = property.FindPropertyRelative("serializedGuid"); - nameProp = property.FindPropertyRelative("cachedName"); - sceneProp = property.FindPropertyRelative("cachedScene"); - - // Using BeginProperty / EndProperty on the parent property means that - // prefab override logic works on the entire property. - EditorGUI.BeginProperty(position, label, property); - - position.height = EditorGUIUtility.singleLineHeight; - - // Draw prefix label, returning the new rect we can draw in - var guidCompPosition = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); - - System.Guid currentGuid; - GameObject currentGO = null; - - // working with array properties is a bit unwieldy - // you have to get the property at each index manually - byte[] byteArray = new byte[16]; - int arraySize = guidProp.arraySize; - for( int i = 0; i < arraySize; ++i ) - { - var byteProp = guidProp.GetArrayElementAtIndex(i); - byteArray[i] = (byte)byteProp.intValue; - } - - currentGuid = new System.Guid(byteArray); - currentGO = GuidManager.ResolveGuid(currentGuid); - GuidComponent currentGuidComponent = currentGO != null ? currentGO.GetComponent() : null; - - GuidComponent component = null; - - if (currentGuid != System.Guid.Empty && currentGuidComponent == null) - { - // if our reference is set, but the target isn't loaded, we display the target and the scene it is in, and provide a way to clear the reference - float buttonWidth = 55.0f; - - guidCompPosition.xMax -= buttonWidth; - - bool guiEnabled = GUI.enabled; - GUI.enabled = false; - EditorGUI.LabelField(guidCompPosition, new GUIContent(nameProp.stringValue, "Target GameObject is not currently loaded."), EditorStyles.objectField); - GUI.enabled = guiEnabled; - - Rect clearButtonRect = new Rect(guidCompPosition); - clearButtonRect.xMin = guidCompPosition.xMax; - clearButtonRect.xMax += buttonWidth; - - if (GUI.Button(clearButtonRect, clearButtonGUI, EditorStyles.miniButton)) - { - ClearPreviousGuid(); - } - } - else - { - // if our object is loaded, we can simply use an object field directly - component = EditorGUI.ObjectField(guidCompPosition, currentGuidComponent, typeof(GuidComponent), true) as GuidComponent; - } - - if (currentGuidComponent != null && component == null) - { - ClearPreviousGuid(); - } - - // if we have a valid reference, draw the scene name of the scene it lives in so users can find it - if (component != null) - { - nameProp.stringValue = component.name; - string scenePath = component.gameObject.scene.path; - sceneProp.objectReferenceValue = AssetDatabase.LoadAssetAtPath(scenePath); - - // only update the GUID Prop if something changed. This fixes multi-edit on GUID References - if (component != currentGuidComponent) - { - byteArray = component.GetGuid().ToByteArray(); - arraySize = guidProp.arraySize; - for (int i = 0; i < arraySize; ++i) - { - var byteProp = guidProp.GetArrayElementAtIndex(i); - byteProp.intValue = byteArray[i]; - } - } - } - - EditorGUI.indentLevel++; - position.y += EditorGUIUtility.singleLineHeight; - bool cachedGUIState = GUI.enabled; - GUI.enabled = false; - EditorGUI.ObjectField(position, sceneLabel, sceneProp.objectReferenceValue, typeof(SceneAsset), false); - GUI.enabled = cachedGUIState; - EditorGUI.indentLevel--; - - EditorGUI.EndProperty(); - } - - void ClearPreviousGuid() - { - nameProp.stringValue = string.Empty; - sceneProp.objectReferenceValue = null; - - int arraySize = guidProp.arraySize; - for (int i = 0; i < arraySize; ++i) - { - var byteProp = guidProp.GetArrayElementAtIndex(i); - byteProp.intValue = 0; - } - } -} \ No newline at end of file diff --git a/Assets/CrossSceneReference/Runtime/GuidComponent.cs b/Assets/CrossSceneReference/Runtime/GuidComponent.cs deleted file mode 100644 index d27c652..0000000 --- a/Assets/CrossSceneReference/Runtime/GuidComponent.cs +++ /dev/null @@ -1,177 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - -#if UNITY_EDITOR -using UnityEditor; -using UnityEditor.SceneManagement; -using UnityEditor.Experimental.SceneManagement; -#endif - -// This component gives a GameObject a stable, non-replicatable Globally Unique IDentifier. -// It can be used to reference a specific instance of an object no matter where it is. -// This can also be used for other systems, such as Save/Load game -[ExecuteInEditMode, DisallowMultipleComponent] -public class GuidComponent : MonoBehaviour, ISerializationCallbackReceiver -{ - // System guid we use for comparison and generation - System.Guid guid = System.Guid.Empty; - - // Unity's serialization system doesn't know about System.Guid, so we convert to a byte array - // Fun fact, we tried using strings at first, but that allocated memory and was twice as slow - [SerializeField] - private byte[] serializedGuid; - - - public bool IsGuidAssigned() - { - return guid != System.Guid.Empty; - } - - - // When de-serializing or creating this component, we want to either restore our serialized GUID - // or create a new one. - void CreateGuid() - { - // if our serialized data is invalid, then we are a new object and need a new GUID - if (serializedGuid == null || serializedGuid.Length != 16) - { -#if UNITY_EDITOR - // if in editor, make sure we aren't a prefab of some kind - if (IsAssetOnDisk()) - { - return; - } - Undo.RecordObject(this, "Added GUID"); -#endif - guid = System.Guid.NewGuid(); - serializedGuid = guid.ToByteArray(); - -#if UNITY_EDITOR - // If we are creating a new GUID for a prefab instance of a prefab, but we have somehow lost our prefab connection - // force a save of the modified prefab instance properties - if (PrefabUtility.IsPartOfNonAssetPrefabInstance(this)) - { - PrefabUtility.RecordPrefabInstancePropertyModifications(this); - } -#endif - } - else if (guid == System.Guid.Empty) - { - // otherwise, we should set our system guid to our serialized guid - guid = new System.Guid(serializedGuid); - } - - // register with the GUID Manager so that other components can access this - if (guid != System.Guid.Empty) - { - if (!GuidManager.Add(this)) - { - // if registration fails, we probably have a duplicate or invalid GUID, get us a new one. - serializedGuid = null; - guid = System.Guid.Empty; - CreateGuid(); - } - } - } - -#if UNITY_EDITOR - private bool IsEditingInPrefabMode() - { - if (EditorUtility.IsPersistent(this)) - { - // if the game object is stored on disk, it is a prefab of some kind, despite not returning true for IsPartOfPrefabAsset =/ - return true; - } - else - { - // If the GameObject is not persistent let's determine which stage we are in first because getting Prefab info depends on it - var mainStage = StageUtility.GetMainStageHandle(); - var currentStage = StageUtility.GetStageHandle(gameObject); - if (currentStage != mainStage) - { - var prefabStage = PrefabStageUtility.GetPrefabStage(gameObject); - if (prefabStage != null) - { - return true; - } - } - } - return false; - } - - private bool IsAssetOnDisk() - { - return PrefabUtility.IsPartOfPrefabAsset(this) || IsEditingInPrefabMode(); - } -#endif - - // We cannot allow a GUID to be saved into a prefab, and we need to convert to byte[] - public void OnBeforeSerialize() - { -#if UNITY_EDITOR - // This lets us detect if we are a prefab instance or a prefab asset. - // A prefab asset cannot contain a GUID since it would then be duplicated when instanced. - if (IsAssetOnDisk()) - { - serializedGuid = null; - guid = System.Guid.Empty; - } - else -#endif - { - if (guid != System.Guid.Empty) - { - serializedGuid = guid.ToByteArray(); - } - } - } - - // On load, we can go head a restore our system guid for later use - public void OnAfterDeserialize() - { - if (serializedGuid != null && serializedGuid.Length == 16) - { - guid = new System.Guid(serializedGuid); - } - } - - void Awake() - { - CreateGuid(); - } - - void OnValidate() - { -#if UNITY_EDITOR - // similar to on Serialize, but gets called on Copying a Component or Applying a Prefab - // at a time that lets us detect what we are - if (IsAssetOnDisk()) - { - serializedGuid = null; - guid = System.Guid.Empty; - } - else -#endif - { - CreateGuid(); - } - } - - // Never return an invalid GUID - public System.Guid GetGuid() - { - if (guid == System.Guid.Empty && serializedGuid != null && serializedGuid.Length == 16) - { - guid = new System.Guid(serializedGuid); - } - - return guid; - } - - // let the manager know we are gone, so other objects no longer find this - public void OnDestroy() - { - GuidManager.Remove(guid); - } -} diff --git a/Assets/CrossSceneReference/Runtime/GuidManager.cs b/Assets/CrossSceneReference/Runtime/GuidManager.cs deleted file mode 100644 index 2ae9523..0000000 --- a/Assets/CrossSceneReference/Runtime/GuidManager.cs +++ /dev/null @@ -1,185 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; -using System; - -// Class to handle registering and accessing objects by GUID -public class GuidManager -{ - // for each GUID we need to know the Game Object it references - // and an event to store all the callbacks that need to know when it is destroyed - private struct GuidInfo - { - public GameObject go; - - public event Action OnAdd; - public event Action OnRemove; - - public GuidInfo(GuidComponent comp) - { - go = comp.gameObject; - OnRemove = null; - OnAdd = null; - } - - public void HandleAddCallback() - { - if (OnAdd != null) - { - OnAdd(go); - } - } - - public void HandleRemoveCallback() - { - if (OnRemove != null) - { - OnRemove(); - } - } - } - - // Singleton interface - static GuidManager Instance; - - // All the public API is static so you need not worry about creating an instance - public static bool Add(GuidComponent guidComponent ) - { - if (Instance == null) - { - Instance = new GuidManager(); - } - - return Instance.InternalAdd(guidComponent); - } - - public static void Remove(System.Guid guid) - { - if (Instance == null) - { - Instance = new GuidManager(); - } - - Instance.InternalRemove(guid); - } - public static GameObject ResolveGuid(System.Guid guid, Action onAddCallback, Action onRemoveCallback) - { - if (Instance == null) - { - Instance = new GuidManager(); - } - - return Instance.ResolveGuidInternal(guid, onAddCallback, onRemoveCallback); - } - - public static GameObject ResolveGuid(System.Guid guid, Action onDestroyCallback) - { - if (Instance == null) - { - Instance = new GuidManager(); - } - - return Instance.ResolveGuidInternal(guid, null, onDestroyCallback); - } - - public static GameObject ResolveGuid(System.Guid guid) - { - if (Instance == null) - { - Instance = new GuidManager(); - } - return Instance.ResolveGuidInternal(guid, null, null); - } - - // instance data - private Dictionary guidToObjectMap; - - private GuidManager() - { - guidToObjectMap = new Dictionary(); - } - - private bool InternalAdd(GuidComponent guidComponent) - { - Guid guid = guidComponent.GetGuid(); - - GuidInfo info = new GuidInfo(guidComponent); - - if (!guidToObjectMap.ContainsKey(guid)) - { - guidToObjectMap.Add(guid, info); - return true; - } - - GuidInfo existingInfo = guidToObjectMap[guid]; - if ( existingInfo.go != null && existingInfo.go != guidComponent.gameObject ) - { - // normally, a duplicate GUID is a big problem, means you won't necessarily be referencing what you expect - if (Application.isPlaying) - { - Debug.AssertFormat(false, guidComponent, "Guid Collision Detected between {0} and {1}.\nAssigning new Guid. Consider tracking runtime instances using a direct reference or other method.", (guidToObjectMap[guid].go != null ? guidToObjectMap[guid].go.name : "NULL"), (guidComponent != null ? guidComponent.name : "NULL")); - } - else - { - // however, at editor time, copying an object with a GUID will duplicate the GUID resulting in a collision and repair. - // we warn about this just for pedantry reasons, and so you can detect if you are unexpectedly copying these components - Debug.LogWarningFormat(guidComponent, "Guid Collision Detected while creating {0}.\nAssigning new Guid.", (guidComponent != null ? guidComponent.name : "NULL")); - } - return false; - } - - // if we already tried to find this GUID, but haven't set the game object to anything specific, copy any OnAdd callbacks then call them - existingInfo.go = info.go; - existingInfo.HandleAddCallback(); - guidToObjectMap[guid] = existingInfo; - return true; - } - - private void InternalRemove(System.Guid guid) - { - GuidInfo info; - if (guidToObjectMap.TryGetValue(guid, out info)) - { - // trigger all the destroy delegates that have registered - info.HandleRemoveCallback(); - } - - guidToObjectMap.Remove(guid); - } - - // nice easy api to find a GUID, and if it works, register an on destroy callback - // this should be used to register functions to cleanup any data you cache on finding - // your target. Otherwise, you might keep components in memory by referencing them - private GameObject ResolveGuidInternal(System.Guid guid, Action onAddCallback, Action onRemoveCallback) - { - GuidInfo info; - if (guidToObjectMap.TryGetValue(guid, out info)) - { - if (onAddCallback != null) - { - info.OnAdd += onAddCallback; - } - - if (onRemoveCallback != null) - { - info.OnRemove += onRemoveCallback; - } - guidToObjectMap[guid] = info; - return info.go; - } - - if (onAddCallback != null) - { - info.OnAdd += onAddCallback; - } - - if (onRemoveCallback != null) - { - info.OnRemove += onRemoveCallback; - } - - guidToObjectMap.Add(guid, info); - - return null; - } -} diff --git a/Assets/CrossSceneReference/Samples/LoadScenes.cs b/Assets/CrossSceneReference/Samples/LoadScenes.cs deleted file mode 100644 index 1328d29..0000000 --- a/Assets/CrossSceneReference/Samples/LoadScenes.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; -using UnityEngine.SceneManagement; - -public class LoadScenes : MonoBehaviour -{ - [System.Serializable] - public struct SceneInfo - { - public string name; - public bool shouldLoad; - } - - public List scenes = new List(); - - // Update is called once per frame - void Update () - { - foreach( var info in scenes ) - { - Scene scene = SceneManager.GetSceneByName(info.name); - - if (info.shouldLoad && !scene.isLoaded) - { - SceneManager.LoadScene(info.name, LoadSceneMode.Additive); - } - - if (!info.shouldLoad && scene.isLoaded) - { - SceneManager.UnloadSceneAsync(scene); - } - } - } - - // a little User Interface - private void OnGUI() - { - // this code is a great example of how NOT to write Unity code - - Vector2 padding = new Vector2(5.0f, 5.0f); - Vector2 buttonSize = new Vector2(150.0f, 50.0f); - Rect buttonPosition = new Rect(padding, buttonSize); - - for ( int i = 0; i < scenes.Count; ++i ) - { - // PER FRAME ALLOCATION HERE - string buttonText = scenes[i].name; - buttonText += scenes[i].shouldLoad ? " : Unload" : " : Load"; - - if (GUI.Button(buttonPosition, buttonText)) - { - // UGH! Lists mean I can't modify scenes[i].shouldLoad directly! I guess make an awkward copy and then set =/ - SceneInfo newInfo = scenes[i]; - newInfo.shouldLoad = !newInfo.shouldLoad; - scenes[i] = newInfo; - } - - buttonPosition.y += buttonSize.y + padding.y; - } - } -} diff --git a/Assets/CrossSceneReference/Samples/TestCrossScene.cs b/Assets/CrossSceneReference/Samples/TestCrossScene.cs deleted file mode 100644 index 2189386..0000000 --- a/Assets/CrossSceneReference/Samples/TestCrossScene.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; -using UnityEngine.Profiling; - -public class TestCrossScene : MonoBehaviour -{ - - public GuidReference crossSceneReference = new GuidReference(); - private Renderer cachedRenderer; - - void Awake() - { - // set up a callback when the target is destroyed so we can remove references to the destroyed object - crossSceneReference.OnGuidRemoved += ClearCache; - } - - void Update () - { - // simple example looking for our reference and spinning both if we get one. - // due to caching, this only causes a dictionary lookup the first time we call it, so you can comfortably poll. - if (crossSceneReference.gameObject != null) - { - transform.Rotate(new Vector3(0, 1, 0), 10.0f * Time.deltaTime); - - if (cachedRenderer == null) - { - cachedRenderer = crossSceneReference.gameObject.GetComponent(); - } - - if (cachedRenderer != null) - { - cachedRenderer.gameObject.transform.Rotate(new Vector3(0, 1, 0), 10.0f * Time.deltaTime, Space.World); - } - - } - - // added a performance test if you want to see. Most cost is in the profiling tags. - //TestPerformance(); - } - - void ClearCache() - { - cachedRenderer = null; - } - - void TestPerformance() - { - GameObject derefTest = null; - - for (int i = 0; i < 10000; ++i) - { - Profiler.BeginSample("Guid Resolution"); - derefTest = crossSceneReference.gameObject; - Profiler.EndSample(); - } - - } - -} diff --git a/Assets/CrossSceneReference/Tests/Editor/GuidReferenceTests.cs b/Assets/CrossSceneReference/Tests/Editor/GuidReferenceTests.cs deleted file mode 100644 index 1ae2c4e..0000000 --- a/Assets/CrossSceneReference/Tests/Editor/GuidReferenceTests.cs +++ /dev/null @@ -1,107 +0,0 @@ -using UnityEngine; -using UnityEngine.TestTools; -using NUnit.Framework; -using System.Collections; -using System.Collections.Generic; -using UnityEditor; - -public class GuidReferenceTests -{ - // Tests - make a new GUID - // duplicate it - // make it a prefab - // delete it - // reference it - // dereference it - - string prefabPath; - GuidComponent guidBase; - GameObject prefab; - GuidComponent guidPrefab; - - [OneTimeSetUp] - public void Setup() - { - prefabPath = "Assets/TemporaryTestGuid.prefab"; - - guidBase = CreateNewGuid(); - prefab = PrefabUtility.CreatePrefab(prefabPath, guidBase.gameObject); - - guidPrefab = prefab.GetComponent(); - } - - public GuidComponent CreateNewGuid() - { - GameObject newGO = new GameObject("GuidTestGO"); - return newGO.AddComponent(); - } - - [UnityTest] - public IEnumerator GuidCreation() - { - GuidComponent guid1 = guidBase; - GuidComponent guid2 = CreateNewGuid(); - - Assert.AreNotEqual(guid1.GetGuid(), guid2.GetGuid()); - - yield return null; - } - - [UnityTest] - public IEnumerator GuidDuplication() - { - LogAssert.Expect(LogType.Warning, "Guid Collision Detected while creating GuidTestGO(Clone).\nAssigning new Guid."); - - GuidComponent clone = GameObject.Instantiate(guidBase); - - Assert.AreNotEqual(guidBase.GetGuid(), clone.GetGuid()); - - yield return null; - } - - [UnityTest] - public IEnumerator GuidPrefab() - { - Assert.AreNotEqual(guidBase.GetGuid(), guidPrefab.GetGuid()); - Assert.AreEqual(guidPrefab.GetGuid(), System.Guid.Empty); - - yield return null; - } - - [UnityTest] - public IEnumerator GuidPrefabInstance() - { - GuidComponent instance = GameObject.Instantiate(guidPrefab); - Assert.AreNotEqual(guidBase.GetGuid(), instance.GetGuid()); - Assert.AreNotEqual(instance.GetGuid(), guidPrefab.GetGuid()); - - yield return null; - } - - [UnityTest] - public IEnumerator GuidValidReference() - { - GuidReference reference = new GuidReference(guidBase); - Assert.AreEqual(reference.gameObject, guidBase.gameObject); - - yield return null; - } - - [UnityTest] - public IEnumerator GuidInvalidReference() - { - GuidComponent newGuid = CreateNewGuid(); - GuidReference reference = new GuidReference(newGuid); - Object.DestroyImmediate(newGuid); - - Assert.IsNull(reference.gameObject); - - yield return null; - } - - [OneTimeTearDown] - public void TearDown() - { - AssetDatabase.DeleteAsset(prefabPath); - } -} diff --git a/Documentation~/guid-based-reference.md b/Documentation~/guid-based-reference.md new file mode 100644 index 0000000..4c7aa67 --- /dev/null +++ b/Documentation~/guid-based-reference.md @@ -0,0 +1,19 @@ +## Guid Based Reference +License - Please see [LICENSE.md](LICENSE.md) in this repository + +### Summary +A component to give a Globaly Unique IDentifier (GUID) to a Game Object. This GUID can then be used to reference an +object even if it is another Scene, not loaded yet, or otherwise not easy to directly reference. + +### Maintainers +William Armstrong williama@unity3d.com + +### How to use +1. Add a GuidComponent to any object you want to be able to reference. +2. In any code that needs to be able to reference objects by GUID, add a GuidReference field. +3. GuidReference.gameObject will then return the GameObject if it is loaded, otherwise null. + +### Sample +Look in the Samples folder for example usage. Load up the LoadFirst scene, and then use the SceneLoader object to load ' +LoadSecond'. +You should see the CrossSceneReferencer object find the CrossSceneTarget object, and set both of them to start spinning. diff --git a/Assets/CrossSceneReference/Editor.meta b/Editor.meta similarity index 100% rename from Assets/CrossSceneReference/Editor.meta rename to Editor.meta diff --git a/Editor/GuidComponentDrawer.cs b/Editor/GuidComponentDrawer.cs new file mode 100644 index 0000000..9cdb3dd --- /dev/null +++ b/Editor/GuidComponentDrawer.cs @@ -0,0 +1,17 @@ +using UnityEditor; + +namespace UnityEngine.GUID { + [CustomEditor(typeof(GuidComponent))] + public class GuidComponentDrawer : Editor { + private GuidComponent guidComp; + + public override void OnInspectorGUI () { + if (guidComp == null) { + guidComp = (GuidComponent) target; + } + + // Draw label + EditorGUILayout.LabelField("Guid:", guidComp.GetGuid().ToString()); + } + } +} diff --git a/Assets/CrossSceneReference/Editor/GuidComponentDrawer.cs.meta b/Editor/GuidComponentDrawer.cs.meta similarity index 100% rename from Assets/CrossSceneReference/Editor/GuidComponentDrawer.cs.meta rename to Editor/GuidComponentDrawer.cs.meta diff --git a/Editor/GuidReferenceDrawer.cs b/Editor/GuidReferenceDrawer.cs new file mode 100644 index 0000000..f9a34da --- /dev/null +++ b/Editor/GuidReferenceDrawer.cs @@ -0,0 +1,121 @@ +using UnityEditor; + +// Using a property drawer to allow any class to have a field of type GuidRefernce and still get good UX +// If you are writing your own inspector for a class that uses a GuidReference, drawing it with +// EditorLayout.PropertyField(prop) or similar will get this to show up automatically + +namespace UnityEngine.GUID { + [CustomPropertyDrawer(typeof(GuidReference))] + public class GuidReferenceDrawer : PropertyDrawer { + SerializedProperty guidProp; + SerializedProperty sceneProp; + SerializedProperty nameProp; + + // cache off GUI content to avoid creating garbage every frame in editor + GUIContent sceneLabel = new GUIContent("Containing Scene", "The target object is expected in this scene asset."); + GUIContent clearButtonGUI = new GUIContent("Clear", "Remove Cross Scene Reference"); + + // add an extra line to display source scene for targets + public override float GetPropertyHeight (SerializedProperty property, GUIContent label) { + return base.GetPropertyHeight(property, label) + EditorGUIUtility.singleLineHeight; + } + + public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { + guidProp = property.FindPropertyRelative("serializedGuid"); + nameProp = property.FindPropertyRelative("cachedName"); + sceneProp = property.FindPropertyRelative("cachedScene"); + + // Using BeginProperty / EndProperty on the parent property means that + // prefab override logic works on the entire property. + EditorGUI.BeginProperty(position, label, property); + + position.height = EditorGUIUtility.singleLineHeight; + + // Draw prefix label, returning the new rect we can draw in + var guidCompPosition = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); + + System.Guid currentGuid; + GameObject currentGO = null; + + // working with array properties is a bit unwieldy + // you have to get the property at each index manually + byte[] byteArray = new byte[16]; + int arraySize = guidProp.arraySize; + for (int i = 0; i < arraySize; ++i) { + var byteProp = guidProp.GetArrayElementAtIndex(i); + byteArray[i] = (byte) byteProp.intValue; + } + + currentGuid = new System.Guid(byteArray); + currentGO = GuidManager.ResolveGuid(currentGuid); + GuidComponent currentGuidComponent = currentGO != null ? currentGO.GetComponent() : null; + + GuidComponent component = null; + + if (currentGuid != System.Guid.Empty && currentGuidComponent == null) { + // if our reference is set, but the target isn't loaded, we display the target and the scene it is in, and provide a way to clear the reference + float buttonWidth = 55.0f; + + guidCompPosition.xMax -= buttonWidth; + + bool guiEnabled = GUI.enabled; + GUI.enabled = false; + EditorGUI.LabelField(guidCompPosition, new GUIContent(nameProp.stringValue, "Target GameObject is not currently loaded."), EditorStyles.objectField); + GUI.enabled = guiEnabled; + + Rect clearButtonRect = new Rect(guidCompPosition); + clearButtonRect.xMin = guidCompPosition.xMax; + clearButtonRect.xMax += buttonWidth; + + if (GUI.Button(clearButtonRect, clearButtonGUI, EditorStyles.miniButton)) { + ClearPreviousGuid(); + } + } else { + // if our object is loaded, we can simply use an object field directly + component = EditorGUI.ObjectField(guidCompPosition, currentGuidComponent, typeof(GuidComponent), true) as GuidComponent; + } + + if (currentGuidComponent != null && component == null) { + ClearPreviousGuid(); + } + + // if we have a valid reference, draw the scene name of the scene it lives in so users can find it + if (component != null) { + nameProp.stringValue = component.name; + string scenePath = component.gameObject.scene.path; + sceneProp.objectReferenceValue = AssetDatabase.LoadAssetAtPath(scenePath); + + // only update the GUID Prop if something changed. This fixes multi-edit on GUID References + if (component != currentGuidComponent) { + byteArray = component.GetGuid().ToByteArray(); + arraySize = guidProp.arraySize; + for (int i = 0; i < arraySize; ++i) { + var byteProp = guidProp.GetArrayElementAtIndex(i); + byteProp.intValue = byteArray[i]; + } + } + } + + EditorGUI.indentLevel++; + position.y += EditorGUIUtility.singleLineHeight; + bool cachedGUIState = GUI.enabled; + GUI.enabled = false; + EditorGUI.ObjectField(position, sceneLabel, sceneProp.objectReferenceValue, typeof(SceneAsset), false); + GUI.enabled = cachedGUIState; + EditorGUI.indentLevel--; + + EditorGUI.EndProperty(); + } + + void ClearPreviousGuid () { + nameProp.stringValue = string.Empty; + sceneProp.objectReferenceValue = null; + + int arraySize = guidProp.arraySize; + for (int i = 0; i < arraySize; ++i) { + var byteProp = guidProp.GetArrayElementAtIndex(i); + byteProp.intValue = 0; + } + } + } +} diff --git a/Assets/CrossSceneReference/Editor/GuidReferenceDrawer.cs.meta b/Editor/GuidReferenceDrawer.cs.meta similarity index 100% rename from Assets/CrossSceneReference/Editor/GuidReferenceDrawer.cs.meta rename to Editor/GuidReferenceDrawer.cs.meta diff --git a/Editor/com.unity.guid.Editor.asmdef b/Editor/com.unity.guid.Editor.asmdef new file mode 100644 index 0000000..32dd54e --- /dev/null +++ b/Editor/com.unity.guid.Editor.asmdef @@ -0,0 +1,18 @@ +{ + "name": "com.unity.guid.editor", + "rootNamespace": "", + "references": [ + "GUID" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Editor/com.unity.guid.Editor.asmdef.meta b/Editor/com.unity.guid.Editor.asmdef.meta new file mode 100644 index 0000000..8286e13 --- /dev/null +++ b/Editor/com.unity.guid.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e3dfa1511d7f25d4f8ba04d918bd49f2 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CrossSceneReference/Runtime.meta b/Runtime.meta similarity index 100% rename from Assets/CrossSceneReference/Runtime.meta rename to Runtime.meta diff --git a/Runtime/GuidComponent.cs b/Runtime/GuidComponent.cs new file mode 100644 index 0000000..0a3255a --- /dev/null +++ b/Runtime/GuidComponent.cs @@ -0,0 +1,145 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEditor.SceneManagement; +#endif + +// This component gives a GameObject a stable, non-replicatable Globally Unique IDentifier. +// It can be used to reference a specific instance of an object no matter where it is. +// This can also be used for other systems, such as Save/Load game + +namespace UnityEngine.GUID { + [ExecuteInEditMode, DisallowMultipleComponent] + public class GuidComponent : MonoBehaviour, ISerializationCallbackReceiver { + // System guid we use for comparison and generation + System.Guid guid = System.Guid.Empty; + + // Unity's serialization system doesn't know about System.Guid, so we convert to a byte array + // Fun fact, we tried using strings at first, but that allocated memory and was twice as slow + [SerializeField] + private byte[] serializedGuid; + + public bool IsGuidAssigned () { + return guid != System.Guid.Empty; + } + + // When de-serializing or creating this component, we want to either restore our serialized GUID + // or create a new one. + void CreateGuid () { + // if our serialized data is invalid, then we are a new object and need a new GUID + if (serializedGuid == null || serializedGuid.Length != 16) { +#if UNITY_EDITOR + // if in editor, make sure we aren't a prefab of some kind + if (IsAssetOnDisk()) { + return; + } + + Undo.RecordObject(this, "Added GUID"); +#endif + guid = System.Guid.NewGuid(); + serializedGuid = guid.ToByteArray(); + +#if UNITY_EDITOR + // If we are creating a new GUID for a prefab instance of a prefab, but we have somehow lost our prefab connection + // force a save of the modified prefab instance properties + if (PrefabUtility.IsPartOfNonAssetPrefabInstance(this)) { + PrefabUtility.RecordPrefabInstancePropertyModifications(this); + } +#endif + } else if (guid == System.Guid.Empty) { + // otherwise, we should set our system guid to our serialized guid + guid = new System.Guid(serializedGuid); + } + + // register with the GUID Manager so that other components can access this + if (guid != System.Guid.Empty) { + if (!GuidManager.Add(this)) { + // if registration fails, we probably have a duplicate or invalid GUID, get us a new one. + serializedGuid = null; + guid = System.Guid.Empty; + CreateGuid(); + } + } + } + +#if UNITY_EDITOR + private bool IsEditingInPrefabMode () { + if (EditorUtility.IsPersistent(this)) { + // if the game object is stored on disk, it is a prefab of some kind, despite not returning true for IsPartOfPrefabAsset =/ + return true; + } else { + // If the GameObject is not persistent let's determine which stage we are in first because getting Prefab info depends on it + var mainStage = StageUtility.GetMainStageHandle(); + var currentStage = StageUtility.GetStageHandle(gameObject); + if (currentStage != mainStage) { + var prefabStage = PrefabStageUtility.GetPrefabStage(gameObject); + if (prefabStage != null) { + return true; + } + } + } + + return false; + } + + private bool IsAssetOnDisk () { + return PrefabUtility.IsPartOfPrefabAsset(this) || IsEditingInPrefabMode(); + } +#endif + + // We cannot allow a GUID to be saved into a prefab, and we need to convert to byte[] + public void OnBeforeSerialize () { +#if UNITY_EDITOR + // This lets us detect if we are a prefab instance or a prefab asset. + // A prefab asset cannot contain a GUID since it would then be duplicated when instanced. + if (IsAssetOnDisk()) { + serializedGuid = null; + guid = System.Guid.Empty; + } else +#endif + { + if (guid != System.Guid.Empty) { + serializedGuid = guid.ToByteArray(); + } + } + } + + // On load, we can go head a restore our system guid for later use + public void OnAfterDeserialize () { + if (serializedGuid != null && serializedGuid.Length == 16) { + guid = new System.Guid(serializedGuid); + } + } + + void Awake () { + CreateGuid(); + } + + void OnValidate () { +#if UNITY_EDITOR + // similar to on Serialize, but gets called on Copying a Component or Applying a Prefab + // at a time that lets us detect what we are + if (IsAssetOnDisk()) { + serializedGuid = null; + guid = System.Guid.Empty; + } else +#endif + { + CreateGuid(); + } + } + + // Never return an invalid GUID + public System.Guid GetGuid () { + if (guid == System.Guid.Empty && serializedGuid != null && serializedGuid.Length == 16) { + guid = new System.Guid(serializedGuid); + } + + return guid; + } + + // let the manager know we are gone, so other objects no longer find this + public void OnDestroy () { + GuidManager.Remove(guid); + } + } +} diff --git a/Assets/CrossSceneReference/Runtime/GuidComponent.cs.meta b/Runtime/GuidComponent.cs.meta similarity index 100% rename from Assets/CrossSceneReference/Runtime/GuidComponent.cs.meta rename to Runtime/GuidComponent.cs.meta diff --git a/Runtime/GuidManager.cs b/Runtime/GuidManager.cs new file mode 100644 index 0000000..f91dd29 --- /dev/null +++ b/Runtime/GuidManager.cs @@ -0,0 +1,160 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using System; + +// Class to handle registering and accessing objects by GUID + +namespace UnityEngine.GUID { + public class GuidManager { + // for each GUID we need to know the Game Object it references + // and an event to store all the callbacks that need to know when it is destroyed + private struct GuidInfo { + public GameObject go; + + public event Action OnAdd; + public event Action OnRemove; + + public GuidInfo (GuidComponent comp) { + go = comp.gameObject; + OnRemove = null; + OnAdd = null; + } + + public void HandleAddCallback () { + if (OnAdd != null) { + OnAdd(go); + } + } + + public void HandleRemoveCallback () { + if (OnRemove != null) { + OnRemove(); + } + } + } + + // Singleton interface + static GuidManager Instance; + + // All the public API is static so you need not worry about creating an instance + public static bool Add (GuidComponent guidComponent) { + if (Instance == null) { + Instance = new GuidManager(); + } + + return Instance.InternalAdd(guidComponent); + } + + public static void Remove (System.Guid guid) { + if (Instance == null) { + Instance = new GuidManager(); + } + + Instance.InternalRemove(guid); + } + + public static GameObject ResolveGuid (System.Guid guid, Action onAddCallback, Action onRemoveCallback) { + if (Instance == null) { + Instance = new GuidManager(); + } + + return Instance.ResolveGuidInternal(guid, onAddCallback, onRemoveCallback); + } + + public static GameObject ResolveGuid (System.Guid guid, Action onDestroyCallback) { + if (Instance == null) { + Instance = new GuidManager(); + } + + return Instance.ResolveGuidInternal(guid, null, onDestroyCallback); + } + + public static GameObject ResolveGuid (System.Guid guid) { + if (Instance == null) { + Instance = new GuidManager(); + } + + return Instance.ResolveGuidInternal(guid, null, null); + } + + // instance data + private Dictionary guidToObjectMap; + + private GuidManager () { + guidToObjectMap = new Dictionary(); + } + + private bool InternalAdd (GuidComponent guidComponent) { + Guid guid = guidComponent.GetGuid(); + + GuidInfo info = new GuidInfo(guidComponent); + + if (!guidToObjectMap.ContainsKey(guid)) { + guidToObjectMap.Add(guid, info); + return true; + } + + GuidInfo existingInfo = guidToObjectMap[guid]; + if (existingInfo.go != null && existingInfo.go != guidComponent.gameObject) { + // normally, a duplicate GUID is a big problem, means you won't necessarily be referencing what you expect + if (Application.isPlaying) { + Debug.AssertFormat(false, guidComponent, "Guid Collision Detected between {0} and {1}.\nAssigning new Guid. Consider tracking runtime instances using a direct reference or other method.", (guidToObjectMap[guid].go != null ? guidToObjectMap[guid].go.name : "NULL"), (guidComponent != null ? guidComponent.name : "NULL")); + } else { + // however, at editor time, copying an object with a GUID will duplicate the GUID resulting in a collision and repair. + // we warn about this just for pedantry reasons, and so you can detect if you are unexpectedly copying these components + Debug.LogWarningFormat(guidComponent, "Guid Collision Detected while creating {0}.\nAssigning new Guid.", (guidComponent != null ? guidComponent.name : "NULL")); + } + + return false; + } + + // if we already tried to find this GUID, but haven't set the game object to anything specific, copy any OnAdd callbacks then call them + existingInfo.go = info.go; + existingInfo.HandleAddCallback(); + guidToObjectMap[guid] = existingInfo; + return true; + } + + private void InternalRemove (System.Guid guid) { + GuidInfo info; + if (guidToObjectMap.TryGetValue(guid, out info)) { + // trigger all the destroy delegates that have registered + info.HandleRemoveCallback(); + } + + guidToObjectMap.Remove(guid); + } + + // nice easy api to find a GUID, and if it works, register an on destroy callback + // this should be used to register functions to cleanup any data you cache on finding + // your target. Otherwise, you might keep components in memory by referencing them + private GameObject ResolveGuidInternal (System.Guid guid, Action onAddCallback, Action onRemoveCallback) { + GuidInfo info; + if (guidToObjectMap.TryGetValue(guid, out info)) { + if (onAddCallback != null) { + info.OnAdd += onAddCallback; + } + + if (onRemoveCallback != null) { + info.OnRemove += onRemoveCallback; + } + + guidToObjectMap[guid] = info; + return info.go; + } + + if (onAddCallback != null) { + info.OnAdd += onAddCallback; + } + + if (onRemoveCallback != null) { + info.OnRemove += onRemoveCallback; + } + + guidToObjectMap.Add(guid, info); + + return null; + } + } +} diff --git a/Assets/CrossSceneReference/Runtime/GuidManager.cs.meta b/Runtime/GuidManager.cs.meta similarity index 100% rename from Assets/CrossSceneReference/Runtime/GuidManager.cs.meta rename to Runtime/GuidManager.cs.meta diff --git a/Assets/CrossSceneReference/Runtime/GuidReference.cs b/Runtime/GuidReference.cs similarity index 55% rename from Assets/CrossSceneReference/Runtime/GuidReference.cs rename to Runtime/GuidReference.cs index 41df019..d63cd10 100644 --- a/Assets/CrossSceneReference/Runtime/GuidReference.cs +++ b/Runtime/GuidReference.cs @@ -1,6 +1,4 @@ -using UnityEngine; -using System; - +using System; #if UNITY_EDITOR using UnityEditor; #endif @@ -11,16 +9,18 @@ // or other methods to track down the specific objects need by any given system // Ideally this would be a struct, but we need the ISerializationCallbackReciever -[System.Serializable] -public class GuidReference : ISerializationCallbackReceiver -{ + +namespace UnityEngine.GUID { + [System.Serializable] + public class GuidReference : ISerializationCallbackReceiver { // cache the referenced Game Object if we find one for performance private GameObject cachedReference; private bool isCacheSet; - + // store our GUID in a form that Unity can save [SerializeField] private byte[] serializedGuid; + private System.Guid guid; #if UNITY_EDITOR @@ -33,69 +33,60 @@ public class GuidReference : ISerializationCallbackReceiver // Set up events to let users register to cleanup their own cached references on destroy or to cache off values public event Action OnGuidAdded = delegate (GameObject go) { }; - public event Action OnGuidRemoved = delegate() { }; + public event Action OnGuidRemoved = delegate () { }; // create concrete delegates to avoid boxing. // When called 10,000 times, boxing would allocate ~1MB of GC Memory private Action addDelegate; private Action removeDelegate; - + // optimized accessor, and ideally the only code you ever call on this class - public GameObject gameObject - { - get - { - if( isCacheSet ) - { - return cachedReference; - } - - cachedReference = GuidManager.ResolveGuid( guid, addDelegate, removeDelegate ); - isCacheSet = true; - return cachedReference; + public GameObject gameObject { + get { + if (isCacheSet) { + return cachedReference; } - private set {} + cachedReference = GuidManager.ResolveGuid(guid, addDelegate, removeDelegate); + isCacheSet = true; + return cachedReference; + } + private set { } } - public GuidReference() { } + public GuidReference () { } - public GuidReference(GuidComponent target) - { - guid = target.GetGuid(); + public GuidReference (GuidComponent target) { + guid = target.GetGuid(); } - private void GuidAdded(GameObject go) - { - cachedReference = go; - OnGuidAdded(go); + private void GuidAdded (GameObject go) { + cachedReference = go; + OnGuidAdded(go); } - private void GuidRemoved() - { - cachedReference = null; - isCacheSet = false; - OnGuidRemoved(); + private void GuidRemoved () { + cachedReference = null; + isCacheSet = false; + OnGuidRemoved(); } //convert system guid to a format unity likes to work with - public void OnBeforeSerialize() - { - serializedGuid = guid.ToByteArray(); + public void OnBeforeSerialize () { + serializedGuid = guid.ToByteArray(); } // convert from byte array to system guid and reset state - public void OnAfterDeserialize() - { - cachedReference = null; - isCacheSet = false; - if (serializedGuid == null || serializedGuid.Length != 16) - { - serializedGuid = new byte[16]; - } - guid = new System.Guid(serializedGuid); - addDelegate = GuidAdded; - removeDelegate = GuidRemoved; + public void OnAfterDeserialize () { + cachedReference = null; + isCacheSet = false; + if (serializedGuid == null || serializedGuid.Length != 16) { + serializedGuid = new byte[16]; + } + guid = new System.Guid(serializedGuid); + addDelegate = GuidAdded; + removeDelegate = GuidRemoved; } + } } diff --git a/Assets/CrossSceneReference/Runtime/GuidReference.cs.meta b/Runtime/GuidReference.cs.meta similarity index 100% rename from Assets/CrossSceneReference/Runtime/GuidReference.cs.meta rename to Runtime/GuidReference.cs.meta diff --git a/Runtime/com.unity.guid.asmdef b/Runtime/com.unity.guid.asmdef new file mode 100644 index 0000000..ee74b1a --- /dev/null +++ b/Runtime/com.unity.guid.asmdef @@ -0,0 +1,14 @@ +{ + "name": "GUID", + "rootNamespace": "", + "references": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Runtime/com.unity.guid.asmdef.meta b/Runtime/com.unity.guid.asmdef.meta new file mode 100644 index 0000000..4d68bfd --- /dev/null +++ b/Runtime/com.unity.guid.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: bed044abed94ba24e99e357597663950 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CrossSceneReference/Samples.meta b/Samples~.meta similarity index 100% rename from Assets/CrossSceneReference/Samples.meta rename to Samples~.meta diff --git a/Assets/CrossSceneReference/Samples/LoadFirst.unity b/Samples~/LoadFirst.unity similarity index 100% rename from Assets/CrossSceneReference/Samples/LoadFirst.unity rename to Samples~/LoadFirst.unity diff --git a/Assets/CrossSceneReference/Samples/LoadFirst.unity.meta b/Samples~/LoadFirst.unity.meta similarity index 80% rename from Assets/CrossSceneReference/Samples/LoadFirst.unity.meta rename to Samples~/LoadFirst.unity.meta index 60f8681..7958081 100644 --- a/Assets/CrossSceneReference/Samples/LoadFirst.unity.meta +++ b/Samples~/LoadFirst.unity.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 6f8ba3c4c3bf19d4999033b50832272b +guid: dc5486f439f4c20489bba7901c90ceee timeCreated: 1524091778 licenseType: Pro DefaultImporter: diff --git a/Samples~/LoadScenes.cs b/Samples~/LoadScenes.cs new file mode 100644 index 0000000..a2f25dc --- /dev/null +++ b/Samples~/LoadScenes.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using UnityEngine.SceneManagement; + +namespace UnityEngine.GUID { + public class LoadScenes : MonoBehaviour { + [System.Serializable] + public struct SceneInfo { + public string name; + public bool shouldLoad; + } + + public List scenes = new List(); + + void Update () { + foreach (var info in scenes) { + Scene scene = SceneManager.GetSceneByName(info.name); + + if (info.shouldLoad && !scene.isLoaded) { + SceneManager.LoadScene(info.name, LoadSceneMode.Additive); + } + + if (!info.shouldLoad && scene.isLoaded) { + SceneManager.UnloadSceneAsync(scene); + } + } + } + + // a little User Interface + private void OnGUI () { + // this code is a great example of how NOT to write Unity code + + Vector2 padding = new Vector2(5.0f, 5.0f); + Vector2 buttonSize = new Vector2(150.0f, 50.0f); + Rect buttonPosition = new Rect(padding, buttonSize); + + for (int i = 0; i < scenes.Count; ++i) { + // PER FRAME ALLOCATION HERE + string buttonText = scenes[i].name; + buttonText += scenes[i].shouldLoad ? " : Unload" : " : Load"; + + if (GUI.Button(buttonPosition, buttonText)) { + // UGH! Lists mean I can't modify scenes[i].shouldLoad directly! I guess make an awkward copy and then set =/ + SceneInfo newInfo = scenes[i]; + newInfo.shouldLoad = !newInfo.shouldLoad; + scenes[i] = newInfo; + } + + buttonPosition.y += buttonSize.y + padding.y; + } + } + } +} diff --git a/Assets/CrossSceneReference/Samples/LoadScenes.cs.meta b/Samples~/LoadScenes.cs.meta similarity index 86% rename from Assets/CrossSceneReference/Samples/LoadScenes.cs.meta rename to Samples~/LoadScenes.cs.meta index c58d837..196d7f5 100644 --- a/Assets/CrossSceneReference/Samples/LoadScenes.cs.meta +++ b/Samples~/LoadScenes.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f1c3a067d9be6204c9e067023c1c768b +guid: f34a8c30583d15e458a12e4388ff1552 timeCreated: 1524092328 licenseType: Pro MonoImporter: diff --git a/Assets/CrossSceneReference/Samples/LoadSecond.unity b/Samples~/LoadSecond.unity similarity index 100% rename from Assets/CrossSceneReference/Samples/LoadSecond.unity rename to Samples~/LoadSecond.unity diff --git a/Assets/CrossSceneReference/Samples/LoadSecond.unity.meta b/Samples~/LoadSecond.unity.meta similarity index 80% rename from Assets/CrossSceneReference/Samples/LoadSecond.unity.meta rename to Samples~/LoadSecond.unity.meta index 6f258b9..5d7691d 100644 --- a/Assets/CrossSceneReference/Samples/LoadSecond.unity.meta +++ b/Samples~/LoadSecond.unity.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8acfd1b1a375f2049a2dde40d955ce3f +guid: 44b3b16f4b544704d926fd5d5f9b9223 timeCreated: 1524091841 licenseType: Pro DefaultImporter: diff --git a/Samples~/TestCrossScene.cs b/Samples~/TestCrossScene.cs new file mode 100644 index 0000000..82c7443 --- /dev/null +++ b/Samples~/TestCrossScene.cs @@ -0,0 +1,46 @@ +using UnityEngine.Profiling; + +namespace UnityEngine.GUID { + public class TestCrossScene : MonoBehaviour { + public GuidReference crossSceneReference = new GuidReference(); + private Renderer cachedRenderer; + + void Awake () { + // set up a callback when the target is destroyed so we can remove references to the destroyed object + crossSceneReference.OnGuidRemoved += ClearCache; + } + + void Update () { + // simple example looking for our reference and spinning both if we get one. + // due to caching, this only causes a dictionary lookup the first time we call it, so you can comfortably poll. + if (crossSceneReference.gameObject != null) { + transform.Rotate(new Vector3(0, 1, 0), 10.0f * Time.deltaTime); + + if (cachedRenderer == null) { + cachedRenderer = crossSceneReference.gameObject.GetComponent(); + } + + if (cachedRenderer != null) { + cachedRenderer.gameObject.transform.Rotate(new Vector3(0, 1, 0), 10.0f * Time.deltaTime, Space.World); + } + } + + // added a performance test if you want to see. Most cost is in the profiling tags. + //TestPerformance(); + } + + void ClearCache () { + cachedRenderer = null; + } + + void TestPerformance () { + GameObject derefTest = null; + + for (int i = 0; i < 10000; ++i) { + Profiler.BeginSample("Guid Resolution"); + derefTest = crossSceneReference.gameObject; + Profiler.EndSample(); + } + } + } +} diff --git a/Assets/CrossSceneReference/Samples/TestCrossScene.cs.meta b/Samples~/TestCrossScene.cs.meta similarity index 83% rename from Assets/CrossSceneReference/Samples/TestCrossScene.cs.meta rename to Samples~/TestCrossScene.cs.meta index 80b87b5..ad6da11 100644 --- a/Assets/CrossSceneReference/Samples/TestCrossScene.cs.meta +++ b/Samples~/TestCrossScene.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: dac07e8eb76e66a4fa8c4fe45f186fc4 +guid: 9d1d72110525b5441bd3f57fda5df9b4 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Samples~/com.unity.guid.samples.asmdef b/Samples~/com.unity.guid.samples.asmdef new file mode 100644 index 0000000..dede599 --- /dev/null +++ b/Samples~/com.unity.guid.samples.asmdef @@ -0,0 +1,16 @@ +{ + "name": "com.unity.guid.samples", + "rootNamespace": "", + "references": [ + "GUID" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Samples~/com.unity.guid.samples.asmdef.meta b/Samples~/com.unity.guid.samples.asmdef.meta new file mode 100644 index 0000000..095ce2a --- /dev/null +++ b/Samples~/com.unity.guid.samples.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 58b9bfc1be0305948a56609d27425d0c +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CrossSceneReference/Tests.meta b/Tests.meta similarity index 100% rename from Assets/CrossSceneReference/Tests.meta rename to Tests.meta diff --git a/Assets/CrossSceneReference/Tests/Editor.meta b/Tests/Runtime.meta similarity index 100% rename from Assets/CrossSceneReference/Tests/Editor.meta rename to Tests/Runtime.meta diff --git a/Tests/Runtime/GuidReferenceTests.cs b/Tests/Runtime/GuidReferenceTests.cs new file mode 100644 index 0000000..fd99bd7 --- /dev/null +++ b/Tests/Runtime/GuidReferenceTests.cs @@ -0,0 +1,99 @@ +using UnityEngine; +using UnityEngine.TestTools; +using NUnit.Framework; +using System.Collections; +using System.Collections.Generic; +using UnityEditor; + +namespace UnityEngine.GUID { + public class GuidReferenceTests { + // Tests - make a new GUID + // duplicate it + // make it a prefab + // delete it + // reference it + // dereference it + + string prefabPath; + GuidComponent guidBase; + GameObject prefab; + GuidComponent guidPrefab; + + [OneTimeSetUp] + public void Setup () { + prefabPath = "Assets/TemporaryTestGuid.prefab"; + + guidBase = CreateNewGuid(); + prefab = PrefabUtility.CreatePrefab(prefabPath, guidBase.gameObject); + + guidPrefab = prefab.GetComponent(); + } + + public GuidComponent CreateNewGuid () { + GameObject newGO = new GameObject("GuidTestGO"); + return newGO.AddComponent(); + } + + [UnityTest] + public IEnumerator GuidCreation () { + GuidComponent guid1 = guidBase; + GuidComponent guid2 = CreateNewGuid(); + + Assert.AreNotEqual(guid1.GetGuid(), guid2.GetGuid()); + + yield return null; + } + + [UnityTest] + public IEnumerator GuidDuplication () { + LogAssert.Expect(LogType.Warning, "Guid Collision Detected while creating GuidTestGO(Clone).\nAssigning new Guid."); + + GuidComponent clone = GameObject.Instantiate(guidBase); + + Assert.AreNotEqual(guidBase.GetGuid(), clone.GetGuid()); + + yield return null; + } + + [UnityTest] + public IEnumerator GuidPrefab () { + Assert.AreNotEqual(guidBase.GetGuid(), guidPrefab.GetGuid()); + Assert.AreEqual(guidPrefab.GetGuid(), System.Guid.Empty); + + yield return null; + } + + [UnityTest] + public IEnumerator GuidPrefabInstance () { + GuidComponent instance = GameObject.Instantiate(guidPrefab); + Assert.AreNotEqual(guidBase.GetGuid(), instance.GetGuid()); + Assert.AreNotEqual(instance.GetGuid(), guidPrefab.GetGuid()); + + yield return null; + } + + [UnityTest] + public IEnumerator GuidValidReference () { + GuidReference reference = new GuidReference(guidBase); + Assert.AreEqual(reference.gameObject, guidBase.gameObject); + + yield return null; + } + + [UnityTest] + public IEnumerator GuidInvalidReference () { + GuidComponent newGuid = CreateNewGuid(); + GuidReference reference = new GuidReference(newGuid); + Object.DestroyImmediate(newGuid); + + Assert.IsNull(reference.gameObject); + + yield return null; + } + + [OneTimeTearDown] + public void TearDown () { + AssetDatabase.DeleteAsset(prefabPath); + } + } +} diff --git a/Assets/CrossSceneReference/Tests/Editor/GuidReferenceTests.cs.meta b/Tests/Runtime/GuidReferenceTests.cs.meta similarity index 100% rename from Assets/CrossSceneReference/Tests/Editor/GuidReferenceTests.cs.meta rename to Tests/Runtime/GuidReferenceTests.cs.meta diff --git a/Tests/Runtime/com.unity.guid.Tests.asmdef b/Tests/Runtime/com.unity.guid.Tests.asmdef new file mode 100644 index 0000000..b8d260e --- /dev/null +++ b/Tests/Runtime/com.unity.guid.Tests.asmdef @@ -0,0 +1,22 @@ +{ + "name": "com.unity.guid.Tests", + "rootNamespace": "", + "references": [ + "GUID", + "UnityEngine.TestRunner", + "UnityEditor.TestRunner" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll" + ], + "autoReferenced": false, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Tests/Runtime/com.unity.guid.Tests.asmdef.meta b/Tests/Runtime/com.unity.guid.Tests.asmdef.meta new file mode 100644 index 0000000..237de54 --- /dev/null +++ b/Tests/Runtime/com.unity.guid.Tests.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: af6f01a44c1c3e14ebf0beaf779d5452 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From ed2fbf69df403a8c734b745f5999e48331bf15b5 Mon Sep 17 00:00:00 2001 From: Patrik Holmberg Date: Sat, 2 Jul 2022 11:11:26 +0200 Subject: [PATCH 5/8] Fixed: Minor edit to README --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 4c7aa67..c2b984b 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,5 @@ William Armstrong williama@unity3d.com 3. GuidReference.gameObject will then return the GameObject if it is loaded, otherwise null. ### Sample -Look in the Samples folder for example usage. Load up the LoadFirst scene, and then use the SceneLoader object to load ' -LoadSecond'. +Look in the Samples folder for example usage. Load up the LoadFirst scene, and then use the SceneLoader object to load 'LoadSecond'. You should see the CrossSceneReferencer object find the CrossSceneTarget object, and set both of them to start spinning. From 96bf455b037a3fe6f2a4b9446898979d9acc0d92 Mon Sep 17 00:00:00 2001 From: Patrik Holmberg Date: Sat, 2 Jul 2022 11:50:53 +0200 Subject: [PATCH 6/8] Fixed: Missing using reference --- Runtime/GuidComponent.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Runtime/GuidComponent.cs b/Runtime/GuidComponent.cs index 0a3255a..57f62d9 100644 --- a/Runtime/GuidComponent.cs +++ b/Runtime/GuidComponent.cs @@ -1,6 +1,7 @@ #if UNITY_EDITOR using UnityEditor; using UnityEditor.SceneManagement; +using UnityEditor.Experimental.SceneManagement; #endif // This component gives a GameObject a stable, non-replicatable Globally Unique IDentifier. From ec600a3f79cbef2d562d5269d3a57c68f0202dd8 Mon Sep 17 00:00:00 2001 From: Patrik Holmberg Date: Sat, 2 Jul 2022 11:57:02 +0200 Subject: [PATCH 7/8] Removed hidden Samples folder meta file --- Samples~.meta | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 Samples~.meta diff --git a/Samples~.meta b/Samples~.meta deleted file mode 100644 index ca24f80..0000000 --- a/Samples~.meta +++ /dev/null @@ -1,10 +0,0 @@ -fileFormatVersion: 2 -guid: 104046076828f4d46812aeed0183ae79 -folderAsset: yes -timeCreated: 1528409386 -licenseType: Pro -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: From 19352c9ea26a0975962a49978c3d61a8409c0515 Mon Sep 17 00:00:00 2001 From: Patrik Holmberg Date: Sat, 2 Jul 2022 11:57:22 +0200 Subject: [PATCH 8/8] Added: Package json file --- package.json | 17 +++++++++++++++++ package.json.meta | 7 +++++++ 2 files changed, 24 insertions(+) create mode 100644 package.json create mode 100644 package.json.meta diff --git a/package.json b/package.json new file mode 100644 index 0000000..478f01e --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "com.unity.guid", + "displayName": "GUID", + "version": "1.0.0", + "unity": "2019.4", + "description": "A component to give a Globaly Unique IDentifier (GUID) to a Game Object.", + "keywords": [ + "guid" + ], + "samples": [ + { + "displayName": "Sample", + "description": "Load up the LoadFirst scene, and then use the SceneLoader object to load 'LoadSecond'. You should see the CrossSceneReferencer object find the CrossSceneTarget object, and set both of them to start spinning.", + "path": "Samples~" + } + ] +} diff --git a/package.json.meta b/package.json.meta new file mode 100644 index 0000000..8fb940a --- /dev/null +++ b/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a93e8ec777bcd674fb1b7f11bec78d42 +PackageManifestImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: