├── LICENSE ├── README.md ├── SceneSelector.cs └── images ├── image1.png ├── image2.png └── image3.png /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity Scene Picker 2 | 3 | Load all project scenes in runtime - useful for testing and prototyping on mobile - single file to rule them all 4 | 5 | If you want to buy me a beer 6 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/wad1m) 7 | 8 | ## Screenshots 9 | 10 | | Interface | Scene picker example | Active scene from list | 11 | |------------|-------------|-------------| 12 | | | | | 13 | | Includes a custom editor for the script | Mouse / touch based picker with scroll view | Press "M☰NU" button from any scene to return to scene selection view | 14 | 15 | 16 | ## Installation 17 | 18 | Download or copy `SceneSelector.cs` and place it anywhere in the assets folder 19 | 20 | ## Usage 21 | 22 | 1. Create an empty scene with no light or camera 23 | 2. Create empty game object and attach the script to it 24 | 3. Play / Build and run ! 25 | 26 | Optional : Attach a header image for the menu. 27 | 28 | ## How is it done ? 29 | 30 | Using IMGUI and screen dpi to adjust the UI scale and draw the buttons on the screen. A main scene will be chosen by this script as the entry point ( first scene in the build order ) - later it will fetch the list of all the build in scenes and one on a button touch / click. I included few tricks to handle scrolling on mobile since OnGUI ScrollView doesn't support mobile scroll. 31 | Once the script gets enabled it will fetch the list of all the scenes and will update them using the custom editor UI ( in unity inspector ) - those values are serialized hence its possible to retain anychanges during play mode. The header image is optional and will not take empty space if the header filed is left empty. 32 | On script start it will attach itself to the do not destroy scene and will be kept alive between all scenes. 33 | Note it will automaticlly create a camera if it doesn't exists during play mode, the camera is set to have a black background and no lights are needed for the UI part. On app start / every time a scene is changed - event system will be added if none exists. 34 | 35 | ## Demo 36 | 37 | In the screen shots above I cloned the Unity AR Foundation samples, created a new empty scene with only 1 empty game object and attached the screen to it. Using "Find and Add all scenes" and made sure all of them are enabled 38 | 39 | On the 3rd image there is a preview of the back button that will be present in any loaded scene from the main menu. 40 | 41 | Fun fact : A red white blue flag is an international signalling code for `Tango` -> `Keep clear of me, I am engaged in pair trawling` -> `Pair trawling is a fishing activity carried out by two boats, with one towing each warp. As the mouth of the net is kept open by the lateral pull of the individual vessels, otter boards are not required` ( source : wikipedia ) , ( Flag colors was randomly generated from the AR Foundation sample scene ) 42 | 43 | ## License 44 | 45 | unlicense :: https://unlicense.org/ 46 | 47 | -------------------------------------------------------------------------------- /SceneSelector.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using UnityEngine.SceneManagement; 6 | using UnityEngine.EventSystems; 7 | #if UNITY_EDITOR 8 | using UnityEditor; 9 | #endif 10 | 11 | public class SceneSelector : MonoBehaviour 12 | { 13 | /// TODO: add headers array in between scenes that will be rendered as large labels in between the buttons 14 | /// quick way : scenes.Add( "title::My Header Text Value" ); 15 | /// 16 | [SerializeField, HideInInspector] internal Texture header = null; 17 | 18 | string main = ""; 19 | 20 | List scenes; 21 | 22 | private void Awake() 23 | { 24 | if (scenes != null) scenes.Clear(); 25 | else scenes = new List(); 26 | 27 | int overflow = 100, count = 0; 28 | 29 | while( overflow -- > 0 ) 30 | { 31 | try 32 | { 33 | var path = SceneUtility.GetScenePathByBuildIndex( count ++ ); 34 | if (string.IsNullOrEmpty(path)) break; 35 | //int start = path.LastIndexOf("/"); 36 | //Debug.Log(start + " " + path.Length + " " + path); 37 | //string name = path.Substring(start, path.Length - start); 38 | if( string.IsNullOrEmpty(main) ) main = path; 39 | scenes.Add( path ); 40 | } 41 | catch( System.Exception e ) { e.ToString(); break; } 42 | } 43 | } 44 | 45 | void Start() 46 | { 47 | DontDestroyOnLoad(gameObject); 48 | 49 | //SceneManager.activeSceneChanged += (_, o) => 50 | //{ 51 | // changed = true; 52 | // TryAddES(); 53 | //}; 54 | 55 | // SceneManager.sceneUnloaded += _ => changed = true; 56 | 57 | TryAddES(); 58 | 59 | FindObjectsOfType().ToList().ForEach(c => c.enabled = false); 60 | 61 | BlackCamera(); 62 | } 63 | 64 | void TryAddES() 65 | { 66 | if (FindObjectOfType() == null) 67 | 68 | new GameObject().AddComponent(); 69 | } 70 | 71 | private void OnGUI() 72 | { 73 | screen = new Rect(0, 0, Screen.width, Screen.height); 74 | 75 | scale = Screen.dpi / 96; 76 | 77 | if ( sBtn == null ) InitStyles(); 78 | 79 | if (!changed) 80 | { 81 | LoadingGUI(); 82 | return; 83 | } 84 | 85 | if (swap && Input.touchCount > 0) HandleTouchScroll(); 86 | 87 | if (MenuGUI()) return; 88 | 89 | if (header != null) HeaderGUI(); 90 | 91 | ButtonsGUI(); 92 | } 93 | 94 | Rect screen = Rect.zero; 95 | 96 | bool changed = true; 97 | 98 | float scale = 1; 99 | 100 | Vector2 scroll = Vector2.zero; 101 | 102 | bool swap = true; 103 | 104 | Vector2 point = Vector2.zero; 105 | 106 | bool scrolling = false; 107 | 108 | GUIStyle sScroll, sBtn, sBtnBack, sLoadText; 109 | 110 | float texW = 0, texH = 0; 111 | 112 | void InitStyles() 113 | { 114 | sBtn = new GUIStyle(GUI.skin.button); 115 | sBtn.fontSize = Mathf.FloorToInt(14 * scale); 116 | sBtn.wordWrap = true; 117 | sBtn.normal.background = Texture2D.grayTexture; 118 | sBtn.border = new RectOffset(1, 1, 1, 1); 119 | var tex2D = new Texture2D(3, 3); 120 | for (var x = 0; x < 3; ++x) for (var y = 0; y < 3; ++y) tex2D.SetPixel(x, y, Color.white); 121 | tex2D.SetPixel(sBtn.border.left, sBtn.border.top, Color.grey); tex2D.filterMode = FilterMode.Point; tex2D.Apply(); 122 | sBtn.hover.background = sBtn.active.background = sBtn.focused.background = tex2D; 123 | 124 | sBtnBack = new GUIStyle(sBtn); 125 | sBtnBack.fontSize = Mathf.FloorToInt(12 * scale); 126 | 127 | sScroll = new GUIStyle(GUI.skin.scrollView); 128 | int pad = Mathf.FloorToInt(10 * scale); 129 | sScroll.padding = new RectOffset(pad, Mathf.FloorToInt(2 * scale), pad, pad); 130 | 131 | var scrollBarTex = new Texture2D(1, 1); 132 | scrollBarTex.SetPixel(0, 0, new Color(0.1f, 0.1f, 0.1f)); 133 | scrollBarTex.Apply(); 134 | 135 | GUI.skin.verticalScrollbarThumb.fixedWidth = 14f * scale; 136 | GUI.skin.verticalScrollbar.fixedWidth = 14f * scale; 137 | GUI.skin.verticalScrollbarThumb.normal.background = scrollBarTex; 138 | 139 | sLoadText = new GUIStyle(GUI.skin.label); 140 | sLoadText.fontStyle = FontStyle.Bold; 141 | sLoadText.fontSize = Mathf.FloorToInt(24 * scale); 142 | sLoadText.alignment = TextAnchor.MiddleCenter; 143 | } 144 | 145 | void BlackCamera() 146 | { 147 | var c = new GameObject().AddComponent(); 148 | c.clearFlags = CameraClearFlags.Color; 149 | c.backgroundColor = Color.black; 150 | } 151 | 152 | IEnumerator StopTouch() 153 | { 154 | yield return new WaitForSeconds(0.15f); 155 | scrolling = false; 156 | } 157 | 158 | void LoadingGUI() 159 | { 160 | string s = $"{Mathf.RoundToInt(load_progress*98f)}%"; 161 | GUI.Label(screen, "Loading " + s , sLoadText); 162 | } 163 | 164 | void HandleTouchScroll() 165 | { 166 | var touch = Input.GetTouch(0); 167 | 168 | switch (touch.phase) 169 | { 170 | case TouchPhase.Began: 171 | point = touch.position; 172 | scrolling = true; 173 | break; 174 | case TouchPhase.Moved: 175 | scroll.y += touch.deltaPosition.y / 4f; 176 | scrolling = true; 177 | break; 178 | case TouchPhase.Canceled: 179 | case TouchPhase.Ended: 180 | bool click = (touch.position - point).magnitude < 5f * scale; 181 | if (click) scrolling = false; 182 | else StartCoroutine(StopTouch()); 183 | break; 184 | } 185 | } 186 | 187 | bool MenuGUI() 188 | { 189 | var scene = SceneManager.GetActiveScene(); 190 | 191 | if (!(!swap && scene.path != main)) return false; 192 | 193 | var rect = new Rect(0, 0, 50 * scale, 25 * scale); 194 | 195 | rect.x = screen.width - rect.width * 1.2f; 196 | rect.y = (screen.height - rect.height) * 0.5f; 197 | 198 | if (GUI.Button(rect, "M☰NU", sBtnBack)) 199 | { 200 | //SceneManager.UnloadSceneAsync( scene ); // not supported ... 201 | //changed = false; 202 | 203 | scene.GetRootGameObjects().ToList().ForEach(go => 204 | { 205 | if (Application.isEditor) DestroyImmediate(go); 206 | Destroy(go); 207 | }); 208 | 209 | BlackCamera(); 210 | 211 | changed = true; 212 | swap = true; 213 | } 214 | 215 | return true; 216 | } 217 | 218 | void HeaderGUI() 219 | { 220 | var vertical = screen.width < screen.height; 221 | var ratio = header.width / (float)header.height; 222 | 223 | texH = Mathf.Max(screen.height * 0.22f, header.height); 224 | texW = texH * ratio; 225 | Rect rect = Rect.zero; 226 | 227 | if (texW > screen.width * 0.9f) 228 | { 229 | texW = screen.width * 0.85f; 230 | texH = texW / ratio; 231 | } 232 | 233 | GUILayout.Space((vertical ? 10 : 2) * scale); 234 | 235 | using (new GUILayout.HorizontalScope()) 236 | { 237 | GUILayout.FlexibleSpace(); 238 | GUI.enabled = false; 239 | GUI.color = Color.black; 240 | GUILayout.Button(GUIContent.none, GUILayout.Width(texW), GUILayout.Height(texH)); 241 | GUI.enabled = true; 242 | GUI.color = Color.white; 243 | rect = GUILayoutUtility.GetLastRect(); 244 | GUILayout.FlexibleSpace(); 245 | } 246 | 247 | GUILayout.Space((vertical ? 10 : 2) * scale); 248 | 249 | GUI.DrawTexture(rect, header); 250 | } 251 | 252 | float load_progress = 0f; 253 | 254 | void SceneLoading(AsyncOperation op ) 255 | { 256 | load_progress = op.progress; 257 | 258 | if( op.isDone ) 259 | { 260 | changed = true; 261 | TryAddES(); 262 | sBtn = null; 263 | } 264 | } 265 | 266 | void ButtonsGUI() 267 | { 268 | var oScroll = new GUILayoutOption[] { 269 | GUILayout.ExpandWidth( true ), 270 | GUILayout.ExpandHeight( true ), 271 | GUILayout.MaxWidth( screen.width ), 272 | GUILayout.MaxHeight( screen.height - ( header == null ? 0 : texH ) ) 273 | }; 274 | 275 | using (new GUILayout.VerticalScope(oScroll)) 276 | { 277 | using (new GUILayout.HorizontalScope()) 278 | { 279 | using (var scope = new GUILayout.ScrollViewScope(scroll, sScroll, oScroll)) 280 | { 281 | scroll = scope.scrollPosition; 282 | 283 | foreach (var scene in scenes ) 284 | { 285 | if ( scene == main ) continue; 286 | 287 | GUILayout.Space(6 * scale); 288 | 289 | var index = scene.LastIndexOf("/"); 290 | if (index < 0) index = scene.LastIndexOf("\\"); 291 | if (index < 0) index = 0; 292 | 293 | var label = scene.Substring(index == 0 ? index : index + 1, scene.Length - 7 - index); 294 | 295 | if (GUILayout.Button(label, sBtn) && !scrolling) 296 | { 297 | swap = false; 298 | changed = false; 299 | 300 | SceneManager.LoadSceneAsync(scene, LoadSceneMode.Single).completed += SceneLoading; 301 | } 302 | } 303 | 304 | GUILayout.Space(6 * scale); 305 | } 306 | 307 | // GUILayout.Space( 10 * scale ); 308 | } 309 | } 310 | } 311 | } 312 | 313 | #if UNITY_EDITOR 314 | 315 | [CustomEditor(typeof(SceneSelector))] 316 | public class SceneSelectorEditor : Editor 317 | { 318 | //[InitializeOnLoadMethod] 319 | //static void Hook() => AssemblyReloadEvents.beforeAssemblyReload += UpdateSceneList; 320 | //static void UpdateSceneList() 321 | //{ 322 | // AssemblyReloadEvents.beforeAssemblyReload -= UpdateSceneList; 323 | // if( staticTarget != null ) staticTarget.scenes = GetEnabledScenes(); 324 | //} 325 | 326 | static SceneSelector staticTarget = null; 327 | 328 | 329 | static bool foldout = true; 330 | static bool arrange = false; 331 | 332 | SerializedProperty header; 333 | 334 | private void OnEnable() 335 | { 336 | if (Application.isPlaying) return; 337 | 338 | staticTarget = ( SceneSelector ) target; 339 | 340 | header = serializedObject.FindProperty("header"); 341 | 342 | PrependFirstScene(); 343 | 344 | //UpdateTargetScenes(); 345 | //UpdateTargetLoader(); 346 | } 347 | 348 | #region GUI 349 | 350 | public override void OnInspectorGUI() 351 | { 352 | if (Application.isPlaying) 353 | { 354 | PlayModeGUI(); 355 | return; 356 | } 357 | 358 | InitStyles(); 359 | 360 | GUILayout.Space(5); 361 | 362 | serializedObject.Update(); 363 | EditorGUILayout.PropertyField(header); 364 | serializedObject.ApplyModifiedProperties(); 365 | 366 | GUILayout.Space(5); 367 | 368 | if (GUILayout.Button("Find and Add all scenes")) 369 | { 370 | IncludeAllScenesToBuild(); 371 | 372 | PrependFirstScene(); 373 | 374 | foldout = true; 375 | 376 | return; 377 | } 378 | 379 | GUILayout.Space(5); 380 | 381 | var current = GetCurrentScenePath(); 382 | 383 | var current_scene = AssetDatabase.LoadAssetAtPath(current); 384 | 385 | bool allowSceneObjects = !EditorUtility.IsPersistent(current_scene); 386 | 387 | using (new GUILayout.HorizontalScope()) 388 | { 389 | GUILayout.Label("Main Scene", EditorStyles.boldLabel); 390 | 391 | EditorGUILayout.ObjectField(current_scene, typeof(SceneAsset), allowSceneObjects); 392 | } 393 | 394 | EditorGUILayout.HelpBox("Current scene is the main scene that holds the navigation options", MessageType.None); 395 | 396 | GUILayout.Space(10); 397 | 398 | foldout = EditorGUILayout.Foldout(foldout, "Edit Scenes", true); 399 | 400 | if (!foldout) return; 401 | 402 | GUILayout.Space(5); 403 | GUILayout.Label(GUIContent.none, GUI.skin.horizontalSlider); 404 | GUILayout.Space(20); 405 | 406 | using (new GUILayout.HorizontalScope()) 407 | { 408 | if (GUILayout.Button("Enable All", EditorStyles.miniButtonLeft)) 409 | { 410 | SetBuildActiveAll(true); 411 | } 412 | if (GUILayout.Button("Disable All", EditorStyles.miniButtonRight)) 413 | { 414 | SetBuildActiveAll(false); 415 | ToggleBuildActive(0, true); 416 | } 417 | } 418 | 419 | GUILayout.Space(5); 420 | 421 | arrange = EditorGUILayout.ToggleLeft("Rearrange", arrange); 422 | 423 | GUILayout.Space(5); 424 | 425 | using (new GUILayout.VerticalScope(EditorStyles.helpBox)) SceneListGUI(); 426 | } 427 | 428 | void PlayModeGUI() 429 | { 430 | EditorGUILayout.HelpBox("Component changes locked during playmode", MessageType.Warning); 431 | } 432 | 433 | void SceneListGUI() 434 | { 435 | var current = GetCurrentScenePath(); 436 | var scenes = EditorBuildSettings.scenes; 437 | var lenght = scenes.Length; 438 | 439 | for (var i = 0; i < lenght; ++i) 440 | { 441 | var scene = scenes[i]; 442 | var path = scene.path; 443 | 444 | if (path == current) continue; 445 | 446 | var sceneAsset = AssetDatabase.LoadAssetAtPath(path); 447 | 448 | using (new GUILayout.HorizontalScope()) 449 | { 450 | bool buildCheck = EditorGUILayout.ToggleLeft(GUIContent.none, scene.enabled, GUILayout.Width(15)); 451 | 452 | if (buildCheck != scene.enabled) ToggleBuildActive(i, buildCheck); 453 | 454 | GUILayout.Space(10); 455 | 456 | // GUILayout.TextArea( sceneAsset.name ); 457 | EditorGUILayout.ObjectField(sceneAsset, typeof(SceneAsset), false); 458 | //if(GUILayout.Button( "Select", EditorStyles.miniButtonLeft, GUILayout.Width( 50 ) )) 459 | // EditorGUIUtility.PingObject( sceneAsset ); 460 | 461 | if (arrange) 462 | { 463 | using (new EditorGUI.DisabledGroupScope(i < 2 )) // 1st one at 0 is main 464 | if (GUILayout.Button("▲", sBtnUp, GUILayout.Width(25))) 465 | SwapBuildSceneIndexes(i, i - 1); 466 | 467 | using (new EditorGUI.DisabledGroupScope(i == lenght - 1)) 468 | if (GUILayout.Button("▼", sBtnDown, GUILayout.Width(25))) 469 | SwapBuildSceneIndexes(i, i + 1); 470 | } 471 | 472 | if (GUILayout.Button("X", sBtnDel, GUILayout.Width(25))) 473 | { 474 | RemoveScene(path); 475 | //UpdateTargetScenes(); 476 | return; 477 | } 478 | } 479 | } 480 | } 481 | 482 | #endregion 483 | 484 | #region Build Scenes - Methods 485 | 486 | void PrependFirstScene() 487 | { 488 | var build = GetBuildScenes(); 489 | 490 | var current = GetCurrentScenePath(); 491 | 492 | var index = build.ToList().IndexOf(current); 493 | 494 | // Auto add current scene to build settings if missing 495 | 496 | if (index == -1) Prepend(current); 497 | 498 | // Swap if main scene is not first 499 | 500 | else if (index > 0) SwapBuildSceneIndexes(0, index); 501 | } 502 | 503 | void SwapBuildSceneIndexes(int a, int b) 504 | { 505 | var tmp = EditorBuildSettings.scenes.ToList(); 506 | var t = tmp[a]; tmp[a] = tmp[b]; tmp[b] = t; 507 | EditorBuildSettings.scenes = tmp.ToArray(); 508 | } 509 | 510 | void ToggleBuildActive(int build_index, bool active) 511 | { 512 | var list = EditorBuildSettings.scenes.ToList(); 513 | list[build_index].enabled = active; 514 | EditorBuildSettings.scenes = list.ToArray(); 515 | } 516 | 517 | void SetBuildActiveAll(bool active) 518 | { 519 | var list = EditorBuildSettings.scenes.ToList(); 520 | list.ForEach(scene => scene.enabled = active); 521 | EditorBuildSettings.scenes = list.ToArray(); 522 | } 523 | 524 | void Prepend(string path) 525 | { 526 | EditorBuildSettings.scenes = (new EditorBuildSettingsScene[] { 527 | new EditorBuildSettingsScene( path, true ) 528 | }) 529 | .Concat(EditorBuildSettings.scenes.ToList()) 530 | .ToArray(); 531 | } 532 | 533 | static List GetEnabledScenes() => EditorBuildSettings.scenes 534 | 535 | .Where(s => s.enabled).Select(s => s.path).ToList(); 536 | 537 | //void UpdateTargetScenes() => ((SceneSelector)target).scenes = GetEnabledScenes(); 538 | 539 | //void UpdateTargetLoader() => ((SceneSelector)target).loader = GetCurrentScenePath(); 540 | 541 | void RemoveScene(string path) 542 | { 543 | var scenes = EditorBuildSettings.scenes.ToList(); 544 | scenes.Remove(scenes.Where(x => x.path == path).First()); 545 | EditorBuildSettings.scenes = scenes.ToArray(); 546 | } 547 | 548 | string GetCurrentScenePath() => SceneManager.GetActiveScene().path; 549 | 550 | string[] GetAllScenes() 551 | { 552 | // source : https://github.com/Demkeys/SceneLoaderWindow/blob/master/SceneLoaderWindow.cs#L59 553 | return AssetDatabase.FindAssets("t:scene") 554 | .Select(x => AssetDatabase.GUIDToAssetPath(x)) 555 | .ToArray(); 556 | } 557 | 558 | string[] GetBuildScenes() 559 | { 560 | return EditorBuildSettings.scenes.Select(s => s.path).ToArray(); 561 | } 562 | 563 | void IncludeAllScenesToBuild() 564 | { 565 | var build = GetBuildScenes(); 566 | 567 | EditorBuildSettings.scenes = GetAllScenes() 568 | .Select(x => new EditorBuildSettingsScene(x, build.Contains(x))) 569 | .ToArray(); 570 | } 571 | 572 | #endregion 573 | 574 | #region GUI Styles 575 | 576 | GUIStyle sBtnUp, sBtnDown, sBtnDel; 577 | 578 | void InitStyles() 579 | { 580 | if (sBtnUp != null) return; 581 | sBtnUp = new GUIStyle(EditorStyles.miniButtonLeft); 582 | sBtnUp.fontSize -= 2; 583 | sBtnDown = new GUIStyle(EditorStyles.miniButtonMid); 584 | sBtnDown.fontSize -= 2; 585 | sBtnDel = new GUIStyle(EditorStyles.miniButtonRight); 586 | sBtnDel.fontStyle = FontStyle.Bold; 587 | } 588 | 589 | #endregion 590 | } 591 | 592 | #endif 593 | -------------------------------------------------------------------------------- /images/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nukadelic/Unity-Scene-Picker/79c4a63f1fce8593a1dacd33e4bd722e6dc47381/images/image1.png -------------------------------------------------------------------------------- /images/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nukadelic/Unity-Scene-Picker/79c4a63f1fce8593a1dacd33e4bd722e6dc47381/images/image2.png -------------------------------------------------------------------------------- /images/image3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nukadelic/Unity-Scene-Picker/79c4a63f1fce8593a1dacd33e4bd722e6dc47381/images/image3.png --------------------------------------------------------------------------------