On this page
Configuring Sundial in Code
The Visual Editor plugin is the easiest way to author Sundial because you can see the result while you’re changing it. But sometimes you just wanna get your hands dirty with code.
SundialImport() ultimately calls the Sundial constructor and configures it through the same methods outlined on this page. If you want to load settings from somewhere else, or simply keep the whole setup in GML, you can use the exact same methods, starting with creating the Sundial constructor:
sundial = new Sundial();
The constructor already has a complete default day/night setup, so even that is a usable Sundial. From there, replace whichever parts your game wants to own.
The six grading anchors
Sundial exposes six authored grading stages through eSundial:
eSundial.MORNING
eSundial.DAY
eSundial.AFTERNOON
eSundial.DUSK
eSundial.NIGHT
eSundial.DAWN
Set a stage’s colour with SetGrading():
sundial.SetGrading(
eSundial.DUSK,
make_color_rgb(161, 51, 71)
);
Then set how strongly that colour should bias the scene:
sundial.SetGradingStrength(eSundial.DUSK, 1.48);
A strength of 0 leaves that stage neutral. 1 applies the full luminance-normalized colour bias, and values above 1 deliberately exaggerate it. Exactly like the Visual Editor plugin.
Sundial normalizes the grading colour around its luminance before applying the strength. In practical terms, the colour control is mainly answering “what colour should this part of the day feel like?” while essentially ignoring brightness, which is configured separately.
Outdoor brightness
Set full-Day and full-Night brightness with:
sundial.SetBrightness(1.09, 0.39);
The first value is Day brightness. The second is Night brightness.
Sundial blends continuously between them using its resolved sunlight amount, so you don’t need to create a separate brightness value for all six grading anchors.
This separation is because two scenes could use the same Dusk colour but very different exposures. A bright stylised game might keep Night around 0.7, whereas a grimy horror game might want to push it much lower.
A full grading setup
Sundial uses a fluent interface, so you can chain the configuration calls:
sundial = new Sundial()
.SetGrading(eSundial.MORNING, make_color_rgb(194, 242, 229))
.SetGradingStrength(eSundial.MORNING, 2.00)
.SetGrading(eSundial.DAY, make_color_rgb(255, 237, 189))
.SetGradingStrength(eSundial.DAY, 2.00)
.SetGrading(eSundial.AFTERNOON, make_color_rgb(219, 121, 55))
.SetGradingStrength(eSundial.AFTERNOON, 1.17)
.SetGrading(eSundial.DUSK, make_color_rgb(161, 51, 71))
.SetGradingStrength(eSundial.DUSK, 1.48)
.SetGrading(eSundial.NIGHT, make_color_rgb(28, 28, 61))
.SetGradingStrength(eSundial.NIGHT, 1.25)
.SetGrading(eSundial.DAWN, make_color_rgb(246, 194, 222))
.SetGradingStrength(eSundial.DAWN, 0.91)
.SetBrightness(1.09, 0.39);
Those are just using Sundial’s current built-in defaults.
Configuring interiors in code
The indoor settings are a bit simpler, since we don’t need all the particular time of day gradings:
sundial
.SetIndoorGrading(make_color_rgb(230, 220, 205))
.SetIndoorGradingStrength(0.8)
.SetIndoorDaylightInfluence(0.25)
.SetIndoorBrightness(0.85, 0.55);
These settings only affect pixels selected by a runtime mask.
See Indoor & Outdoor Grading for the mask itself and the exact meaning of each indoor control.
Configuring cycle timing
SetCycleTiming() controls the shape of Sundial’s continuous time period resolver:
sundial.SetCycleTiming(
0.60, // day fraction
0.10, // morning duration
0.27, // afternoon blend start
65 / 600, // sunset duration
45 / 600, // twilight duration
75 / 600 // predawn duration
);
All six values live in Sundial’s normalized 0..1 day cycle.
They mean:
day_fraction where daytime ends and Dusk begins
morning_duration how long Morning takes to fade into Day
afternoon_start where the Afternoon colour begins blending in
sunset_duration how long before day end the sunset blend occupies
twilight_duration how long after day end Twilight occupies
predawn_duration how much of the end of night is used for Predawn
There is a little more going on here than six simple clock timestamps because Sundial’s internal visual resolver uses extra Sunset, Twilight and Predawn phases to make the grade move naturally.
The public semantic period boundaries resolve to:
Morning = 0
Day = morning_duration
Afternoon = day_fraction - sunset_duration
Dusk = day_fraction
Night = day_fraction + twilight_duration
Dawn = 1 - predawn_duration * 0.5
afternoon_start is deliberately separate from the public Afternoon boundary. It controls when the Afternoon colour begins blending in before the authored Afternoon period is considered active.
This is the one part of Sundial I would absolutely recommend authoring in the Visual Editor unless you have a very specific reason not to. Dragging the visible period nodes around a timeline is simply easier to reason about than editing six normalized timing values by hand like this.
Giving Sundial a clock
The visual cycle itself only needs 0..1. If game code wants a 24-hour clock as well, tell Sundial what time Morning represents:
sundial.SetMorningTime(6);
Now normalized 0 is 06:00.
Set the current clock time with:
sundial.SetTime(17.5);
which means 17:30.
The clock origin doesn’t change the visual timing shape, it just tells Sundial where the “seam” is between days.
Configuring automatic time
For a ten-minute day:
sundial
.SetDayLength(600)
.SetTime(8)
.SetAutoTime(true);
Or configure the speed directly in game seconds per real second:
sundial
.SetSpeed(144)
.SetAutoTime(true);
The two controls describe the same progression rate in different ways, so setting one updates the value represented by the other, you don’t need to set both.
See Time, Periods & Callbacks for the exact synchronization and callback behaviour.
Adding time period callbacks in code
The general form is:
sundial.OnPeriod(eSundialPeriod.NIGHT, function() {
global.is_night = true;
});
but the convenience calls are usually a bit nicer to work with:
sundial
.OnDusk(function() {
global.streetlights = true;
})
.OnMorning(function() {
global.streetlights = false;
});
OnPeriodChanged() is available when one callback needs to react to every time period change instead of just one:
sundial.OnPeriodChanged(function(_from, _to) {
global.current_period = _to;
});
Mix and match code-authored and Visual Editor-authored Sundial settings
You don’t have to make a project-wide choice between the Visual Editor and manual configuration.
For example, you can author the expensive visual part in the Visual Editor:
sundial = SundialImport("Overworld");
then maybe alter the brightness in code when winter starts or something like that:
if (winter_started) {
sundial.SetBrightness(0.9, 0.15);
}
Or use the Visual Editor for the Overworld and create a tiny special-purpose Sundial in code for a dream sequence, or whatever. It’s your game, not mine, so it’s your decision to make how and why to mix and match, if at all.
Once imported, the handle returned by SundialImport is just a normal Sundial struct built from the constructor. Use whichever authoring path keeps the thing you’re trying to express easiest to understand for you.
API Reference
That’s the end of the teaching material for Sundial. For exact signatures, argument types, return values and enum members, the plain code of Sundial, continue to the API Reference.