Skip to content

Instantly share code, notes, and snippets.

@giacomelli
Last active April 16, 2024 12:11
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save giacomelli/4122c117c927d5c370dd562e619129e6 to your computer and use it in GitHub Desktop.
Save giacomelli/4122c117c927d5c370dd562e619129e6 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityToolbarExtender;
/// <summary>
/// #unitytips: Scene Selection Toolbar - http://diegogiacomelli.com.br/unitytips-scene-selection-toolbar
/// </summary>
[InitializeOnLoad]
public static class SceneSelectionToolbar
{
static List<SceneInfo> _scenes;
static SceneInfo _sceneOpened;
static int _selectedIndex;
static string[] _displayedOptions;
static SceneSelectionToolbar()
{
LoadFromPlayerPrefs();
ToolbarExtender.LeftToolbarGUI.Add(OnToolbarGUI);
EditorSceneManager.sceneOpened += HandleSceneOpened;
}
static void OnToolbarGUI()
{
GUILayout.FlexibleSpace();
_selectedIndex = EditorGUILayout.Popup(_selectedIndex, _displayedOptions); ;
GUI.enabled = _selectedIndex == 0;
if (GUILayout.Button("+"))
AddScene(_sceneOpened);
GUI.enabled = _selectedIndex > 0;
if (GUILayout.Button("-"))
RemoveScene(_sceneOpened);
GUI.enabled = true;
if (GUI.changed && _selectedIndex > 0 && _scenes.Count > _selectedIndex - 1)
EditorSceneManager.OpenScene(_scenes[_selectedIndex - 1].Path);
}
static void RefreshDisplayedOptions()
{
_displayedOptions = new string[_scenes.Count + 1];
_displayedOptions[0] = "Click on '+' to add current scene";
for (int i = 0; i < _scenes.Count; i++)
_displayedOptions[i + 1] = _scenes[i].Name;
}
static void HandleSceneOpened(Scene scene, OpenSceneMode mode) => SetOpenedScene(scene);
static void SetOpenedScene(SceneInfo scene)
{
if (scene == null || string.IsNullOrEmpty(scene.Path))
return;
for (int i = 0; i < _scenes.Count; i++)
{
if (_scenes[i].Path == scene.Path)
{
_sceneOpened = _scenes[i];
_selectedIndex = i + 1;
SaveToPlayerPrefs(true);
return;
}
}
_sceneOpened = scene;
_selectedIndex = 0;
SaveToPlayerPrefs(true);
}
static void SetOpenedScene(Scene scene) => SetOpenedScene(new SceneInfo(scene));
static void AddScene(SceneInfo scene)
{
if (scene == null)
return;
if (_scenes.Any(s => s.Path == scene.Path))
RemoveScene(scene);
_scenes.Add(scene);
_selectedIndex = _scenes.Count;
SetOpenedScene(scene);
RefreshDisplayedOptions();
SaveToPlayerPrefs();
}
static void RemoveScene(SceneInfo scene)
{
_scenes.Remove(scene);
_selectedIndex = 0;
RefreshDisplayedOptions();
SaveToPlayerPrefs();
}
static void SaveToPlayerPrefs(bool onlyLatestOpenedScene = false)
{
if (!onlyLatestOpenedScene)
{
var serialized = string.Join(";", _scenes.Where(s => !string.IsNullOrEmpty(s.Path)).Select(s => s.Path));
SetPref("SceneSelectionToolbar.Scenes", serialized);
}
if(_sceneOpened != null)
SetPref("SceneSelectionToolbar.LatestOpenedScene", _sceneOpened.Path);
}
static void LoadFromPlayerPrefs()
{
var serialized = GetPref("SceneSelectionToolbar.Scenes");
_scenes = serialized.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(s => new SceneInfo(s)).ToList();
if (_scenes == null)
_scenes = new List<SceneInfo>();
serialized = GetPref("SceneSelectionToolbar.LatestOpenedScene");
if (!string.IsNullOrEmpty(serialized))
SetOpenedScene(new SceneInfo(serialized));
RefreshDisplayedOptions();
}
static void SetPref(string name, string value) => EditorPrefs.SetString($"{Application.productName}_{name}", value);
static string GetPref(string name) => EditorPrefs.GetString($"{Application.productName}_{name}");
[Serializable]
class SceneInfo
{
public SceneInfo() { }
public SceneInfo(Scene scene)
{
Name = scene.name;
Path = scene.path;
}
public SceneInfo(string path)
{
Name = System.IO.Path.GetFileNameWithoutExtension(path);
Path = path;
}
public string Name;
public string Path;
}
}
@danieldownes
Copy link

Ah, there is a problem with this, if you add a scene. Then move that scene to a different folder, it then fails. If I try to remove the scene entry from the dropdown and then readd, after a editor reload it then does not restore the new location of the scene and fails again. This makes opening that scene from the dropdown impossible. :(

@PasterLak
Copy link

There is an critical bug in this code - when switching scenes, everything that was done in the current scene is not saved

Small fix for the line 43-44:

if (GUI.changed && _selectedIndex > 0 && _scenes.Count > _selectedIndex - 1)
{
    if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
    { 
       EditorSceneManager.OpenScene(_scenes[_selectedIndex - 1].Path);
    }
}

@danieldownes
Copy link

Thanks @PasterLak .. what a coincidence as I was just looking at the scene move issue I described above. I have a fix for it so will fork and add your suggestion too as the code I've added is around your addition.

@danieldownes
Copy link

Please see my fork which includes fixes for the two above issues:
https://gist.github.com/danieldownes/d9cef8b2c9136cf95be150357e74c7e3

..I would Pull-Request back if/when its possible with a gist.

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