Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How to Link Scenes in the Inspector

Discussion in 'Editor & General Support' started by Oshroth, Jan 31, 2016.

  1. Oshroth

    Oshroth

    Joined:
    Apr 28, 2014
    Posts:
    99
    This script lets you link scenes in scripts using a property drawer. Based on Network Manager code

    Place this script in Editor script location
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [CustomPropertyDrawer (typeof (SceneAttribute))]
    5. public class SceneDrawer : PropertyDrawer {
    6.  
    7.     public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
    8.      
    9.         if (property.propertyType == SerializedPropertyType.String) {
    10.             var sceneObject = GetSceneObject(property.stringValue);
    11.             var scene = EditorGUI.ObjectField(position, label, sceneObject, typeof(SceneAsset), true);
    12.             if (scene == null) {
    13.                 property.stringValue = "";
    14.             } else if (scene.name != property.stringValue) {
    15.                 var sceneObj = GetSceneObject(scene.name);
    16.                 if (sceneObj == null) {
    17.                     Debug.LogWarning("The scene " + scene.name + " cannot be used. To use this scene add it to the build settings for the project");
    18.                 } else {
    19.                     property.stringValue = scene.name;
    20.                 }
    21.             }
    22.         }
    23.         else
    24.             EditorGUI.LabelField (position, label.text, "Use [Scene] with strings.");
    25.     }
    26.     protected SceneAsset GetSceneObject(string sceneObjectName) {
    27.         if (string.IsNullOrEmpty(sceneObjectName)) {
    28.             return null;
    29.         }
    30.  
    31.         foreach (var editorScene in EditorBuildSettings.scenes) {
    32.             if (editorScene.path.IndexOf(sceneObjectName) != -1) {
    33.                 return AssetDatabase.LoadAssetAtPath(editorScene.path, typeof(SceneAsset)) as SceneAsset;
    34.             }
    35.         }
    36.         Debug.LogWarning("Scene [" + sceneObjectName + "] cannot be used. Add this scene to the 'Scenes in the Build' in build settings.");
    37.         return null;
    38.     }
    39. }
    40.  
    Place this script in normal script location
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class SceneAttribute : PropertyAttribute {
    4. }
    Example of how to use [Scene] Attribute to access scenes
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Networking;
    3. using UnityEngine.SceneManagement;
    4.  
    5.  
    6. public class SceneSelector : MonoBehaviour
    7. {
    8.     [Scene]
    9.     public string newScene;
    10.  
    11.     public void ChangeScene() {
    12.         SceneManager.LoadScene(newScene, LoadSceneMode.Single);
    13.     }
    14.  
    15.     public void ChangeNetworkScene() {
    16.         if(NetworkServer.active && NetworkManager.singleton != null) {
    17.             NetworkManager.singleton.ServerChangeScene(newScene);
    18.         }
    19.     }
    20. }
     

    Attached Files:

  2. blinz117

    blinz117

    Joined:
    Feb 5, 2015
    Posts:
    2
    Thanks, this is really useful! Exactly what I was looking for.
     
  3. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    I think this is a useful concept. What I personally need is a field that will allow me to drag assets into it, and only the asset's path will be stored. This would probably be used in conjunction with a [Path] attribute or something similar.

    The motivation for this is that referencing the asset itself makes it automatically get loaded with the scene. This is not always desirable. Instead, i'd like to store only the path for the asset, and later load it using Resources.Load<T>
     
  4. JohannesMP

    JohannesMP

    Joined:
    Nov 4, 2016
    Posts:
    21
    Just storing a path string (as the SceneAsset documentation suggests) is inadequate for production, because if the scene file is renamed, moved, or otherwise looked at funny, you've lost your reference. We can use an Object reference to the SceneAsset but we no longer have access to AssetDatabase to look up the path when not in editor.

    To reliably handle both file renames and runtime paths you need two serialized pieces of data (Object reference in editor, string path at runtime). You can create a SceneReference object that uses ISerializationCallbackReceiver to ensure that the stored path is always valid based on the specified SceneAsset Object.

    As an example, here is my approach to this problem: https://gist.github.com/JohannesMP/ec7d3f0bcf167dab3d0d3bb480e0e07b



    Features:
    • Custom PropertyDrawer that displays the current Build Settings status, including BuildIndex and convenient buttons for managing it with destructive action confirmation dialogues.
    • If (and only if) the serialized Object reference is invalid but path is still valid (for example if someone merged incorrectly) will recover object using path.
    • Buttons collapse to smaller text if full text cannot be displayed.
    • Includes detailed tooltips and respects Version Control if EditorBuildSettings.asset is not checked out (tested with Perforce)
     
    Last edited: Aug 14, 2018
    mendelicus, star4z, Lukafice and 8 others like this.
  5. winxalex

    winxalex

    Joined:
    Jun 29, 2014
    Posts:
    166

    Code (CSharp):
    1. #if UNITY_EDITOR
    2.     // What we use in editor to select the scene
    3.     [SerializeField] private Object sceneAsset = null;
    4.     bool IsValidSceneAsset
    5.     {
    6.         get
    7.         {
    8.             if (sceneAsset == null)
    9.                 return false;
    10.             return sceneAsset.GetType().Equals(typeof(SceneAsset));
    11.         }
    12.     }
    13. #endif
    Why don't u use privateObject SceneAsset here?
     
    JohannesMP likes this.
  6. lmraddmb

    lmraddmb

    Joined:
    Jun 26, 2019
    Posts:
    3
    You're a freakin angel, bro. Thanks so much.