Unity: Creating a day-night cycle

In some games, it is useful to have a day and night cycle. For example, In Mystery Queen, the creatures of the world behave differently depending on the time of day, e.g., Slimey is only active when the sun is up, and goes to sleep at dusk.

Slimey goes to sleep when the sun sets.

In this guide, I’ll discuss how to create a day-night cycle with the lighting system in Unity 2D, so that you can incorporate similar systems in your game! Similar to my previous guide on creating a translucent effect, I will be walking through the steps here, and also providing downloadable Unity packages (Step 0, Steps 1–5) and a Github page. Since we’ll be changing project settings, Step 0 (the initial setup) and later steps will be in separate projects. The projects and code have been created to be easier to understand over efficiency, so you may want to do some optimizations and customizations for your own project.

The screenshots below are from Unity 2019.4, so if you’re using a different version, the menus may not be exactly the same.

Step 0: Setting up the scene in Unity

We’re going to set up a scene in Unity with a single NPC: a wolf. Similar to our last guide, we’re using freely-available sprites from the Liberated Pixel Cup (LPC). In this case, we’re using sprites from the [LPC] Wolf Animation. For the purposes of this guide, we won’t be having any player character, so there won’t be any way for a player to interact with the scenes.

We’ll set up our wolf, Charlie Wolf, with 3 animations: idle, sleep, and asleep. For now, Charlie Wolf will remain in the idle state, where he will dance endlessly. We will allow Charlie Wolf to sleep once we have a day-night cycle.

Charlie Wolf is dancing to music only he can hear.

Step 1: Switching to the Universal Render Pipeline

To set up Unity’s Universal Render Pipeline (URP) to your project, you can refer to this official guide. I had some issues setting it up myself, so I figured that it’ll be good to walk you through the steps I took.

Step 1a: install the Universal Render Pipeline package. You can do so via Window → Package Manager → Universal RP

Install Universal Render Pipeline via the Unity Package Manager.

Step 1b: create a URP Pipeline Asset. You can do so via Create → Rendering → Universal Render Pipeline → Pipeline Asset. Two assets will get created: a pipeline asset and a pipeline asset renderer.

Create a Pipeline Asset for URP.

Step 1c: create a URP 2D Renderer. You can do so via Create → Rendering → Universal Render Pipeline → 2D Renderer.

Create a 2D Renderer for URP.

Step 1d: update the Pipeline Asset to use the 2D Renderer Data asset in its Renderer List.

Update the Pipeline Asset to use the 2D Renderer Data asset.

Step 1e: set the project to use URP. You can do so via Edit → Project Settings → Graphics, and then set “Scriptable Render Pipeline” to the URP Pipeline asset you created in Step 1b.

We’re done! Well, kind of. We still need to create lights for our day-night cycle.

Step 2: Creating a Global Light

Now, we’re going to add a Global Light 2D into our project. We’ll use this global light to change the color of the “sky” (or ambient light) as the day turns to night.

Create a Global Light 2D object in the project hierarchy.

In the Light 2D object’s inspector, you’ll see that the Light Type is set to “Global”, indicating that this is a global light. You can also experiment with other types of light, like directional and point lights!

Inspector for the global light object we just created.

The color of the light indicates how the light will affect the colors of the sprites. The default color is pure white, which means that sprites will appear “normally”. If you set a different color, e.g., RGB (1, 0, 0), then sprites should have a red tint. This is very similar to changing the color of the SpriteRenderer component of a sprite.

However, currently, if you were to change the color of the global light, you’ll notice that nothing happens. That’s because the sprites (Charlie Wolf in this project) hasn’t been set up to use lights yet!

If you have multiple layers for rendering, you can have separate lights for each layer. Alternatively, you can set the global light to affect all layers, by changing the Target Sorting Layers.

Step 3: Updating textures and materials in the scene

To allow our sprites and other objects to use the global light, we’ll need to change their textures and materials. Luckily, URP has a handy menu tool that’ll do that for us.

You can do so via Edit → Render Pipeline → Universal Render Pipeline → 2D Renderer → Upgrade Scene to 2D Renderer (Experimental)

Upgrade the scene to use the 2D Renderer.

Alternatively, you can also upgrade the entire project to use the 2D renderer, depending on your needs.

Once you’ve done so, you can check that the Sprite Renderer in Charlie Wolf (and/or other sprites if you have any) will have their Material updated to “Sprite-Lit-Default”.

The “Sprite-Lit-Default” material should be set for sprites that you want to be affected by the light(s).

As a quick test that everything is working as it should, you can change the color of the global light. You should observe that Charlie Wolf’s color changes as the global light’s color changes.

The disco lights are on, but Charlie Wolf is getting tired of dancing.

Step 4: Creating a Day-Night Cycle

In this step, we’ll be creating the DayNightCycle behavior that changes the global light depending on the time of day. Here are the variables defined in DayNightCycleBehavior:

public float dayLength = 10.0f;
public float nightLength = 10.0f;
public Color dayColor = new Color(1.0f, 1.0f, 1.0f);
public Color nightColor = new Color(0.25f, 0.25f, 0.6f);
private Light2D light2D;public bool daytime { get; private set; }
private float timeRemaining;

dayLength and nightLength define how long day time and night time are respectively, and dayColor and nightColor define the global light’s color during the day and night. Typically, the day color should be pure white, and the night should be some sort of blue.

Light2D is from UnityEngine.Experimental.Rendering.Universal and is the global light component we created in Step 2.

daytime tracks whether it is day or night now, and timeRemaining tracks how long more the current day/night will last.

With these variables (and initializations in Start() that aren’t shown here), we can update the day and night as the game progresses:

void Update()
{
timeRemaining -= Time.deltaTime;
if (timeRemaining <= 0)
{
daytime = !daytime;
if (daytime)
{
light2D.color = dayColor;
timeRemaining = dayLength;
} else
{
light2D.color = nightColor;
timeRemaining = nightLength;
}
}
}

The Update() code reduces the time remaining, and flips day and night if necessary. When that happens, the global light’s color is changed, and timeRemaining is reset.

With a second wind, Charlie Wolf is back to dancing, non-stop?

Step 5: Updating the wolf

Now that we have the day-night cycle, we can update the Charlie Wolf’s behavior. He’s been dancing through the day and night, and rightly deserves some rest.

To do so, we only have to set up the Animator to set the Sleep flag when it’s time for Charlie Wolf to sleep. Since wolves are nocturnal, we’ll let him sleep when it’s day time. Here‘s the code for WolfBehavior:

private Animator animator;
private DayNightCycleBehavior dayNightCycle;
void Start()
{
animator = GetComponent<Animator>();
dayNightCycle = FindObjectOfType<DayNightCycleBehavior>();
}
void Update()
{
// Sleep when it's day time, dance through the night
animator.SetBool("Sleep", dayNightCycle.daytime);
}
Dancing through the night, sleeping in the day.

With that, Charlie Wolf can dance through the night, and get some sleep during the day!

How we did it for Mystery Queen

In Mystery Queen, we used a similar Day-Night Cycle behavior, but we added sunrise and sunset as well. Also, we used some color blending during the transitions between day states (e.g., day → sunset) so that the color transitions are smooth.

In addition, the non-player characters (NPCs) and creatures have finite state machines (FSMs) that transition depending on the time of day. We’ll cover FSMs in another guide. For example, Slimey transitions from the Idle state to the Sleep state when it’s dusk, and transitions back to Idle when it’s day again.

Besides having the NPC behaviors query the time of day, we are also considering setting up an event-based system that the behaviors can subscribe to, so it’ll reduce unnecessary polling.

I hope you enjoyed this guide on setting up a day-night cycle! Stay tuned for more Unity-related guides as we discuss other systems that we developed (and are developing) for Mystery Queen. If you’re interested, you can also read my guide on setting up translucent effects linked below:

Proud mom, roboticist, software engineer.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store