Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Any way to instantiate a prefab without dirtying the scene?

Discussion in 'Editor Workflows' started by Baste, Sep 20, 2023.

  1. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,179
    We're working on a system where we want to instantiate prefabs with HideFlags.DontSave as a level editor tool.

    Instantiating and then setting HideFlags still dirties the scene, for obvious reasons.
    Setting the prefab's HideFlags, instantiating, and then reverting the prefab's HideFlags still dirties the scene.
    Instantiating the prefab as a child of an object spawned with EditorUtility.CreateGameObjectWithHideFlags still dirties the scene.

    Any idea how to do this properly? The alternative is to spawn everything through CreateGameObjectWithHideFlags, and then copy over components with something like EditorUtility.CopySerialized, but that seems annoying!


    If you care about what I'm trying to do here:
    We've had a bunch of issues over the years with how we handle looking at objects in the game view in edit mode. If we have a camera in the scene, moving that camera around dirties the scene and creates changes in version control that we don't need. It also requires destroying that camera at build/runtime if we ever do mutli-scene things, which we often do.

    The solution I've come up with is essentially this:
    - Spawn the camera prefab(s) that's used at runtime, but with HideFlags.DontSave
    - Make that camera copy the position of the scene view camera, with an optional toggle to turn that off
    - profit

    Now the game view shows the same thing as the scene view camera, just at the exact z-depth and perspective you'll see while playing, without having to move it manually, and without getting version control noise. Great!

    Only problem is that the scene gets dirty, and if we save it, it shows up in version control as "changed" with no changes (so staging it just makes it get marked as unchanged again). This is annoying to say the least! We generally fight a lot against having bogus causes for the scene getting dirty.


    An alternative solution would be if it was possible to clear the dirty flag from code. Then I could check if it was set through scene.isDirty, and then if it wasn't set I could clear it immediately after instantiation.
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,721
    One thing you could try is to set an Undo point before instantiating. Instantiate your prefab but without registering it with the Undo buffer. Then Undo once.

    In theory, this might trick Unity into thinking that the dirty, dirty changes to the scene have been undone, but you purposefully neglected to undo the prefab instantiation.

    I also remember the HideFlags behaviour is somewhat weird. If I remember correctly with just DontSave it behaves differently than with HideAndDontSave - but I can't really recall what was different. Can't hurt to try again with HideAndDontSave.

    If I recall correctly you can still select objects even when hidden, provided that you do so manually via the Selection class or with an Editor tool. For example you could redirect the selection of the Camera or some other bogus object to actually select the hidden game view camera, or make it an EditorTool icon that makes the game view camera active.
     
    LeonhardP likes this.
  3. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,179
    Thanks for the reply!

    I want the object to not be hidden, so our level editors can find it without having to go through a custom workflow. That might change, we'll see.

    For now I've found a very hairy workaround that I really don't like:
    - Instantiate one object for each prefab with EditorUtility.CreateGameObjectWithHideFlags
    - Create a new instance of each component on the prefab
    - Copy over the values from the prefab to the corresponding object with EditorUtility.CopySerialized

    This mostly works, with some caveats:
    - There's a CinemachineVirtualCamera on one of the prefabs, and that immediately instantiates a new "cm" GameObject and hides it when it's instantiated. This dirties the scene. So I hook into CinemachineVirtualCamera.CreatePipelineOverride and have it create the "cm" using CreateGameObjectWithHideFlags(vCam.hideFlags).
    - EditorUtility.CopySerialized on the CinemachineVirtualCamera also transfers the LookAt component, somehow, which also causes a dirty of the scene, for unknown reasons. So for the vcam, I skip CopySerialized and just write the values I want over one-by-one.

    These issues would probably all be fixed by going to Cinemachine 3.0, but I don't have the time or patience for that right now, so this will do. If somebody knows of a way to instantiate prefabs directly without doing all of this, I'm interested.

    A coworker suggested creating an additive, empty scene, spawning into it, setting hide flags, moving over to the new scene, and closing the scene. Will try that at one point, even though it feels like the worst hack yet.