Search code examples
androidunity-game-engineunityscriptskybox

Why do scripting changes to the Skybox Material not reset when play mode over?


My Unity app animates the scene's Skybox Material Exposure property over time (based on audio). The Material asset in my project file has Exposure=1 (initial value). This works great.

When I play the app inside Unity:

  1. Before play, I select the Material in Project (to see what's happening),
  2. Play the app in Unity,
  3. As soon as app starts, the Material's Exposure is set to a very low value (as expected),
  4. The Skybox Exposure animates as expected (i.e., Skybox is changing to the music),
  5. The Exposure value (in Inspector) does not change after the first change at start (#3), during the play (seems odd),
  6. When I exit play mode, the Exposure (in Inspector) is set to the last value during play (which is not displayed in the Inspector while running).

==> I expected the Material to return to its pre-play values after exiting.

IMPORTANT: If I try this same experiment with say a Sphere radius (no scripting though), and I manually change the sphere's radius while playing, when I exit play mode it resets (as expected) to its original pre-play value.

Clearly, I am missing something very core to Unity. I've read everything I can find & still lost. (Wondering if the issue is default Hideflags on the scene's skybox or something similar.)

Scene controller script that changes skybox.material.exposure each frame:

public class AnimateSkyboxFromAudio : BaseAnimateFromAudio {
    protected override void Start() {
         base.Start ();
    }

    protected override void MyUpdate () {
        float rms = GetAttrByName (AudioAnalyzer.RMS);
        RenderSettings.skybox.SetFloat ("_Exposure", rms * 5);
    }
}

Solution

  • I expected the Material to return to its pre-play values after exiting.

    RenderSettings.skybox works differently than other GameObjects with materials. It returns shared material reference unlike a unique material returned by Renderer.material when its material property is accessed.

    So, you must make a back-up of the material before modifying it. When you click stop, assign the backup material back to RenderSettings.skybox. Both of these can be done in the Start() and the OnDisable() function. You make a backup material by calling the Material class constructor with the Material parameter.(Material newMat = new Material(RenderSettings.skybox)).

    If you only are changing the _Exposure property then you don't even need to make the material backup. Just backup the _Exposure property with RenderSettings.skybox.GetFloat("_Exposure"); then re-assign it when stop is clicked.

    The Exposure value (in Inspector) does not change after the first change at start (#3), during the play (seems odd),

    Call DynamicGI.UpdateEnvironment(); after you change the material values.

    Example of changing the Skybox _Exposure property with a UI Slider:

    public Slider slider;
    Material backUpMaterial;
    
    void Start()
    {
        slider.minValue = 0f;
        slider.maxValue = 3f;
        slider.value = 1.3f;
    
    
        //Get Material Backup
        backUpMaterial = makeSkyboxBackUp();
    
        slider.onValueChanged.AddListener(delegate { sliderCallBack(slider.value); });
    }
    
    void sliderCallBack(float value)
    {
        RenderSettings.skybox.SetFloat("_Exposure", value);
        DynamicGI.UpdateEnvironment();
    }
    
    Material makeSkyboxBackUp()
    {
        return new Material(RenderSettings.skybox);
    }
    
    
    //Restore skybox material back to the Default values
    void OnDisable()
    {
        RenderSettings.skybox = backUpMaterial;
        DynamicGI.UpdateEnvironment();
        slider.onValueChanged.RemoveListener(delegate { sliderCallBack(slider.value); });
    }
    

    Since you have already modified the original copy of the skybox settings, I suggest you reset it by setting the _Exposure property to 1.3 which is the default value, then try the solution above.