Skip to content

Instantly share code, notes, and snippets.

@JohannesDeml
Forked from ulrikdamm/EditPrefab.cs
Last active May 17, 2023 04:40
Show Gist options
  • Save JohannesDeml/5802473b569718c9c86de906b7039aec to your computer and use it in GitHub Desktop.
Save JohannesDeml/5802473b569718c9c86de906b7039aec to your computer and use it in GitHub Desktop.
Unity editor script for better editing of prefabs. Put in Assets/Editor.
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
// From https://gist.github.com/JohannesDeml/5802473b569718c9c86de906b7039aec
// Original https://gist.github.com/ulrikdamm/338392c3b0900de225ec6dd10864cab4
// Adds a "Edit Prefab" option in the Assets menu (or right clicking an asset in the project browser).
// This opens an empty scene with your prefab where you can edit it.
// Put this script in your project as Assets/Editor/EditPrefab.cs
public class EditPrefabInScene
{
private static Object GetPrefab(Object selection)
{
if (selection == null)
{
return null;
}
var prefabType = PrefabUtility.GetPrefabType(selection);
if (prefabType == PrefabType.PrefabInstance)
{
var prefab = PrefabUtility.GetPrefabParent(selection);
return prefab;
}
if (prefabType == PrefabType.Prefab)
{
return selection;
}
return null;
}
[MenuItem("Assets/Edit prefab")]
public static void EditPrefab()
{
var prefab = GetPrefab(Selection.activeObject);
EditorSceneManager.SaveOpenScenes();
var currentScenes = new SceneSetupWrapper();
currentScenes.TakeSnapshot();
var scene = EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single);
var instance = (GameObject) PrefabUtility.InstantiatePrefab(prefab, scene);
Selection.activeObject = instance;
SceneView.lastActiveSceneView.FrameSelected();
var returnHandler = new ReturnToSceneGUI();
returnHandler.PreviousSceneSetup = currentScenes;
returnHandler.PrefabInstance = instance;
}
[MenuItem("Assets/Edit prefab", true)]
public static bool EditPrefabValidator()
{
var prefab = GetPrefab(Selection.activeObject);
return prefab != null;
}
}
public sealed class SceneSetupWrapper
{
private string[] scenePaths;
private bool[] scenesLoaded;
private int activeSceneIndex;
public void TakeSnapshot()
{
int sceneCount = SceneManager.sceneCount;
scenePaths = new string[sceneCount];
scenesLoaded = new bool[sceneCount];
var activeScene = SceneManager.GetActiveScene();
for (int i = 0; i < sceneCount; i++)
{
var scene = SceneManager.GetSceneAt(i);
scenePaths[i] = scene.path;
scenesLoaded[i] = scene.isLoaded;
if (scene == activeScene)
{
activeSceneIndex = i;
}
}
}
public void OpenSetup()
{
if (scenePaths.Length == 0)
{
Debug.LogError("Can't open scene setup, no data stored.");
return;
}
try
{
bool canceled = false;
int sceneCount = scenePaths.Length;
canceled = EditorUtility.DisplayCancelableProgressBar("Loading scenes",
"Loading scenes from Setup", (float)0 / sceneCount);
Scene activeScene = EditorSceneManager.OpenScene(scenePaths[0]);
if (canceled)
{
EditorUtility.ClearProgressBar();
return;
}
for (int i = 1; i < sceneCount; i++)
{
canceled = EditorUtility.DisplayCancelableProgressBar("Loading scenes",
"Loading scenes from Setup", (float)i / sceneCount);
if (canceled)
{
EditorUtility.ClearProgressBar();
return;
}
var openSceneMode = scenesLoaded[i] ? OpenSceneMode.Additive : OpenSceneMode.AdditiveWithoutLoading;
var scene = EditorSceneManager.OpenScene(scenePaths[i], openSceneMode);
if (activeSceneIndex == i)
{
activeScene = scene;
}
}
SceneManager.SetActiveScene(activeScene);
}
finally
{
EditorUtility.ClearProgressBar();
}
}
}
public sealed class ReturnToSceneGUI
{
public SceneSetupWrapper PreviousSceneSetup;
public GameObject PrefabInstance;
private GUIStyle style;
public ReturnToSceneGUI()
{
SceneView.onSceneGUIDelegate += RenderSceneGUI;
style = new GUIStyle();
style.margin = new RectOffset(10, 10, 10, 10);
}
public void RenderSceneGUI(SceneView sceneview)
{
Handles.BeginGUI();
GUILayout.BeginArea(new Rect(20, 20, 180, 300), style);
var rect = EditorGUILayout.BeginVertical();
GUI.Box(rect, GUIContent.none);
if (GUILayout.Button("Apply and return", new GUILayoutOption[0]))
{
PrefabUtility.ReplacePrefab(PrefabInstance, PrefabUtility.GetPrefabParent(PrefabInstance), ReplacePrefabOptions.ConnectToPrefab);
SceneView.onSceneGUIDelegate -= RenderSceneGUI;
PreviousSceneSetup.OpenSetup();
}
if (GUILayout.Button("Discard changes", new GUILayoutOption[0]))
{
SceneView.onSceneGUIDelegate -= RenderSceneGUI;
PreviousSceneSetup.OpenSetup();
}
EditorGUILayout.EndVertical();
GUILayout.EndArea();
Handles.EndGUI();
}
}
@IRobS
Copy link

IRobS commented May 24, 2020

It seems that DisplayCancelableProgressBar is known to cause these NullReferenceExceptions, although it doesn't seem to do any harm.
I resolved it by adding
EditorGUIUtility.ExitGUI();
After each of the
EditorUtility.ClearProgressBar();

Not very important, but some of your solution is depreciated:
onSceneGUIDelegate
is replaced with
duringSceneGui

The prefabType is replaced with prefabAssetType, and ReplacePrefab -> SaveAsPrefabAssetAndConnect

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment