Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 14 Next capture
2021 2022 2023
2 captures
13 Jun 22 - 14 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
13
Question by kromenak · Apr 20, 2012 at 11:33 PM · sceneinspectorcustom-editor

Inspector Field for Scene Asset

Pretty straightforward question: is there a way to create an inspector field for either a Scene asset or an array of Scene assets?

I'm trying to create a custom editor window that allows our designers to easily modify our level packs and the levels in them. It would be ideal if, rather than having to add scenes manually to the Build Settings list, I could provide an inspector field to slot in a Scene (.unity3d) asset. This way, I could throw warnings when a level isn't assigned a scene file, and ensure that all the scenes needed for the game are included when we do a build.

So far, this seems much harder than necessary because I can't find an appropriate asset type that corresponds to the scene asset type.

Thanks!

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

8 Replies

· Add your reply
  • Sort: 
avatar image
15
Best Answer

Answer by kromenak · Apr 24, 2012 at 05:18 PM

Found a workable solution through experimentation, thought I would share:

There is no class exposed in Unity's API to create an inspector reference to a Scene, but it turns out that a Scene does inherit from Object, so it is possible to have an Object reference and then slot in a Scene asset:

 public Object[] mySceneAssets;

The only problem with this solution is that you can't really enforce that the list contain only Scene objects at compile time; there is nothing stopping someone on the team from inserting a reference to some other asset entirely. Still, it is better than nothing, and you can do some runtime checks to make sure it works.

With the scene asset reference, you can use the AssetDatabase object to get path information, and manually add the scene data to the build settings list of scenes.

 List<EditorBuilderSettingsScene> scenes = new List<EditorBuilderSettingsScene>();

 //have some sort of for loop to cycle through scenes...
 string pathToScene = AssetDatabase.GetAssetPath(mySceneAssets[i]);
 EditorBuildSettingsScene scene = new EditorBuildSettingsScene(pathToScene, true);
 scenes.Add(scene);

 //later on...
 EditorBuildSettings.scenes = scenes.ToArray();
Comment
Add comment · Show 2 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image JGriffith · Aug 17, 2015 at 06:31 PM 2
Share

The scene object references become null for me in builds.

avatar image kromenak · Aug 26, 2015 at 08:05 PM 2
Share

@JGriffith, yeah, for whatever reason, Scenes are editor-only assets, so they disappear when you actually create a build.

However, that doesn't have to be a problem, necessarily. During your build process, you can still access the scene objects and do some processing on them. When building your final build, you can add scenes to to the BuildSettings scene list (like above) or save the name of the scene to a string array (which will be available at runtime).

But as you say, when you try to run a final build file, these scene references won't be available. You should probably just consider them to be "editor only".

avatar image
35

Answer by glitchers · Jun 17, 2016 at 10:10 AM

I have come across this issue a few times and I don't want to make a custom inspector for every class I need a scene reference I need so I have made this script.

You can just use it like public SceneField myScene and drag a Scene to the Inspector and use Application.loadLevel( myScene ) or SceneManager.LoadScene( myScene ) and it will work. It will be serialized and work in builds. Include this script in your project and never worry about it again.

 using UnityEngine;

 #if UNITY_EDITOR
 using UnityEditor;
 #endif

 [System.Serializable]
 public class SceneField
 {
     [SerializeField]
     private Object m_SceneAsset;

     [SerializeField]
     private string m_SceneName = "";
     public string SceneName
     {
         get { return m_SceneName; }
     }

     // makes it work with the existing Unity methods (LoadLevel/LoadScene)
     public static implicit operator string( SceneField sceneField )
     {
         return sceneField.SceneName;
     }
 }

 #if UNITY_EDITOR
 [CustomPropertyDrawer(typeof(SceneField))]
 public class SceneFieldPropertyDrawer : PropertyDrawer 
 {
     public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label)
     {
         EditorGUI.BeginProperty(_position, GUIContent.none, _property);
         SerializedProperty sceneAsset = _property.FindPropertyRelative("m_SceneAsset");
         SerializedProperty sceneName = _property.FindPropertyRelative("m_SceneName");
         _position = EditorGUI.PrefixLabel(_position, GUIUtility.GetControlID(FocusType.Passive), _label);
         if (sceneAsset != null)
         {
             sceneAsset.objectReferenceValue = EditorGUI.ObjectField(_position, sceneAsset.objectReferenceValue, typeof(SceneAsset), false); 

             if( sceneAsset.objectReferenceValue != null )
             {
                 sceneName.stringValue = (sceneAsset.objectReferenceValue as SceneAsset).name;
             }
         }
         EditorGUI.EndProperty( );
     }
 }
 #endif
Comment
Add comment · Show 14 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Halfbiscuit · Aug 19, 2016 at 09:24 AM 4
Share

Thanks for this it really helped us.

Just a slight addition:

         if (sceneAsset != null)
         {
             EditorGUI.BeginChangeCheck();
 
             Object value = EditorGUI.ObjectField(_position, sceneAsset.objectReferenceValue, typeof(SceneAsset), false);
             if (EditorGUI.EndChangeCheck() )
             {
                 sceneAsset.objectReferenceValue = value;
                 if (sceneAsset.objectReferenceValue != null)
                 {
                     sceneName.stringValue = (sceneAsset.objectReferenceValue as SceneAsset).name;
                 }
             }
          
         }

This allows for multi-editing (and stops multi-editing from causing issues).

avatar image thunderdev321 · Mar 06, 2017 at 06:07 PM 0
Share

How would you go about using your SceneField in a custom editor window ? I tried using ObjectField but I can't get it to work :(

avatar image codestage · Jul 05, 2017 at 04:09 PM 8
Share

Here is a slightly modified version, including @Halfbiscuit addition, but using scene paths ins$$anonymous$$d of names (since there are few scenes with same name are possible in the project):

 using System;
 using UnityEngine;
 using Object = UnityEngine.Object;
 #if UNITY_EDITOR
 using UnityEditor;
 #endif
 
 namespace Utilities
 {
     [System.Serializable]
     public class SceneField
     {
         [SerializeField] private Object sceneAsset;
         [SerializeField] private string sceneName = "";
 
         public string SceneName
         {
             get { return sceneName; }
         }
 
         // makes it work with the existing Unity methods (LoadLevel/LoadScene)
         public static implicit operator string(SceneField sceneField)
         {
             return sceneField.SceneName;
         }
     }
 
 #if UNITY_EDITOR
     [CustomPropertyDrawer(typeof(SceneField))]
     public class SceneFieldPropertyDrawer : PropertyDrawer
     {
         public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
         {
             EditorGUI.BeginProperty(position, GUIContent.none, property);
             var sceneAsset = property.FindPropertyRelative("sceneAsset");
             var sceneName = property.FindPropertyRelative("sceneName");
             position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
             if (sceneAsset != null)
             {
                 EditorGUI.BeginChangeCheck();
                 var value = EditorGUI.ObjectField(position, sceneAsset.objectReferenceValue, typeof(SceneAsset), false);
                 if (EditorGUI.EndChangeCheck())
                 {
                     sceneAsset.objectReferenceValue = value;
                     if (sceneAsset.objectReferenceValue != null)
                     {
                         var scenePath = AssetDatabase.GetAssetPath(sceneAsset.objectReferenceValue);
                         var assetsIndex = scenePath.IndexOf("Assets", StringComparison.Ordinal) + 7;
                         var extensionIndex = scenePath.LastIndexOf(".unity", StringComparison.Ordinal);
                         scenePath = scenePath.Substring(assetsIndex, extensionIndex - assetsIndex);
                         sceneName.stringValue = scenePath;
                     }
                 }
             }
             EditorGUI.EndProperty();
         }
     }
 #endif
 }
avatar image Eldoir codestage · Jan 24, 2018 at 03:16 PM 0
Share

Thanks mate! Since you packed up everything that's good on this page, your code is the most functional :D

avatar image tarahugger codestage · Feb 09, 2018 at 09:39 AM 0
Share

This was exactly what i needed, thanks!

avatar image $$anonymous$$ codestage · Apr 02, 2018 at 01:47 AM 0
Share

Thank you so much.

Show more comments
avatar image LaneFox · Apr 05, 2018 at 07:16 PM 2
Share

FYI this basically uses a backing field and serializes/stores the string name for later use so if you change the name of the SceneAsset file then it will not update the SceneField name string, thus breaking it.

So if you use this make sure you either don't rename your scenes after hooking them into SceneFields, or prepare to update the fields by drag-n-dropping them again to refresh the serialized backer string.

avatar image JohannesMP · Aug 14, 2018 at 05:37 AM 5
Share

I used a similar approach, but with ISerializationCallbackReceiver to guarantee that the string path is valid: https://gist.github.com/Johannes$$anonymous$$P/ec7d3f0bcf167dab3d0d3bb480e0e07b

I Including a more designer-friendly PropertyDrawer that provides at-a-glance build status and some convenient actions (with destructive action confirmation dialogues), and made sure that it respects version control checkout status of EditorBuildSettings.Asset, such as when using Perforce: alt text
When in Editor, only the Object reference is ever used, and only during serialization is the asset path string set, guaranteeing that they will never be out of sync. This means this approach fully supports moving/rena$$anonymous$$g scene assets.

The only time the path is ever used in-editor is if the object reference is invalid during serialization (such as after a failed scene/prefab merge), in which case the Object reference is recovered from the path if it actually references a SceneAsset. For builds only the path is stored, and is guaranteed to have been serialized from the Object reference.

avatar image codestage JohannesMP · Mar 15, 2019 at 01:57 PM 0
Share

Thanks, @Johannes$$anonymous$$P, like your solution a lot!

Show more comments
avatar image
10

Answer by JohannesMP · Aug 13, 2018 at 10:36 PM

Just storing a path string (as the SceneAsset documentation suggests) is inadequate for production, because if the scene file is renamed or moved 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


alt text


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 build settings is not checked out (tested with Perforce)

alt text

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image codestage · Mar 15, 2019 at 01:58 PM 0
Share

Thanks, this is a nice complete solution.

avatar image
4

Answer by FriesB_DAEStudios · Jun 25, 2014 at 12:42 PM

UnityEngine.SceneAsset is not recognized by the compiler, but you can check if the Object.ToString() ends with "(UnityEngine.SceneAsset)". I used a workaround to create a level manager which creates the order in which levels will be played.

This is how I managed it:

 public string LevelName;
 private Object _levelScene;
 public Object LevelScene
 {
     get {return _levelScene;}
     set
     {
         //Only set when the value is changed
         if(_levelScene != value && value != null)
         {
             string name = value.ToString();
             if(name.Contains(" (UnityEngine.SceneAsset)"))
             {
                 _levelScene = value;
                 LevelName = name.Substring(0,name.IndexOf(" (UnityEngine.SceneAsset)"));
             }
         }
     }
 }

And in editor window:

 LevelScene = EditorGUILayout.ObjectField("Scene",LevelScene,typeof(Object),false);

Later, I can load the correct level using the LevelName.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by 1337GameDev · Apr 20, 2012 at 11:39 PM

I'm assuming you could make a script or such and have a public variable so it can be assigned in the editor window. I am unsure ifna similar thing applies to asset packages.

Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image kromenak · Apr 20, 2012 at 11:56 PM 0
Share

I would also assume this, but I'm having trouble finding the class to use in this instance.

To make a transform field, I use a "public Transform myTransform;" field.

To make an AudioClip field, I use a "public AudioClip myClip;" field.

To make a scene file field, I have no idea what class I should be using to create the inspector field. I'm fearing that there is no such class exposed.

avatar image fwalker · Mar 11, 2013 at 05:32 PM 0
Share

I am also looking for the answer to this. Are there any updates on this. And a related question. If Application.LoadLevel("aLevel") does not take any paths, how does it avoid name clashes if you have levels with the same name in different folders?

thanks

avatar image 1337GameDev · Mar 11, 2013 at 06:59 PM 0
Share

It is stated above that scenes inherit from Object. You could try using this in a public variable.

For the scene name problem, unity might grab the first name it finds (or random) so avoid sharing names of scenes. It's similar to Java's arraylist object and the find method. It returns the first occurrence. If the arraylist is unwonted you don't know which is returned. Same principle here. Just name them differently, and come up with a na$$anonymous$$g convention for your scene assets.

avatar image fwalker · Mar 11, 2013 at 07:35 PM 0
Share

Thanks GameDev. I get the part about using an Object. But then how in the world do I load that object as a scene at runtime?. I can use Application.LoadLevel() which takes a string. And the only way I see of getting the LevelName from the Object is to call AssetDatabase.GetAssetPath which is an Editor class. So it will not work at runtime. This can't possibly be this hard :(

avatar image kromenak · Mar 15, 2013 at 05:14 AM 1
Share

You can probably achieve what you're looking for by creating a custom inspector. In the custom inspector, you can trim and save the scene names for use at runtime. For example, something like this seems to work:

 using UnityEngine;
 using UnityEditor;
 
 [CustomEditor(typeof(YourClass))]
 public class YourCustomInspector : Editor 
 {
         public override void OnInspectorGUI()
     {
         DrawDefaultInspector();
         
         YourClass comp = (YourClass)target;
         if(comp != null)
         {
             if(comp.scenes.Length > 0)
             {
                 comp.sceneNames = new string[comp.scenes.Length];
                 for(int i = 0; i < comp.scenes.Length; i++)
                 {
                     string scenePath = AssetDatabase.GetAssetPath(comp.scenes[i]);
                     scenePath = scenePath.Substring(scenePath.LastIndexOf('/') + 1);
                     scenePath = scenePath.Substring(0, scenePath.LastIndexOf('.'));
                     
                     comp.sceneNames[i] = scenePath;
                 }
             }
         }
     }
 }

Since this editor class creates an array of scene names for you, you can then use those scene names at runtime to load the scenes. I think this is kind of a dumb solution, but I don't see a better way to do it, unfortunately :-\

  • 1
  • 2
  • ›

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

21 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

How do I change inspector values of other game objects using a custom Editor Window 0 Answers

Set Level inquiry/data from the inspector 1 Answer

How do I change inspector values of other game objects using a custom Editor Window 0 Answers

CustomEditor : DrawDefaultInspector for Class 1 Answer

Moving a inspector slider to add objects to the scene 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges