static void MergeScenes()
		{
			Scene activeScene = new Scene();
			AmsMultiSceneSetup activeSetup = null;
			List<AmsMultiSceneSetup.SceneEntry> bakedScenes = new List<AmsMultiSceneSetup.SceneEntry>();

			GetCommonParameters( ref activeScene, ref activeSetup, bakedScenes );
			if ( bakedScenes.Count < 1 )
				return;

			AmsDebug.Log( null, "Running AMS MergeScenes on Scene {0} ({1})", activeScene.name, activeSetup.scenePath );

			foreach( var entry in bakedScenes )
			{
				if ( !entry.scene.isLoaded )
				{
					AmsDebug.LogError( activeSetup, "Could not merge non-loaded scene: {0}", entry.scene.name );
					continue;
				}

				// Merge the cross-scene references (and keep track of the merges)
				var bakedSceneSetup = GameObjectEx.GetSceneSingleton<AmsMultiSceneSetup>( entry.scene.scene, false );
				if ( bakedSceneSetup )
					AmsCrossSceneReferences.EditorBuildPipelineMergeScene( bakedSceneSetup, activeSetup );

				AmsDebug.Log( null, "Running Unity MergeScenes for {0} into {1}", entry.scene.name, activeScene.name );
				EditorSceneManager.MergeScenes( entry.scene.scene, activeScene );
			}
		} // MergeScenes
		static void RestoreCrossSceneReferences()
		{
			Scene activeScene = new Scene();
			AmsMultiSceneSetup activeSetup = null;
			List<AmsMultiSceneSetup.SceneEntry> bakedScenes = new List<AmsMultiSceneSetup.SceneEntry>();

			GetCommonParameters( ref activeScene, ref activeSetup, bakedScenes );
			if ( bakedScenes.Count < 1 )
				return;

			AmsDebug.Log( null, "Running RestoreCrossSceneReferences on Scene {0}", activeScene.name );

			// Do the merge (bake)
			var targetCrossRefs = AmsCrossSceneReferences.GetSceneSingleton( activeScene, false );
			if ( targetCrossRefs )
				targetCrossRefs.ResolvePendingCrossSceneReferences();

			foreach( var entry in bakedScenes )
			{
				if ( !entry.scene.isLoaded )
				{
					AmsDebug.LogError( activeSetup, "Could not restore cross-scene references for non-loaded scene: {0}", entry.scene.name );
					continue;
				}

				var sourceCrossRefs = AmsCrossSceneReferences.GetSceneSingleton( entry.scene.scene, false );
				if ( sourceCrossRefs )
					sourceCrossRefs.ResolvePendingCrossSceneReferences();
			}
		}
		static void MergeScenes()
		{
			Scene activeScene = new Scene();
			AmsMultiSceneSetup activeSetup = null;
			List<AmsMultiSceneSetup.SceneEntry> bakedScenes = new List<AmsMultiSceneSetup.SceneEntry>();

			GetCommonParameters( ref activeScene, ref activeSetup, bakedScenes );
			if ( bakedScenes.Count < 1 )
				return;

			AmsDebug.Log( null, "Running MergeScenes on Scene {0}", activeScene.name );

			foreach( var entry in bakedScenes )
			{
				if ( !entry.scene.isLoaded )
				{
					AmsDebug.LogError( activeSetup, "Could not merge non-loaded scene: {0}", entry.scene.name );
					continue;
				}

				var sourceCrossRefs = AmsCrossSceneReferences.GetSceneSingleton( entry.scene.scene, false );
				if ( sourceCrossRefs )
					GameObject.DestroyImmediate( sourceCrossRefs.gameObject, false );

				AmsDebug.Log( null, "Merging {0} into {1}", entry.scene.name, activeScene.name );
				EditorSceneManager.MergeScenes( entry.scene.scene, activeScene );
			}
		} // MergeScenes
Beispiel #4
0
		private static void EditorApplication_playModeStateChanged()
		{
			bool isExitingEditMode = !EditorApplication.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode;
#endif

#if UNITY_5_6_OR_NEWER
			if ( EditorUtility.scriptCompilationFailed )
			{
				AmsDebug.Log( null, "Skipping cross-scene references due to compilation errors" );
				return;
			}
#endif

			if ( isExitingEditMode )
			{
				List<Scene> allScenes = new List<Scene>( EditorSceneManager.sceneCount );
				for (int i = 0 ; i < EditorSceneManager.sceneCount ; ++i)
				{
					var scene = EditorSceneManager.GetSceneAt(i);
					if ( scene.IsValid() && scene.isLoaded )
						allScenes.Add( scene );
				}

				AmsDebug.Log( null, "Handling Cross-Scene Referencing for Playmode" );
				AmsSaveProcessor.HandleCrossSceneReferences( allScenes );
			}
		}
Beispiel #5
0
		public static void HandleCrossSceneReferences( IList<Scene> scenes )
		{
			// If we don't allow cross-scene references, then early return.
			var crossSceneReferenceBehaviour = AmsPreferences.CrossSceneReferencing;
			bool bSkipCrossSceneReferences = (crossSceneReferenceBehaviour == AmsPreferences.CrossSceneReferenceHandling.UnityDefault);
			bool bSaveCrossSceneReferences = (crossSceneReferenceBehaviour == AmsPreferences.CrossSceneReferenceHandling.Save);

			if ( bSkipCrossSceneReferences || scenes.Count < 1 )
				return;

			// We need to create an AmsMultiSceneSetup singleton in every scene.  This is how we keep track of Awake scenes and
			// it also allows us to use cross-scene references.
			foreach( var scene in scenes )
			{
				if ( !scene.isLoaded )
					continue;

				// Reset all of the cross-scene references for loaded scenes.
				var crossSceneRefBehaviour = AmsCrossSceneReferences.GetSceneSingleton( scene, true );
				for (int i = 0 ; i < EditorSceneManager.sceneCount ; ++i)
				{
					var otherScene = EditorSceneManager.GetSceneAt(i);
					if ( otherScene.isLoaded )
						crossSceneRefBehaviour.ResetCrossSceneReferences( otherScene );
				}
			}

			var xSceneRefs = AmsCrossSceneReferenceProcessor.GetCrossSceneReferencesForScenes( scenes );
			if ( bSaveCrossSceneReferences && xSceneRefs.Count > 0 )
			{
				AmsDebug.LogWarning( null, "Ams Plugin: Found {0} Cross-Scene References. Saving them.", xSceneRefs.Count );
				AmsCrossSceneReferenceProcessor.SaveCrossSceneReferences( xSceneRefs );
			}

			// Zero-out these cross-scene references so we can save without pulling in those assets.
			for(int i = 0 ; i < xSceneRefs.Count ; ++i)
			{
				var xRef = xSceneRefs[i];
				int refIdToRestore = xRef.fromProperty.objectReferenceInstanceIDValue;
					
				if ( !bSaveCrossSceneReferences )
					Debug.LogWarningFormat( "Cross-Scene Reference {0} will become null", xRef );

				// Set it to null.
				xRef.fromProperty.objectReferenceInstanceIDValue = 0;
				xRef.fromProperty.serializedObject.ApplyModifiedPropertiesWithoutUndo();

				// Restore if we're not about to enter play mode
				if ( !EditorApplication.isPlayingOrWillChangePlaymode )
				{
					EditorApplication.delayCall += () =>
						{
							AmsDebug.Log( null, "Restoring Cross-Scene Ref (Post-Save): {0}", xRef );
							xRef.fromProperty.objectReferenceInstanceIDValue = refIdToRestore;
							xRef.fromProperty.serializedObject.ApplyModifiedPropertiesWithoutUndo();
						};
				}
			}
		}
Beispiel #6
0
        static void PlayableDirector_SceneBindings(RuntimeCrossSceneReference xRef)
        {
            var data = xRef.data;

            UnityEngine.Playables.PlayableDirector playableDirector = xRef.fromObject as UnityEngine.Playables.PlayableDirector;
            for (int i = 0; i < data.Count; i += 2)
            {
                Object key = data[i].@object;

                AmsDebug.Log(xRef.fromObject, "Restoring PlayableDirector Scene Binding {0} = {1}", key, xRef.toObject);
                playableDirector.SetGenericBinding(key, xRef.toObject);
            }
        }
Beispiel #7
0
        /// <summary>
        /// Save all of the passed-in cross-scene references.  The entries in the passed-in list will be removed as they are properly accounted for.
        /// </summary>
        /// <param name="editorCrossSceneRefs"></param>
        public static void SaveCrossSceneReferences( List<EditorCrossSceneReference> editorCrossSceneRefs )
        {
            // Save all of the cross-scene references, removing them from our input list as we receive them
            for( int i = editorCrossSceneRefs.Count-1 ; i >= 0 ; --i)
            {
                var xRef = editorCrossSceneRefs[i];

				AmsDebug.Log( null, "Saving Cross-Scene Reference: {0}", xRef );
				
                try
                {
					RuntimeCrossSceneReference serializedReference = xRef.ToSerializable();

					try
					{
						// Save the object
						var initialObject = xRef.fromProperty.objectReferenceValue;
						
						// Resolve it (this can throw exceptions)
						AmsCrossSceneReferenceResolver.Resolve( serializedReference );
#if UNITY_5_6_OR_NEWER
						xRef.fromProperty.serializedObject.UpdateIfRequiredOrScript();
#else
						xRef.fromProperty.serializedObject.UpdateIfDirtyOrScript();
#endif

						// Check to make sure it resolved properly
						if ( initialObject && xRef.fromProperty.objectReferenceValue != initialObject )
							throw new ResolveException( string.Format("Resolve should have pointed to {0} ({1}) but instead resolved to {2} ({3})", initialObject ? initialObject.ToString() : "(null)", initialObject ? initialObject.GetInstanceID() : 0, xRef.fromProperty.objectReferenceValue, xRef.fromProperty.objectReferenceInstanceIDValue) );
					}
					catch ( System.Exception ex )
					{
						AmsDebug.LogError( xRef.fromObject, "Could not perform a runtime resolve on cross-scene reference {0}.\nReason: {1}. Please review Documentation.", serializedReference, ex.Message );
						continue;
					}

					// Record the cross-scene reference
					var crossSceneRefBehaviour = AmsCrossSceneReferences.GetSceneSingleton( xRef.fromScene, true );
					crossSceneRefBehaviour.AddReference( serializedReference );

                    // Add an updated reference map value
                    if ( _referenceMap != null )
                        _referenceMap.Add( new KeyValuePair<SerializedProperty, Object>(xRef.fromProperty, xRef.fromProperty.objectReferenceValue) );
                }
                catch ( UnityException ex )
                {
                    Debug.LogException( ex );
                }
            }
        }
Beispiel #8
0
        static void PlayableDirector_ExposedReferences(RuntimeCrossSceneReference xRef)
        {
            var data = xRef.data;

            UnityEngine.Playables.PlayableDirector playableDirector = xRef.fromObject as UnityEngine.Playables.PlayableDirector;
            for (int i = 0; i < data.Count; i += 2)
            {
                string key = data[i].@string;

                AmsDebug.Log(xRef.fromObject, "Restoring PlayableDirector Exposed Binding {0} = {1}", key, xRef.toObject);
                playableDirector.ClearReferenceValue(key);
                playableDirector.SetReferenceValue(key, xRef.toObject);
            }
        }
		/// <summary>
		/// Attempt to handle a cross-scene reference.
		/// </summary>
		static bool HandleCrossSceneReference( RuntimeCrossSceneReference xRef )
		{
			MonoBehaviour cinemachineBehaviour = xRef.fromObject as MonoBehaviour;
			if ( !cinemachineBehaviour || !cinemachineBehaviour.isActiveAndEnabled )
				return false;

			if ( !cinemachineBehaviour.GetType().Namespace.StartsWith( "Cinemachine" ) )
				return false;

			AmsDebug.LogWarning( xRef.fromObject, "xSceneRef on Cinemachine Behaviour: {0}. Disabling/Enabling to ensure pipeline is up to date.", xRef );
			cinemachineBehaviour.enabled = false;
			cinemachineBehaviour.enabled = true;

			return false;
		}
Beispiel #10
0
        /// <summary>
        /// Attempt to handle a cross-scene reference.
        /// </summary>
        static bool HandleCrossSceneReference(RuntimeCrossSceneReference xRef)
        {
            if (!(xRef.fromObject is UnityEngine.Playables.PlayableDirector))
            {
                return(false);
            }

            bool isDirty = false;

            string sourceField = xRef.sourceField;

            if (sourceField.StartsWith("m_SceneBindings"))
            {
                PlayableDirector_SceneBindings(xRef);
                isDirty = true;
            }

            if (sourceField.StartsWith("m_ExposedReferences"))
            {
                PlayableDirector_ExposedReferences(xRef);
                isDirty = true;
            }

            if (isDirty)
            {
                UnityEngine.Playables.PlayableDirector playableDirector = xRef.fromObject as UnityEngine.Playables.PlayableDirector;
                if (playableDirector)
                {
#if UNITY_2017_3_OR_NEWER
                    if (playableDirector.state == UnityEngine.Playables.PlayState.Playing)
                    {
                        AmsDebug.LogWarning(playableDirector, "To prevent issues, delay the PlayableDirector '{0}' until after cross-scene references are loaded. Cross-Scene Reference: {1}", playableDirector, xRef);
                        playableDirector.RebuildGraph();
                    }
#else
                    if (playableDirector.gameObject.activeSelf)
                    {
                        AmsDebug.LogWarning(playableDirector, "Upgrade to Unity 2017.3 for proper Playables support. Hack work-around for 2017.1 and 2017.2: Disable/ReEnable the GameObject");
                        playableDirector.gameObject.SetActive(false);
                        playableDirector.gameObject.SetActive(true);
                    }
#endif
                }
            }

            return(isDirty);
        }
Beispiel #11
0
        /// <summary>
        /// Given a property, let's return a runtime serializeable field string.
        /// </summary>
        /// <param name="property">The property to capture the field from</param>
        /// <returns>The field string which can be parsed at runtime</returns>
        private string ToRuntimeSerializableField(SerializedProperty property)
        {
            const string ARRAY_INDICATOR      = "@ArrayIndex[";
            int          arrayIndicatorLength = ARRAY_INDICATOR.Length;

            // Give us an easy sentinel value to scan for in case of arrays
            string parseablePropertyPath = property.propertyPath.Replace(".Array.data[", "." + ARRAY_INDICATOR);
            var    splitPaths            = parseablePropertyPath.Split('.');

            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            for (int i = 0; i < splitPaths.Length; ++i)
            {
                string pathPiece = splitPaths[i];

                bool bIsArrayIndex = pathPiece.StartsWith(ARRAY_INDICATOR);
                if (!bIsArrayIndex)
                {
                    // Append the . if we're a nested object
                    if (i > 0)
                    {
                        sb.Append('.');
                    }

                    sb.Append(pathPiece);
                }
                else
                {
                    // It's an array, so we're doing the index portion of @ArrayIndex[index]
                    string indexString = pathPiece.Substring(arrayIndicatorLength, pathPiece.Length - arrayIndicatorLength - 1);

                    int arrayIndex = 0;
                    if (int.TryParse(indexString, out arrayIndex))
                    {
                        // Arrays are of the form fieldName,arrayIndex
                        sb.Append(',');
                        sb.Append(arrayIndex);
                    }
                    else
                    {
                        AmsDebug.LogError(null, "Could not parse array index for property path {0}", property.propertyPath);
                    }
                }
            }

            return(sb.ToString());
        }
		private static void SaveCrossSceneReferencesBeforePlayInEditMode()
		{
			EditorApplication.playmodeStateChanged += () =>
				{
					if ( !EditorApplication.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode )
					{
						List<Scene> allScenes = new List<Scene>( EditorSceneManager.sceneCount );
						for (int i = 0 ; i < EditorSceneManager.sceneCount ; ++i)
						{
							var scene = EditorSceneManager.GetSceneAt(i);
							if ( scene.IsValid() && scene.isLoaded )
								allScenes.Add( scene );
						}

						AmsDebug.Log( null, "Handling Cross-Scene Referencing for Playmode" );
						AmsSaveProcessor.HandleCrossSceneReferences( allScenes );
					}
				};
		}
        internal static void LoadScenesForMerging()
		{
			Scene activeScene = new Scene();
			AmsMultiSceneSetup activeSetup = null;
			List<AmsMultiSceneSetup.SceneEntry> bakedScenes = new List<AmsMultiSceneSetup.SceneEntry>();

			GetCommonParameters( ref activeScene, ref activeSetup, bakedScenes );
			if ( bakedScenes.Count < 1 )
				return;

			AmsDebug.Log( null, "Running LoadScenesForBaking on Scene {0}", activeScene.name );

			// Now load all of the scenes
			foreach( var entry in bakedScenes )
			{
				var realScene = entry.scene.scene;
				if ( !realScene.IsValid() )
				{
					// This is good.  This means it's not loaded yet.
					realScene = EditorSceneManager.OpenScene( entry.scene.editorPath, OpenSceneMode.Additive );

					if ( !realScene.IsValid() )
					{
						AmsDebug.LogError( activeSetup, "BakeScene: Scene {0} ({1}) referenced from Multi-Scene Setup in {2} is invalid.", entry.scene.editorPath, entry.scene.name, activeScene.name );
						continue;
					}
				}

				// Let's catch this...
				if ( !realScene.isLoaded )
				{
					realScene = EditorSceneManager.OpenScene( realScene.path, OpenSceneMode.Additive );
					
					// if we're still not loaded, we're probably in trouble.
					if ( !realScene.isLoaded )
					{
						AmsDebug.LogError( activeSetup, "BakeScene: Scene {0} ({1}) referenced from Multi-Scene Setup in {2} could not load.", entry.scene.editorPath, entry.scene.name, activeScene.name );
						continue;
					}
				}
			}
		}
Beispiel #14
0
		/// <summary>
		/// This will update the map of SerializedProperties to Object ID
		/// </summary>
		private static void UpdateReferencesMap()
		{
			if ( _referenceMap != null )
				return;

			double startTime = EditorApplication.timeSinceStartup;

			// Get all of the valid MonoBehaviours or ScriptableObjects
			var allMonoBehaviourObjs = Resources.FindObjectsOfTypeAll<MonoBehaviour>().Where( x => !EditorUtility.IsPersistent(x) ).Cast<Object>();
			var allScriptableObjs = Resources.FindObjectsOfTypeAll<ScriptableObject>().Where( x => 
				!EditorUtility.IsPersistent(x) && !typeof(EditorWindow).IsAssignableFrom(x.GetType())
			).Cast<Object>();

			// We assume we're up to date for a single frame...
			EditorApplication.delayCall += () => { _referenceMap = null; };
			_referenceMap = new List<KeyValuePair<SerializedProperty, Object>>( allMonoBehaviourObjs.Count() + allScriptableObjs.Count() );

			// Figure out what they're referencing
			PopulateReferenceMap( _referenceMap, allMonoBehaviourObjs );
			PopulateReferenceMap( _referenceMap, allScriptableObjs );

			// Now we have all of the custom data processors.  Note that we don't want to run over-top of what we've already processed, so let's exclude all of those...
			foreach ( var customType in _customCrossSceneReferenceDataProcessors.Keys )
			{
				// Handle the cases where we've already added these objects to the list...
				if ( typeof(MonoBehaviour).IsAssignableFrom(customType) )
					continue;

				if ( typeof(ScriptableObject).IsAssignableFrom(customType) )
					continue;

				// Now grab all of the objects and add them to our reference map
				var sceneCustomObjects = Resources.FindObjectsOfTypeAll( customType ).Where( x => !EditorUtility.IsPersistent(x) );
				PopulateReferenceMap( _referenceMap, sceneCustomObjects );
			}

			AmsDebug.LogPerf( null, "Cross-Scene Reference Map Update: {0}", (EditorApplication.timeSinceStartup - startTime) );
		}
Beispiel #15
0
		/// <summary>
		/// This will update the map of SerializedProperties to Object ID
		/// </summary>
		private static void UpdateReferencesMap()
		{
			if ( _referenceMap != null )
				return;

			double startTime = EditorApplication.timeSinceStartup;

			// Get all of the valid MonoBehaviours or ScriptableObjects
			var allMonoBehaviourObjs = Resources.FindObjectsOfTypeAll<MonoBehaviour>().Where( x => !EditorUtility.IsPersistent(x) ).Cast<Object>();
			var allScriptableObjs = Resources.FindObjectsOfTypeAll<ScriptableObject>().Where( x => 
				!EditorUtility.IsPersistent(x) && !typeof(EditorWindow).IsAssignableFrom(x.GetType())
			).Cast<Object>();

			// We assume we're up to date for a single frame...
			EditorApplication.delayCall += () => { _referenceMap = null; };
			_referenceMap = new List<KeyValuePair<SerializedProperty, Object>>( allMonoBehaviourObjs.Count() + allScriptableObjs.Count() );

			// Figure out what they're referencing
			PopulateReferenceMap( _referenceMap, allMonoBehaviourObjs );
			PopulateReferenceMap( _referenceMap, allScriptableObjs );

			AmsDebug.LogPerf( null, "Cross-Scene Reference Map Update: {0}", (EditorApplication.timeSinceStartup - startTime) );
		}