0
\$\begingroup\$

I'm currently working on a 2D game, and I was wondering if anyone around here has experience/tips in achieving this sort of day/night transition of the sky?

I've done some reading, and some people do it as a shader and change gradient values in the shader; others swap out planes. I've not done anything similar before, thus my question here.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Sounds like you know two viable ways to do this already. Have you tried implementing either one? Did you get stuck somewhere along the way that we can help with? \$\endgroup\$
    – DMGregory
    Nov 12 '18 at 3:21
3
\$\begingroup\$

For my small needs i only needed to shift the color of my sprites. through those two scripts.

Cycler : singleton, will go through all the registered sprites and change their color

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;

public class DNCycler : MonoBehaviour {
    public static DNCycler Handler { get; private set; }

[Header ("Settings:")]

[Tooltip ("Wether or not the system should change the colors of the world.\nIf set to false, the screen will only darken, and colors will remain the same.\nDefault: True")]
public bool colored;

[Tooltip ("The amount of time in seconds between each non animated sprite's color update.")]
/// <summary>
/// The amount of time it takes for non animated sprites and mic to update their colors.
/// </summary>
[Range(0, 5)]public float staticUpdateFreq;

[Tooltip ("The starting cycle.\n0 = day\n1 = dusk\n2 = night\n3 = dawn")]
/// <summary>
/// The starting cycle.
/// </summary>
[Range(0, 3)]public int startingCycle; 

[Tooltip ("The delay for when the Day and Night cycle begins. 0 to disable delay.")]
/// <summary>
/// The amount of time for the day night system to start updating colors.
/// </summary>
public float cycleStartDelay;

[Tooltip ("The amount of time in seconds it takes for each day night cycle to pass.")]
/// <summary>
/// The amount of time it takes for each individual cycle.
/// </summary>
public float cycleTime;
float cycleUpdate;

/// <summary>
/// The lists to hold all the renderers.
/// </summary>
List<SpriteRenderer> AnimatedSprites, StaticSprites;
List<Renderer> MiscRenderers;   // Uncomment this and line 147 - 151 and use this list for misc mesh renderers.

[Header ("Color settings")]

/// <summary>
/// The current world color.
/// </summary>
Color mainColor; 

[Tooltip ("The color of the concurrent cycle\nNote: The alpha value corresponds to the overall darkness of the screen." +
    " The alpha will reset to 255 during runtime. This is normal.\nHigher alpha value = darker screen.")]
/// <summary>
/// The colors that the system cycles through.
/// </summary>
public Color day, dusk, night, dawn;

/// <summary>
/// The current cycle that the system is on.
/// </summary>
int cycle = 0;

/// <summary>
/// A time variable used for color lerping.
/// </summary>
float t = 0;

/// <summary>
/// Properly starts the color cycling.
/// </summary>
bool started;

/// <summary>
/// Used for the darkening of the screen.
/// </summary>
Image screenDark;
Color scrnColor, scrnDay, scrnDusk, scrnNight, scrnDawn;

// Use this for initialization
void Start () {
    // Sets the main DayNight handler.
    if (Handler != null && Handler != this) {
        Destroy (gameObject);
    } else if (Handler == null) {
        Handler = this;
    }

    // Sets the proper cycle duration for each of the 4 phases
    cycleUpdate = cycleTime / 4;

    // Creates new lists to store all the renderer variables.
    AnimatedSprites = new List<SpriteRenderer> ();
    StaticSprites = new List<SpriteRenderer> ();
    MiscRenderers = new List<Renderer> ();

    // Creates the proper colors for the screen darkening effect.
    scrnDay = new Color (0, 0, 0, day.a);
    scrnDusk = new Color (0, 0, 0, dusk.a);
    scrnNight = new Color (0, 0, 0, night.a);
    scrnDawn = new Color (0, 0, 0, dawn.a);

    // Resets the default colors' alpha to 255.
    day = new Color (day.r, day.g, day.b, 1);
    dusk = new Color (dusk.r, dusk.g, dusk.b, 1);
    night = new Color (night.r, night.g, night.b, 1);
    dawn = new Color (dawn.r, dawn.g, dawn.b, 1);

    // Gets the image needed for the darkening effect.
    screenDark = GetComponentInChildren<Image> ();

    // Jumps to the specified starting cycle.
    JumpToCycle (startingCycle);

    // Starts the delay and the static updates.
    StartCoroutine ("StartDelay");
    InvokeRepeating ("UpdateStatic", cycleStartDelay, staticUpdateFreq);
}

/// <summary>
/// Delays the start.
/// </summary>
/// <returns>The delay.</returns>
IEnumerator StartDelay () {
    yield return new WaitForSeconds (cycleStartDelay);
    started = true;
}

// Update is called once per frame
void Update () {
    if (started && colored) {
        UpdateMainColor ();
        UpdateAnimated ();
    } else if (started && !colored) {
        UpdateNoColor ();
    }
}

/// <summary>
/// Updates the main color to correspond to the current cycle.
/// </summary>
void UpdateMainColor () {
    switch (cycle) {

    case 0:
        mainColor = Color.Lerp (day, dusk, t);
        scrnColor = Color.Lerp (scrnDay, scrnDusk, t);
        break;

    case 1:
        mainColor = Color.Lerp (dusk, night, t);
        scrnColor = Color.Lerp (scrnDusk, scrnNight, t);
        break;

    case 2:
        mainColor = Color.Lerp (night, dawn, t);
        scrnColor = Color.Lerp (scrnNight, scrnDawn, t);
        break;

    case 3:
        mainColor = Color.Lerp (dawn, day, t);
        scrnColor = Color.Lerp (scrnDawn, scrnDay, t);
        break;
    }

    // Resets t variable and increments the cycle.
    if (t < 1) {
        t += Time.deltaTime / cycleUpdate;
    } else {
        t = 0;

        if (cycle < 3) {
            cycle++;
        } else {
            cycle = 0;
        }
    }
}

/// <summary>
/// Updates the main color to correspond to the current cycle when colored option is turned off.
/// </summary>
void UpdateNoColor () {
    switch (cycle) {

    case 0:
        scrnColor = Color.Lerp (scrnDay, scrnDusk, t);
        break;

    case 1:
        scrnColor = Color.Lerp (scrnDusk, scrnNight, t);
        break;

    case 2:
        scrnColor = Color.Lerp (scrnNight, scrnDawn, t);
        break;

    case 3:
        scrnColor = Color.Lerp (scrnDawn, scrnDay, t);
        break;
    }

    // Resets t variable and increments the cycle.
    if (t < 1) {
        t += Time.deltaTime / cycleUpdate;
    } else {
        t = 0;

        if (cycle < 3) {
            cycle++;
        } else {
            cycle = 0;
        }
    }
}

/// <summary>
/// Updates the color for all animated sprites. This has to be called every
/// frame, otherwise the sprites will flash.
/// </summary>
void UpdateAnimated () {
    for (int i = 0; i < AnimatedSprites.Count; i++) {
        if (AnimatedSprites [i].isVisible) {
            AnimatedSprites [i].color = mainColor;
        }
    }
}

/// <summary>
/// Updates static sprites, as well as misc renderers and the ground if used.
/// </summary>
void UpdateStatic () {
    if (colored) {
        for (int i = 0; i < StaticSprites.Count; i++) {
            if (StaticSprites [i].isVisible) {
                StaticSprites [i].color = mainColor;
            }
        }

        for (int i = 0; i < MiscRenderers.Count; i++) {
            if (MiscRenderers [i].isVisible) {
                MiscRenderers [i].material.color = mainColor;
            }
        }
    }

    screenDark.color = scrnColor;
}

/// <summary>
/// Jumps to selected cycle.
/// 0 = day /
/// 1 = dusk /
/// 2 = night /
/// 3 = dawn
/// </summary>
/// <param name="cycle">The cycle to jump to.</param>
public void JumpToCycle (int selCycle) {

    // Error incase the selected cycle doesn't exist.
    if (selCycle < 0 || selCycle > 3) {
        Debug.LogError ("WHOOPSIE DAISY! Looks like you tried to make the day night handler jump to a cycle that doesn't exist." +
            "\nThe JumpToCycle only accepts values from 0 to 3." +
            "\n0 = day" +
            "\n1 = dusk" +
            "\n2 = night" +
            "\n3 = dawn");
    }

    cycle = selCycle;

    switch (cycle) {

    case 0:
        mainColor = day;
        scrnColor = scrnDay;
        break;

    case 1:
        mainColor = dusk;
        scrnColor = scrnDusk;
        break;

    case 2:
        mainColor = night;
        scrnColor = scrnNight;
        break;

    case 3:
        mainColor = dawn;
        scrnColor = scrnDawn;
        break;
    }

    // Updates the static colors to match that of the new cycle selected.
    UpdateStatic ();
}

/// <summary>
/// Registers a sprite renderer to the day night system.
/// </summary>
/// <param name="render">The sprite renderer to register.</param>
/// <param name="animated">Whether or not the sprite is animated.</param>
public void RegRenderer (SpriteRenderer render, bool animated) {
    if (animated) {
        AnimatedSprites.Add (render);
    } else {
        StaticSprites.Add (render);
    }
}

/// <summary>
/// Registers a miscellaneous renderer to the day night system.
/// </summary>
/// <param name="render">The misc renderer to register.</param>
public void RegRenderer (Renderer render) {
    MiscRenderers.Add (render);
}

/// <summary>
/// Removes a sprite renderer from the registry.
/// </summary>
/// <param name="render">The rsprite renderer to delete from the registry.</param>
/// <param name="animated">Whether or not the sprite was animated.</param>
public void DelRenderer (SpriteRenderer render, bool animated) {
    if (animated) {
        AnimatedSprites.Remove (render);
    } else {
        StaticSprites.Remove (render);
    }
}

/// <summary>
/// Removes a miscellaneous renderer from the registry.
/// </summary>
/// <param name="render">The misc renderer to delete from the registry.</param>
public void DelRenderer (Renderer render) {
    MiscRenderers.Remove (render);
}

/// <summary>
/// Returns the current cycle color.
/// </summary>
public Color GetColor () {
    return mainColor;
}

/// <summary>
/// Returns the time value. Used in the shadow handler, but could be useful for other things.
/// </summary>
public float GetTime () {
    return t;
}

/// <summary>
/// Returns the current cycle.
/// </summary>
public int GetCycle () {
    return cycle;
}
}

Register: Attached to sprite gameObjects, subscribe/unsubscribe them to the cycler:

public class RegisterDN : MonoBehaviour {
/// <summary>
/// The renderer on this object
/// </summary>
private SpriteRenderer spriteRend; //------------} These are the variables you would set to 
private Renderer miscRend; //--------------------} public if you keep getting the below errors.

/// <summary>
/// Whether or not the sprite is animated.
/// </summary>
bool animated;

/// <summary>
/// Finds a renderer attached to this object and register it to the day night handler.
/// </summary>
void Start () {
    // Tries to get a sprite renderer if one is not set manually.
    if (spriteRend == null) {
        spriteRend = GetComponent<SpriteRenderer> ();
    }
    // Tries to get a misc renderer if one is not set manually.
    if (miscRend == null) {
        miscRend = GetComponent<Renderer> ();
    }

    // If a sprite is present, checks if it is animated and registers it to the registry.
    if (spriteRend != null) {
        Animator anim = GetComponent<Animator> ();
        if (anim != null) {
            animated = true;
        } else {
            animated = false;
        }

        DNCycler.Handler.RegRenderer (spriteRend, animated);
    }

    // If a sprite is not present and a misc renderer is found, registers that to the registry
    if (miscRend != null && spriteRend == null) {
        DNCycler.Handler.RegRenderer (miscRend);
    }

    // An error in case the script fails to register any renderers.
    if (spriteRend == null && miscRend == null) {
        Debug.LogError ("OOPS! DayNightRegister error in object (" + this.gameObject.name + "): Could not register any renderers." +
            "\nAre you sure there is a renderer attached to this object?" +
            "\nIf this problem persists, and you have a renderer attached to this object. Try setting the 'Rend variables in this script " +
            "\nto public, and setting the variables manually through the inspector." +
            "\nNote: You do not need to set these for all objects. Only (" + this.gameObject.name + ") would need to be set manually.");
    }
}

void OnDestroy () {
    // Delets the renderer from the registry.
    if (spriteRend != null) {
        DNCycler.Handler.DelRenderer (spriteRend, animated);
    }
    if (miscRend != null) {
        DNCycler.Handler.DelRenderer (miscRend);
    }

    // An error in case the script fails to delete any renderers from the registry.
    if (spriteRend == null && miscRend == null) {
        Debug.LogError ("THERE IT GOES AGAIN! DayNightRegister error in object (" + this.gameObject.name + "): Could not delete any renderers from the registry." +
            "\nAre you sure there is a renderer attached to this object?" +
            "\nIf this problem persists, and you have a renderer attached to this object. Try setting the 'Rend variables in this script " +
            "\nto public, and setting the variables manually through the inspector." +
            "\nNote: You do not need to set these for all objects. Only (" + this.gameObject.name + ") would need to be set manually.");
    }
}

}

But beware, doing so you will lose Batching, it might not be production ready.

\$\endgroup\$
1
  • \$\begingroup\$ Is it possible to get an example of how to use your code? because I'm new to this and I don't understand well how to use it. \$\endgroup\$ Jan 22 '19 at 5:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.