Sky bodies

Put up to 16 moons, planets and stars in the sky on simple orbits or fixed in place, moving with the world clock, with phases lit by the sun, rise, set and full-moon events, a night light, and a visibility that follows day, night and any game variable.

Source: module-kits:kits/world_sky_bodies/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs world-sky-bodiesMCP tool: gripforge_docs { "topic": "world-sky-bodies" }

Moons, planets and stars in your sky (world.sky_bodies) #

What it is for #

A sky with two moons, a ringed giant hanging over the horizon or a bright wandering star makes a world feel like another place. world.sky_bodies keeps the bodies of that sky: where each one is, whether it is up, its phase, how much of it shows and how much light it brings at night. Bodies move on simple orbits (a period, an inclination, an offset) or stay fixed in the sky. The kit follows the clock of world.daynight when it is installed, else it holds the sky at a configured hour. Its presentation module draws the sky from what it publishes.

Turn it on #

gripforge_gamekit_install   { project_id: "gkp_…", id: "world.sky_bodies" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "world.sky_bodies",
                              config: { "bodies": [{ "id": "moon", "angularSize": 2.5, "motion": { "periodDays": 12 } }] } }

It requires game.engine. Install world.daynight too for a sky that moves with the hours.

First example #

<!-- test: kits/game_engine/host/tests/world_sky_bodies.rs::the_clock_of_world_daynight_places_a_full_moon_at_midnight --> With world.daynight and the config

{ "latitude": 30, "bodies": [{ "id": "moon", "motion": { "mode": "orbit", "periodDays": 8, "phaseOffset": 0.25 } }] }

on day 3 at midnight the moon has made a quarter of a turn more since the start: it is full, opposite the sun. It culminates at 60 degrees toward +Z, illuminated is 1 and phase 0.5. At noon of the same day it is below the horizon.

Settings and defaults #

SettingTypeDefaultMeaning
bodieslist (0 to 16)one moonthe bodies, see below
starsobject{ density: 0.5, brightness: 1, rotate: true, day: 0, band: { intensity: 0.35, tilt: 60, node: 0 } }the star field: how many, how bright, turning with the hour, how much shows by day (day), a band of more stars along a great circle (band)
visibilityobject{ driver: "", phases: [{ at: 0, day: 1, night: 1 }], smoothing: 6, twilight: 6, horizonFade: 2 }how much the sky lets through
nightLightsobject{ max: 2, shadows: true, scale: 0.25, horizonFade: 6, saturation: 0.5 }the night lights of the moons: how many at most, whether the brightest casts shadows, how bright a full moon of nightLight 1 is (in the units of the sun of world.lighting), the degrees over which they fade in above the horizon, how much of the colour of the body they keep
latitudenumber -90 to 9018latitude of the observer in degrees: how high the paths climb (18 puts the noon sun at 72 degrees, as world.daynight draws it)
axialTiltnumber 0 to 900tilt of the orbital plane on the equator, in degrees
hournumber 0 to 240the hour of the sky when world.daynight is not installed

A body: id (a-z, 0-9, _), label, kind (moon, planet, star), angularSize (apparent diameter in degrees), color, motion, lit (lit by the sun, so it has phases; false for a star by default), rings ({ inner, outer, tilt, color, opacity }, radii of the body), glow (0 to 1), nightLight (light at night when full, 0 by default), appearance ({ craters, bands, bandColor, patches, patchColor, streaks, streakColor, spots, spotColor, albedoVariation, seed } for a procedural surface), textureSlot (a binding of the body_texture slot instead) and hidden.

motion is { mode: "orbit", periodDays, phaseOffset, inclination, node } (days between two new phases; where it is at the start of day 1 in turns from the sun, 0.5 being full; degrees) or { mode: "fixed", azimuth, elevation }.

The driver of visibility is the path of any numeric variable (an atmosphere that thickens, a corruption that spreads). The phases give the visibility by day and by night along it, blended between neighbours; without a driver the first phase holds. smoothing is the seconds it takes to catch up with a jump, twilight the degrees of sun height over which day turns into night, horizonFade the degrees over which a body fades in above the horizon.

Directions use the frame of the sun of world.lighting: +Y up, azimuth 0 toward +Z, -90 toward -X. The sun rises at the sunrise of world.daynight toward azimuth -90 and sets at its sunset toward +90.

Services, events, variables #

  • world.sky (service): body(id) returns { direction, azimuth, elevation, up, phase, illuminated, visible, light, color, angularSize, glow }; visible() the ids that show; list() every body as defined with its look; set(id, patch) changes a body during the game and is saved (null puts it back); sun(), stars() (rotation and pole of the star field), visibility().
  • sky.rise, sky.set (events): a body crossed the horizon, with id, azimuth, day and hour; once per crossing, never at the start nor after a load.
  • sky.phase (event): a body lit by the sun became full or new.
  • Variables, written at the game tick only when they change: per body azimuth, elevation, phase, illuminated and visible under sky. and its id; sky.nightLight, sky.visibility, sky.stars.rotation, sky.stars.visible, sky.sunAzimuth and sky.sunElevation (where the sun is, for the phases), and sky.revision, which moves when a definition or a look changes (read list() again then).
  • Night lights: each body with a nightLight lights the ground from where it stands, as bright as its lit fraction, its visibility and its height let it, only at night, in a desaturated colour of the body. The brightest nightLights.max are sky.light.<n>.azimuth, .elevation, .intensity, .color (0xRRGGBB) and .shadows (n from 0, the brightest first, which alone casts the shadows), drawn by the presentation module as directional lights; sky.nightLight is their total. With world.daynight 1.2 its stand-in night sun steps down to a faint minimum (nightSun: "auto").

Hooks, verbs and modding #

The hook sky.body is asked with { id, kind, reason, color, angularSize, glow, visibility } at the start, after a load, a config change or a set, and each time the body rises. color, angularSize, glow and visibility (a factor from 0 to 1) are mutable; it is not cancellable.

{ "id": "blood_moon", "hook": "sky.body", "match": { "id": "moon", "reason": "rise" }, "set": { "color": "#b0302a" } }

Scripts use the action set_body ({ "type": "set_body", "body": "moon", "hidden": false }), the conditions body_up and body_visible (with min), and missions the step wait_rise ({ "type": "wait_rise", "body": "moon" }, done when the body is up; next: true waits for the next rise). Every setting is moddable and live.

When it does not do what you expect #

  • A body never shows: it is hidden, below the horizon, or the visibility phases give 0 at this driver value.
  • The sky does not move: world.daynight is not installed, or its clock is paused.
  • A body never rises: at a high latitude a body far from the equator stays up or down all day.
  • No sky.phase: the body has lit: false.

Limits #

Orbits are simple: no eccentricity, no seasons (the sun stays on the celestial equator), no eclipses yet. Its presentation module draws each body as a sphere with its phase, procedural surface, halo and rings, and the star field; it does not draw a textureSlot yet (the procedural surface shows instead). Bodies and stars are added over the sky: the unlit side of a body shows the sky behind it, and a lit sky washes the stars out.

Versions #

Kit world.sky_bodies 1.0.0, requires game.engine@^1; uses world.clock of world.daynight when installed.