Skip to content

Instantly share code, notes, and snippets.

@Rovsau
Last active August 7, 2023 00:48
Show Gist options
  • Save Rovsau/696df9f6c108c93e348bc79b2c4592eb to your computer and use it in GitHub Desktop.
Save Rovsau/696df9f6c108c93e348bc79b2c4592eb to your computer and use it in GitHub Desktop.

This is a Prefab Instance Replacer in the form of a MonoBehaviour script. It requires both PrefabInstanceReplacer.cs and GameObjectTypeClassifier.cs

using Rovsau.Unity.Editor.Extensions;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace Rovsau.Unity.Editor.Monos
{
    [ExecuteInEditMode]
    public class PrefabInstanceReplacer : MonoBehaviour
    {
        [Header("Config")]
        [SerializeField] private bool _includeInactive;
        [SerializeField] private GameObject _findAll;
        [SerializeField] private GameObject _replaceWith;

        [Header("Run")]
        [SerializeField] private bool _FindAllPrefabInstances;
        [SerializeField] private bool _InstantiateReplacements;
        [SerializeField] private bool _RemoveOriginals;

        [Header("Run")]
        [SerializeField] private bool _DoEverything;

        [Header("Objects in Scene")]
        [SerializeField] private GameObject[] _found;

        private void Update()
        {
            if (!Application.isPlaying)
            {
                if (_FindAllPrefabInstances)
                {
                    _FindAllPrefabInstances = false;
                    FindAllInstances();
                }

                if (_InstantiateReplacements)
                {
                    _InstantiateReplacements = false;
                    InstantiateReplacements();
                }

                if (_RemoveOriginals)
                {
                    _RemoveOriginals = false;
                    RemoveOriginals();
                }

                if (_DoEverything)
                {
                    _DoEverything = false;
                    FindAllInstances();
                    InstantiateReplacements();
                    RemoveOriginals();
                }
            }
        }

        public void FindAllInstances()
        {
            if (_findAll.IsSceneObject()) throw new System.ArgumentException("Scene Objects cannot have instances. Assign a Prefab.");
            if (!_findAll) throw new System.ArgumentException("Cannot find null. Assign a Prefab.");

            // If an instance was used, its corresponding asset is returned. 
            _findAll = _findAll.GetPrefabAsset();

            // If an instance was used, its corresponding asset is returned. 
            if (_replaceWith) _replaceWith = _replaceWith.GetPrefabAsset();

#if UNITY_2021_3_OR_NEWER
            _found = PrefabUtility.FindAllInstancesOfPrefab(_findAll);
            if (!_includeInactive) RemoveInactive();
#else
            _found = FindAllInstancesOfPrefab(_findAll, _includeInactive);
#endif
            System.Array.Sort(_found, (a, b) => a.name.CompareTo(b.name));
        }

        private void RemoveInactive()
        {
            for (int i = _found.Length - 1; i >= 0; i--)
            {
                if (!_found[i].activeInHierarchy) ArrayUtility.RemoveAt(ref _found, i);
            }
        }

        public static GameObject[] FindAllInstancesOfPrefab(GameObject prefab, bool includeInactive)
        {
            GameObject[] loadedSceneObjects = FindObjectsOfType<GameObject>(includeInactive);
            List<GameObject> results = new List<GameObject>();
            for (int i = 0; i < loadedSceneObjects.Length; i++)
            {
                if (PrefabUtility.GetCorrespondingObjectFromSource(loadedSceneObjects[i]) == prefab)
                {
                    results.Add(loadedSceneObjects[i]);
                }
            }
            return results.ToArray();
        }

        public void InstantiateReplacements()
        {
            if (_replaceWith.IsSceneObject()) throw new System.ArgumentException("Cannot replace with a Scene Object. Make it into a Prefab first.");
            if (!_replaceWith) throw new System.ArgumentException("Cannot replace with null. Assign a replacement Prefab.");

            // If an instance was used, its corresponding asset is returned. 
            _findAll = _findAll.GetPrefabAsset();

            int n = 0;
            foreach (GameObject go in _found)
            {
                GameObject g = (GameObject)PrefabUtility.InstantiatePrefab(_replaceWith);
                Undo.RegisterCreatedObjectUndo(g, "Instantiate Prefab");
                g.transform.position = go.transform.position;
                g.transform.rotation = go.transform.rotation;
                g.transform.parent = go.transform.parent;
                g.name = $"{g.name}_{n++}";
            }
        }

        public void RemoveOriginals()
        {
            GameObject go;
            bool isAsset;
            for (int i = _found.Length - 1; i >= 0; i--)
            {
                if (go = _found[i])
                {
                    // Failsafe: Does not destroy files stored on disk. 
                    isAsset = EditorUtility.IsPersistent(go);
                    if (!isAsset) Undo.DestroyObjectImmediate(_found[i]);

                }
                ArrayUtility.RemoveAt(ref _found, i);
            }
        }
    }
}
using System;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
//using static Rovsau.Unity.Editor.Extensions.GameObjectTypeClassifierInternal;

namespace Rovsau.Unity.Editor.Extensions
{
    public enum GameObjectType
    {
        SceneObject,
        PrefabAsset,
        PrefabInstance,
        PrefabVariantAsset,
        PrefabVariantInstance,
    }

    public static partial class GameObjectTypeClassifier
    {
        /// <summary>
        /// Scene Objects only exist inside the scene file itself. 
        /// </summary>
        /// <param name="componentOrGameObject"></param>
        /// <returns>True if the object only exists in the scene. False if the object is an Asset or an Instance.</returns>
        public static bool IsSceneObject(this UnityEngine.Object componentOrGameObject)
        {
            return PrefabUtility.GetPrefabInstanceStatus(componentOrGameObject) == PrefabInstanceStatus.NotAPrefab && PrefabUtility.GetPrefabAssetType(componentOrGameObject) == PrefabAssetType.NotAPrefab;
        }

        public static bool IsPrefabAsset(this UnityEngine.Object componentOrGameObject)
        {
            return PrefabUtility.GetPrefabInstanceStatus(componentOrGameObject) == PrefabInstanceStatus.NotAPrefab && PrefabUtility.GetPrefabAssetType(componentOrGameObject) == PrefabAssetType.Regular;
        }

        public static bool IsPrefabInstance(this UnityEngine.Object componentOrGameObject)
        {
            return PrefabUtility.GetPrefabInstanceStatus(componentOrGameObject) != PrefabInstanceStatus.NotAPrefab && PrefabUtility.GetPrefabAssetType(componentOrGameObject) == PrefabAssetType.Regular;
        }

        public static bool IsPrefabVariantAsset(this UnityEngine.Object componentOrGameObject)
        {
            return PrefabUtility.GetPrefabInstanceStatus(componentOrGameObject) == PrefabInstanceStatus.NotAPrefab && PrefabUtility.GetPrefabAssetType(componentOrGameObject) == PrefabAssetType.Variant;
        }

        public static bool IsPrefabVariantInstance(this UnityEngine.Object componentOrGameObject)
        {
            return PrefabUtility.GetPrefabInstanceStatus(componentOrGameObject) != PrefabInstanceStatus.NotAPrefab && PrefabUtility.GetPrefabAssetType(componentOrGameObject) == PrefabAssetType.Variant;
        }

        public static GameObjectType GetGameObjectType(this UnityEngine.Object componentOrGameObject, out PrefabInstanceStatus instanceStatus, out PrefabAssetType assetType)
        {
            PrefabInstanceStatus i = PrefabUtility.GetPrefabInstanceStatus(componentOrGameObject);
            PrefabAssetType a = PrefabUtility.GetPrefabAssetType(componentOrGameObject);
            instanceStatus = i;
            assetType = a;
            if (IsSceneObject(i, a)) return GameObjectType.SceneObject;
            if (IsPrefabAsset(i, a)) return GameObjectType.PrefabAsset;
            if (IsPrefabInstance(i, a)) return GameObjectType.PrefabInstance;
            if (IsPrefabVariantAsset(i, a)) return GameObjectType.PrefabVariantAsset;
            if (IsPrefabVariantInstance(i, a)) return GameObjectType.PrefabVariantInstance;

            throw new NotSupportedException("GameObjectType could not be determined by PrefabInstanceStatus and PrefabAssetType.");
        }

        public static GameObjectType GetGameObjectType(this UnityEngine.Object componentOrGameObject)
        {
            PrefabInstanceStatus instanceStatus = PrefabUtility.GetPrefabInstanceStatus(componentOrGameObject);
            PrefabAssetType assetType = PrefabUtility.GetPrefabAssetType(componentOrGameObject);
            if (IsSceneObject(instanceStatus, assetType)) return GameObjectType.SceneObject;
            if (IsPrefabAsset(instanceStatus, assetType)) return GameObjectType.PrefabAsset;
            if (IsPrefabInstance(instanceStatus, assetType)) return GameObjectType.PrefabInstance;
            if (IsPrefabVariantAsset(instanceStatus, assetType)) return GameObjectType.PrefabVariantAsset;
            if (IsPrefabVariantInstance(instanceStatus, assetType)) return GameObjectType.PrefabVariantInstance;

            throw new NotSupportedException("GameObjectType could not be determined by PrefabInstanceStatus and PrefabAssetType.");
        }

        /// <summary>
        /// Accurate for Prefabs, Variants and instances. Checking for Unpacked Models in the scene might return a false positive if the object is a distant Parent of a logical Model. 
        /// </summary>
        /// <param name="componentOrGameObject"></param>
        /// <returns>True if the object is part of a Model Prefab (Asset or Instance), or if it is not a prefab and contains known model components.</returns>
        public static bool IsModel(this UnityEngine.Object componentOrGameObject)
        {
            GameObjectType t = GetGameObjectType(componentOrGameObject);

            switch (t)
            {
                case GameObjectType.SceneObject: // SceneObjectHasModel() instead?
                    GameObject go = (componentOrGameObject is Component) ? (componentOrGameObject as Component).gameObject : (GameObject)componentOrGameObject;
                    return go.GetComponentInChildren<SkinnedMeshRenderer>() != null || (go.GetComponentInChildren<MeshRenderer>() != null && go.GetComponentInChildren<MeshFilter>() != null);
                case GameObjectType.PrefabAsset:
                case GameObjectType.PrefabInstance:
                    return PrefabUtility.IsPartOfModelPrefab(componentOrGameObject);
                case GameObjectType.PrefabVariantAsset:
                case GameObjectType.PrefabVariantInstance:
                default:
                    return PrefabUtility.IsPartOfModelPrefab(PrefabUtility.GetCorrespondingObjectFromOriginalSource(componentOrGameObject));
            }
        }

        public static void VariantIsModel(this UnityEngine.Object componentOrGameObject)
        {
            //PrefabUtility.IsPartOfModelPrefab
        }

        // unfinished // makes sense??
        public static bool IsNestedAssetOrInstance(this UnityEngine.Object componentOrGameObject)
        {
            return PrefabUtility.IsPartOfPrefabAsset(componentOrGameObject) && PrefabUtility.IsPartOfPrefabInstance(componentOrGameObject);
        }

        // unfinished
        public static bool IsAsset(this UnityEngine.Object componentOrGameObject)
        {
            return PrefabUtility.IsPartOfPrefabAsset(componentOrGameObject);
            //return !PrefabUtility.IsPartOfNonAssetPrefabInstance(componentOrGameObject);
            //return PrefabUtility.GetPrefabInstanceStatus(componentOrGameObject) != PrefabInstanceStatus.NotAPrefab && PrefabUtility.GetPrefabAssetType(componentOrGameObject) != PrefabAssetType.NotAPrefab;
        }

        /// <summary>
        /// Is part of the current Prefab Stage. 
        /// </summary>
        /// <param name="gameObject"></param>
        /// <returns>True if the GameObject is currently being edited in Prefab Mode, or being dragged from Project View to Scene View.</returns>
        public static bool IsBeingEditedInPrefabMode(UnityEngine.Object componentOrGameObject)
        {
            GameObject gameObject = (componentOrGameObject is Component) ? (componentOrGameObject as Component).gameObject : (GameObject)componentOrGameObject;
            PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
            return prefabStage != null && prefabStage.IsPartOfPrefabContents(gameObject);
        }

        public static bool IsInstance(this UnityEngine.Object componentOrGameObject)
        {
            PrefabUtility.IsPartOfPrefabInstance(componentOrGameObject);
            return PrefabUtility.IsPartOfNonAssetPrefabInstance(componentOrGameObject);
            //return PrefabUtility.GetPrefabInstanceStatus(componentOrGameObject) != PrefabInstanceStatus.NotAPrefab && PrefabUtility.GetPrefabAssetType(componentOrGameObject) != PrefabAssetType.NotAPrefab;
        }

        public static bool IsMissingAsset(this UnityEngine.Object componentOrGameObject)
        {
            return PrefabUtility.IsPrefabAssetMissing(componentOrGameObject);
            //return PrefabUtility.GetPrefabInstanceStatus(componentOrGameObject) == PrefabInstanceStatus.MissingAsset || PrefabUtility.GetPrefabAssetType(componentOrGameObject) == PrefabAssetType.MissingAsset;
        }

        // unfinished
        public static bool IsVariant(this UnityEngine.Object componentOrGameObject)
        {
            return PrefabUtility.IsPartOfVariantPrefab(componentOrGameObject);
            //return PrefabUtility.GetPrefabInstanceStatus(componentOrGameObject) != PrefabInstanceStatus.NotAPrefab && PrefabUtility.GetPrefabAssetType(componentOrGameObject) != PrefabAssetType.NotAPrefab;
        }

        /// <summary>
        /// If the gameObject is an instance, return its corresponding asset, else return the original gameObject. 
        /// </summary>
        /// <param name="gameObject"></param>
        /// <returns></returns>
        public static GameObject GetPrefabAsset(this GameObject gameObject)
        {
            // Use IsInstance() instead.
            GameObjectType t = GetGameObjectType(gameObject);
            switch (t)
            {
                case GameObjectType.PrefabInstance:
                case GameObjectType.PrefabVariantInstance:
                    return PrefabUtility.GetCorrespondingObjectFromSource(gameObject); // null check instead?
                case GameObjectType.SceneObject:
                case GameObjectType.PrefabAsset:
                case GameObjectType.PrefabVariantAsset:
                default:
                    return gameObject;
            }
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment