Programmatically setting editor defaults at runtime?

Hey all,
So I’m currently saving some values in a game file to be loaded into individual scenes when they load up, but this is both slightly on the slow side and with my current system, this save file is combined with the user save, which I don’t like for various reasons. I was wondering, is there a way to set the editor default values via script at runtime?

What do you mean by “editor default values”?

2 Likes

Like, when you open up a scene and can see transforms for an object in the inspector, for example. I know how to set these outside of play mode, and how to alter them in play mode, either manually or programmatically. But what I don’t know how to do is how to save these changes in script so that the editor defaults to the changed values.

Alternatively I would settle for a way to programmatically set them while outside of play mode. But for that I’d also need to know how to programmatically load up scenes outside of play mode. I think that’s all I’d need for that method anyway… that and setting the values programmatically outside of play mode, of course.

This is the only part of your post that makes sense so far, and you’d use this: https://docs.unity3d.com/ScriptReference/SceneManagement.EditorSceneManager.html

Otherwise you really need to explain yourself more clearly.

3 Likes

I’m not really sure how to make it any more clear, unfortunately.

When you open up the inspector, objects have a transform. If you change the transform while in edit mode, the changes are retained between both edit mode and play mode. This is what I’m calling “setting the default values”. If you make changes like this in play mode, the changes are not retained.

I need a way to “set the default values” for objects via script, across multiple scenes, which are not necessarily open in the editor at all times. I’ll look into that link but hopefully that clears things up and we’re on the same page.

You’re effectively referring to editor scripting.

When you say ‘set the default values’, 99.99% of us will think of the default values present when you create an instance of an asset in the editor. You’re just talking about modifying assets via code. And it can work pretty much the same outside of playmode as it does in play mode. You just need to get references to said objects and mutate their values. Just outside of play mode you need to tell the Editor you have changed said values to let it know it needs to write them to disk, usually done with UnityEditor.EditorUtility.SetDirty.

2 Likes

After modifying content in a scene as spiney described, you can use EditorSceneManager.SaveScene or a similar save-related EditorSceneManager method to explicitly save the scene content if you don’t want to require any user intervention to save the modifications.
Although… when you say “runtime” do you mean you’re doing this in a game? You mention a game save / user save but also mentioned the editor a number of times so the terminology is of concern here.

1 Like

hopefully you mean how to you get unity to keep values changed during play in the editor

So, basically the idea is I’m generating a map from a bunch of scenes, setting their locations. Right now the way I generate the map is by flagging a bool to generate the map, which - when I enter play mode - then calls a recursive function, loading each scene and setting its location to the appropriate place.

I also have a user save file, which is where I’m currently storing the values of these locations. But I don’t want to keep those in a save file - I want the new default locations to remain in the editor.

I haven’t had a chance to try this method out yet but it does sound promising, as long as we’re all on the same page.

Hmm, so I finally got a chance to look at this and am still having issues.
SetDirty works to make the object dirty - but the scenes unload when the application quits, and I can’t save scenes while in play mode.
So I created a ContextMenu item, in order to run map generation from the editor - but Async scene loads are not supported in the editor, so I’m not sure how to run my map generation without keeping every scene open. Ideally, the scenes would also close after their generation is complete.

Here’s what I’ve got:

    [ContextMenu("Generate Map")]
    void Generate()
    {
        GenerateMap(firstMappingScene, Vector2.zero);
    }
    void GenerateMap(SceneField scene, Vector2 offset)
    {
        allMappedScenes.Add(scene);
        StartCoroutine(LoadSceneForMapGeneration(scene, offset));
    }

private IEnumerator LoadSceneForMapGeneration(SceneField scene, Vector2 offset)
{
     // Start loading the scene
     if (!SceneManager.GetSceneByName(scene).isLoaded)
     {
         AsyncOperation asyncLoadRoom = SceneManager.LoadSceneAsync(scene, LoadSceneMode.Additive);
         // Wait until the level finish loading
         while (!asyncLoadRoom.isDone)
         {
             yield return null;
         }
     }

     Room room = GetRoomFromScene(scene);
     if (room != null)
     {
     //TODO: added to save scene, make sure this works
     //TODO: remove the need to do this in play mode by putting it in the editor
     //this should allow it to save without marking dirty
     room.transform.position += (Vector3)offset;
     EditorUtility.SetDirty(room);
     //TODO: editor save scene here

         room.isMapped = true;
         room.positionOffset = offset;

         foreach (var door in room.doors)
         {
             if (door.connectingScene != "")
             {
                 if (!SceneManager.GetSceneByName(door.connectingScene).isLoaded)
                 {
                     AsyncOperation asyncLoadNextRoom = SceneManager.LoadSceneAsync(door.connectingScene, LoadSceneMode.Additive);
                     // Wait until the level finish loading
                     while (!asyncLoadNextRoom.isDone)
                     {
                         yield return null;
                     }
                 }

                 Room nextRoom = GetRoomFromScene(door.connectingScene);
                 if (nextRoom != null && !nextRoom.isMapped)
                 {
                     Collider2D thisDoor = door.doorCollider;
                     if (door.nextSceneDirection != Calculations.RelationToCollider.Undefined)
                     {
                         bool foundDoor = false;
                         foreach (var nextDoor in nextRoom.doors)
                         {
                             if (nextDoor.doorNumber == door.connectingDoorNumber)
                             {
                                 Collider2D thatDoor = nextDoor.doorCollider;
                                 Vector2 nextOffset = Calculations.GetColliderVectorOffset(thisDoor, thatDoor, door.nextSceneDirection);
                                 foundDoor = true;
                                 GenerateMap(door.connectingScene, nextOffset);
                                 break;
                             }
                         }
                     }
                 }
             }
         }
     }
}

   IEnumerator LoadSceneAsync(SceneField connectedScene)
   {
       // The Application loads the Scene in the background as the current Scene runs.
       // This is particularly good for creating loading screens.
       // You could also load the Scene by using sceneBuildIndex. In this case Scene2 has
       // a sceneBuildIndex of 1 as shown in Build Settings.
       if (!SceneManager.GetSceneByName(connectedScene).isLoaded)
       {
           AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(connectedScene, LoadSceneMode.Additive);
           // Wait until the asynchronous scene fully loads
           while (!asyncLoad.isDone)
           {
               Debug.Log("Still loading... " + asyncLoad.progress);
               yield return null;
           }
           //TODO: these are the previous means of saving/l scene
           //Room thisRoom = GetRoomFromScene(connectedScene);
           //GameManager.instance.LoadObject(thisRoom);

           //thisRoom.transform.position = thisRoom.positionOffset;
           //Debug.Log("Map Data loaded and moved " + connectedScene + " to " + thisRoom.positionOffset);
       }
       else
       {
           Debug.Log(connectedScene + " already loaded");
       }
   }

You can use the editor coroutines package: Unity - Manual: Editor Coroutines

Alternatively - and this would constitute a big change - but you could represent your map generation data as pure data, not baked into scenes. Then at runtime you build the visual representation of your scenes. Thus you can generate data without needing any particular scene open.

2 Likes

Hmm, yeah that would take quite an overhaul. My scenes also contain non-visual data so I don’t really want to do too much runtime generation. Although, I guess it’s not terribly different from scene loading… but definitely a bit much of a project.

I downloaded that package and tried running it but unfortunately still get this error when trying to load the scene using the AsyncOperation:

InvalidOperationException: This can only be used during play mode, please use EditorSceneManager.OpenScene() instead.

Hm. Maybe I can sort of force an async operation using events…

Play Mode: You can use SceneManager.OpenSceneAsync in play mode for normal scenes in the build’s scene list, and EditorSceneManager.LoadSceneAsyncInPlayMode for some special cases.
Edit Mode: You’re meant to use EditorSceneManager.OpenScene which operates synchronously. That should work fine with an editor coroutine outside of play mode. Doing thing in edit-time doesn’t really benefit from async loading anyway.

Right, I meant the async scene load is not supported in Editor mode. The issue being that I need the scenes to be loaded before I can perform actions on them. It looks like there’s a sceneLoaded event but I haven’t figured out how to reshape the code to use it yet.

Edit: ah, it’s not the editor scene manager, just the scene manager. Hm. Oh wait, I just realized I don’t really need it to be async, do I? Okay wow hahaha

Again, you really shouldn’t need async loading in edit mode, asynchrony is only to keep things responsive. Ideally, you wouldn’t be trying to do anything in the editor while something is being generated.

EditorSceneManager.OpenScene should be all you need.

2 Likes

Ha! Got it figured out! Took a bit of finagling with the editor code but I got it :slight_smile: Appreciate your help guys