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?
Because I’m a top down game I don’t need:
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:
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.
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.