5. Day and night
Rat Hunt, step 5: a game clock that drives timeOfDay and atmosphereFrame from morning to dusk, and point lights that come on as it gets dark.
The game lasts one day. In this step a clock moves the sun from mid-morning to after sunset, the sky and the light change with it, and as it gets dark a lamp by the door and a lantern you carry come on.
The clock
timeOfDay(phase) turns a single number
into every lighting field of a frame: the sun's direction and colour, ambient light, the sky
gradient, the moon. The phase runs over a day: 0 is midnight, 0.25 sunrise, 0.5 noon, 0.75
sunset. Rat Hunt runs from 0.3 to 0.78, early morning to just after sunset, over three minutes:
import type { PointLight, Vec3 } from "@voxolith/renderer";
const DAY_SECONDS = Math.max(10, Number(params.get("day") ?? 180));
const DAWN = 0.3, DUSK = 0.78;
let clock = 0;?day=30 on the URL makes a 30-second day, which is handy while you test the evening.
Remove the fixed sky constant from step 2; the frame now computes it from the clock:
clock += dt;
const phase = DAWN + (Math.min(clock, DAY_SECONDS) / DAY_SECONDS) * (DUSK - DAWN);
const lighting = timeOfDay(phase);
renderer.setLights(lights(lighting.nightFactor));
status.textContent = `${Math.max(0, Math.ceil(DAY_SECONDS - clock))} s to nightfall`;and renders with it:
renderer.render({ ...view(), ...atmosphereFrame(lighting, ATMOSPHERES.clear), time: now / 1000 });atmosphereFrame combines the lighting
with an atmosphere. ATMOSPHERES.clear changes nothing; try ATMOSPHERES.cloudy, rain or
fog to see the same day under other weather. The engine only describes and blends weather:
when it changes, and how wet the ground gets, is the game's decision (see
Atmosphere and weather).
At the top of the file, next to canvas, take the HUD's status line:
const status = document.getElementById("status")!;Lights
The sun and moon are one directional key light, which timeOfDay controls. Anything else that
gives light is a point light, a position
with a colour and a hard range beyond which it adds exactly nothing. Up to 32 at a time are
passed with renderer.setLights.
const doorLamp: Vec3 = [hx, pad + 20, hz1 + 3];
function lights(night: number): PointLight[] {
if (night < 0.02) return [];
return [
{ position: doorLamp, color: [1, 0.72, 0.4], intensity: 2.4 * night, range: 90, glow: 2 * night },
// A lantern the player carries: no shadow ray, so it costs little.
{ position: [player.x, player.eyeY - 4, player.z], color: [1, 0.85, 0.6], intensity: 0.9 * night, range: 60, shadows: false },
];
}nightFactor from timeOfDay goes from 0 by day to 1 at night, so both lights fade in through
the evening instead of switching on. By day there are no point lights at all, which costs
nothing.
- The door lamp stands just in front of the door, 20 voxels up, at the top of the frame. Its
glowdraws a visible halo, so the lamp itself shows. - The lantern follows the player, a little below the eye. It sets
shadows: false: every shadowed light traces an extra ray per pixel it reaches, and a light at the camera casts shadows you would hardly see.
The lit windows from step 4 are emissive: they glow, but light only their own voxels. To make a window light the ground outside, put a point light at it.
Run it
Load the page with ?day=30. The sun climbs, swings across and reddens; the shadows lengthen; at
dusk the lamp and the lantern come on and the windows glow. The HUD counts down to nightfall.
When the clock runs out the evening simply stays; the next step makes that the end of the game.
See Lighting for shadow cost and the quality presets' shadowSteps.
Next: rats.
4. Trees and a house
Rat Hunt, step 4: generate a cottage and trees with gen-building and gen-tree, level the ground under the house, and stamp the models into the world.
6. Rats
Rat Hunt, step 6: rigged, animated rats from gen-creature, moved with makeCrowd and makeBrickStamper, that flee from you and are caught with a tap.