Skip to content

Instantly share code, notes, and snippets.

@dron247
Last active May 3, 2017 07:05
Show Gist options
  • Select an option

  • Save dron247/f44da7e51158cd45b3f3d8a025df5919 to your computer and use it in GitHub Desktop.

Select an option

Save dron247/f44da7e51158cd45b3f3d8a025df5919 to your computer and use it in GitHub Desktop.
Circular scene switch utility for Unity 3D
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SceneNavigator : EditorWindow {
private string mCurrentScene = string.Empty;
private bool mIsDirty;
private IList<string> mScenesList;
[MenuItem("Window/Scene switcher")]
public static void ShowLevelNavigator() {
var window = GetWindow<SceneNavigator>();
var minSize = window.minSize;
minSize.y = 35;
window.minSize = minSize;
}
private void OnDisable() {
if (mScenesList != null) mScenesList.Clear();
mScenesList = null;
}
private void OnEnable() {
titleContent.text = "Scene switcher";
var allAssets = AssetDatabase.GetAllAssetPaths();
mScenesList = allAssets.Where(a => a.EndsWith(".unity")).OrderBy(x => x, new SceneNameComparer()).ToList();
mCurrentScene = CleanupSceneName(SceneManager.GetActiveScene().name);
}
Vector2 mScrollPos;
private void OnGUI() {
if (mIsDirty) {
mCurrentScene = CleanupSceneName(SceneManager.GetActiveScene().name);
mIsDirty = false;
}
EGUI.Vertical(() => {
EGUI.SimpleScroll(ref mScrollPos, scroll => {
GUILayout.Label("Active: " + mCurrentScene + Environment.NewLine);
foreach (var scene in mScenesList) {
EGUI.Button(scene, DirectOpenScene);
}
});
EGUI.Horizontal(() => {
EGUI.Button("<-", caption => ChangeScene(Direction.Back));
EGUI.Button("->", caption => ChangeScene(Direction.Forward));
});
});
}
/// <summary>
/// Scene change method
/// </summary>
/// <param name="direction">Forward or backwards</param>
private void ChangeScene(Direction direction) {
if (mCurrentScene == string.Empty) return;
var currentScene = mCurrentScene;
if (currentScene.IndexOf("/", StringComparison.Ordinal) != -1 || currentScene.Contains(".unity"))
currentScene = CleanupSceneName(currentScene);
var index = FindIndex(mScenesList, currentScene);
switch (direction) {
case Direction.Back:
if (index <= 0) index = mScenesList.Count;
index--;
break;
case Direction.Forward:
if (index >= mScenesList.Count - 1) index = -1;
index++;
break;
default:
index = 0;
break;
}
EditorSceneManager.OpenScene(mScenesList[index]);
mIsDirty = true;
}
private void DirectOpenScene(string sceneName) {
var index = FindIndex(mScenesList, sceneName);
EditorSceneManager.OpenScene(mScenesList[index]);
mIsDirty = true;
}
/// <summary>
/// Cleans scene name from file system artifacts
/// </summary>
/// <param name="sceneName">Name of scene in assets</param>
/// <returns>Name of scene which can be used for loading scene in editor</returns>
private static string CleanupSceneName(string sceneName) {
return Path.GetFileNameWithoutExtension(sceneName);
}
/// <summary>
/// Finds index of scene in scene collection by its name
/// </summary>
/// <param name="collection">collection of scenes</param>
/// <param name="query">part of scene name</param>
/// <returns>more or less accurate scene index in collection</returns>
public static int FindIndex(IList<string> collection, string query) {
for (var i = 0; i < collection.Count(); i++) if (collection.ElementAt(i).Contains(query)) return i;
return -1;
}
/// <summary>
/// Represents a direction of scene switching
/// </summary>
private enum Direction {
Back = -1,
Forward = 1
}
/// <summary>
/// Simple dsl wrapper for easier editor layout building
/// </summary>
private static class EGUI {
/// <summary>
/// Creates horizontal editor gui layout
/// </summary>
/// <param name="content">here you can place all content</param>
public static void Horizontal(Action content) {
using (var h = new EditorGUILayout.HorizontalScope()) {
if (content != null) content.Invoke();
}
}
/// <summary>
/// Creates vertical editor gui layout
/// </summary>
/// <param name="content">here you can place your content</param>
public static void Vertical(Action content) {
using (var v = new EditorGUILayout.VerticalScope()) {
if (content != null) content.Invoke();
}
}
/// <summary>
/// Do not use it outside, should be tested
/// </summary>
/// <param name="content"></param>
public static void SimpleScroll(ref Vector2 position, Action<EditorGUILayout.ScrollViewScope> content) {
using (var scrollView = new EditorGUILayout.ScrollViewScope(position, false, false, null)) {
position = scrollView.scrollPosition;
if (content != null) content.Invoke(scrollView);
}
}
/// <summary>
/// Creates a button
/// </summary>
/// <param name="caption">Button text</param>
/// <param name="handler">Button click handler</param>
public static void Button(string caption, Action<string> handler) {
if (GUILayout.Button(caption)) if (handler != null) handler.Invoke(caption);
}
}
/// <summary>
/// Numbered scenes names comparer
/// </summary>
private class SceneNameComparer : IComparer<string> {
private readonly Regex mRegex = new Regex(@"\d+");
public int Compare(string x, string y) {
var cnx = ContainedNumber(x);
var cny = ContainedNumber(y);
if (cnx > cny) return 1;
if (cnx < cny) return -1;
return 0;
}
private int ContainedNumber(string source) {
var match = mRegex.Match(source);
int val;
return int.TryParse(match.Value, out val) ? val : -1;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment