Super Simple Day Night Cycle20 Jan 2018
A Day/Night cycle can be a bit of a double edged sword. On the plus side it gives you the freedom to demarcate events into time periods — “they mostly come at night. Mostly…” — and on the downside you have to make the environment look good in a range of different lighting setups. So it’s important to have some easy controls.
I’ve flip-flopped on whether to include a day/night cycle in my new project for a while. I’m not at the point where the game is looking good in any setup, so making things harder from the start might not be the best decision. But, I do want to have night-time specific events, and having drowned myself in Breath Of The Wild over the last few months it’s clear that having a cycle, even if simple, does add to the sense of “place”. The player gets to see the world evolve, even in small ways, over time, and that’s good.
Before I begin explaining my simple implementation, let me point you at this series of videos from Kleiner Baer. He’s implemented an awesome Day/Night cycle that not only takes into account the long/lat of your game-world, but also the calendar seasons. I spent a day implementing it when I first considered adding Day/Night to my game, and it’s great, but not quite what I wanted…
So what does my system need to do?
- I’ll need to be able to set the time of day, for obvious reasons.
- I’ll need to be able to get the time of day, as the Player will no doubt need it in a UI somewhere.
- I want control over the length of my shadows. This means doing more than a simple slow, continuous rotation of the Sun and Moon. I want short mid-day shadows, and longer shadows at dawn/dusk, that project at specific angles. I’m trying flatter the top-down view of the world.
- I don’t want black shadows, so I’m going to have to use a Sky Light to help lift them up, which means I’ll need to continuously update both the intensity of the Sky Light, and it’s colour.
- I’ll need the same control for the colour and intensity of the Sun and Moon…
Because I’m a top down game I don’t need:
- The Sun or Moon to be visible in the sky, or move across the sky correctly.
This simplifies things.
My controller therefore needs: two directional lights (Sun and Moon), a Sky Light to help with the shadows, and a value to track the time.
The first thing to do is decide how to handle the time. I don’t want to be fiddling around with minutes / seconds — why do the maths when you can avoid it? — so given I only want hours I can track this with a single float, that works as a 24 hour clock:
- 0.9f == 9pm
- 1.2f == Noon
- 2.1f == 9pm
Updating the time is simple:
fCurrentGameTime += DeltaTime * (2.4f / fLengthOfDayInSeconds);
which I can do in my DayNightController’s Tick function. Bob’s your mother’s brother.
(If I want minutes, say for the UI, I can get them by multiplying fCurrentGameTime by 10, and then pulling out the fractional part of the resulting float. This is the percentage of an “hour”, so:
(fFract*100.0f) * 0.59f
will give me a screen-printable value, and I’ll just ignore seconds…)
So the main thing about this implementation is having control over colour and light intensity. I could just lerp between known-good values, say for morning, noon and night, but a far better way would be to use Curves.
UE4, handily, has an editable curve system for float, vector and colour values — exactly what’s needed here — and one of their big benefits is that you can edit them and see your changes reflected during PIE. This is perfect for me, as I’ll be fiddling with these curves until the day the game is released.
The Curve editor is fairly basic, but it does the job. For my system I need curves that go from 0.0 to 2.4f, which I pull-out using fCurrentGameTime as the index.
This is the curve for the colour of the Sun’s light during the course of the day. Atm I’m staying quite blue in the morning and blending to a very orange/red sunset in the evening, but this will be tweaked a million more times…
The angle of the sun’s light is done in a similar way:
Editing the Y value in this graph is what allows me to have very small shadows at mid-day and longer ones at dusk/dawn. Very tweakable.
So the class setup becomes:
And putting all this together, the Tick function looks like:
As the title says: Super. Simple.
There’re a couple of gotchas with this, at least for me.
- You need some sensible defaults, otherwise everything is going to be waaaay too bright when you drop a derived Blueprint into the world.
- Moveable Sky Lights will apply distance field AO by default, and that’s not something I’m after right now. It looks a little weird with my top-down view, but I may go back and tweak this later. Setting the Sky Light to Stationary disables this.
- Because my game is top-down I don’t need a sky box. Passing in a specific Cube map (all white) to the Sky Light makes it a little cheaper and gives better colour control. (Hat-tip to Mr Large for that factoid.)
So you’ll want to setup some sane values in the constructor. You’ll probably want to play around with a couple of lights in the editor first and work out how you want the shadows to appear, as well. There’re a lot of settings to toy with depending on the look you’re after.
The end result, before I spend the next several years tweaking it, is pretty good.