Skip to content
ct

Calendar Time v2.0.4

Getting Started with Calendar Time

A beginner-friendly walkthrough. If you've never used the plugin before, this is where to start. For why the time model is the way it is, see [time-model.md](./time-model.md). For exact API names, see

Status
Current
Version
v2.0.4
Updated
2026-08-09

A beginner-friendly walkthrough. If you've never used the plugin before, this is where to start. For why the time model is the way it is, see time-model.md. For exact API names, see the addon README.

The 30-second mental model

There are three things in the plugin, and each has a clear role:

  1. Engine time — Godot's own delta (real seconds). Drives rendering, physics, input, animation. The plugin never touches this.
  2. Game time — calendar time your game cares about (e.g. Day 3 of Spring). The plugin manages game time.
  3. The bridgeTimeScale.delta_multiplier. Controls game-time flow vs engine time. Default 1000.0.

The plugin gives you one or more shared runtime clocks — GameClocks — driven by a TimeHost. Each clock derives its date, time of day, and day-night phase from its own µs counter.

That's the whole picture. The rest of this guide is "how to author each piece."

What the demo looks like

The 2.0 demo includes a live TimeHost, GameClock, calendar display, time dial, event log, speed controls, and save/load buttons.

All wiring uses the public APIs described below.

Setup (10 minutes)

1. Install the addon

Copy addons/calendar_time/ from this plugin into your Godot project's addons/ folder, then enable it under Project Settings → Plugins → Calendar Time → Enable. No autoload is registered — everything is explicit.

2. Add the Time Host scene

The plugin ships a ready-made scene called default_time_host.tscn (in the templates/calendar_time/ folder — copy it into your project). Its root node is a TimeHost already wired to the bundled starter GameClock. Instantiate it once somewhere in your main scene tree:

Node What it does
TimeHost The engine→game driver. Drives every clock in TimeHost.clocks each frame via delta × TimeScale.delta_multiplier.
GameClock Shared Resource assigned in TimeHost.clocks; owns the current time, calendar, signal bus, serializer, and optional age service.

AgeComponent is a scene Node, not a data component on another system.

Keep your own age data in an adapter layer at the Calendar Time boundary.

You can add nodes individually from the Add Child menu.

The bundled scene is faster and wired correctly out of the box.

3. Configure the calendar

A GameCalendar .tres defines the shape of your time: months, days, hours, and event days.

Create one:

  1. In the FileSystem panel, right-click your project folder → New → Resource… → pick GameCalendar → save it as my_game_calendar.tres.

  2. Inside the calendar, add GameYear and GameMonth sub-resources.

    Optionally set TimeScale for seconds per minute, hour, and day.

Don't want to build one from scratch? The plugin ships a four_seasons starter calendar in templates/calendar_time/resources/four_seasons/ — copy and tweak it.

4. Author your shared GameClock

Every clock consumer binds the same GameClock .tres so they share one runtime timeline. Author it once:

  1. New → Resource… → GameClock → save as my_clock.tres.
  2. Set calendar to my_game_calendar.tres.
  3. Optionally set epoch to your calendar's epoch.
  4. Optionally assign an AgeService Resource if you use aging.

Bind this same my_clock.tres everywhere: TimeHost.clocks, UI displays, AgeComponents, lighting, and game code.

5. Wire TimeHost

Select the TimeHost node (the root of the default_time_host.tscn scene you added). In the Inspector:

  • clocks → add your my_clock.tres
  • time_scale → your TimeScale bridge multiplier, or leave empty for the default 1000× bridge

Press Play. The host is now ticking every clock in clocks. You're done with setup.

6. (Optional) Add a UI

The templates/calendar_time/ui/ folder has ready-made UI scenes:

  • date_time_display.tscn — current date/time.
  • time_of_day_display.tscn — dawn/day/dusk/night.
  • calendar_month_display.tscn — month grid.
  • engine_time_scale_control.tscn — fast-forward/slow-mo slider.

Add templates under a CanvasLayer.

Assign the same my_clock.tres to each display.

Templates ship with a default clock for standalone use — override for your game.

How time flows (the short version)

Godot's _process(delta) runs on your TimeHost. The host converts the engine delta into host game time with TimeHost.time_scale.delta_multiplier, then advances each GameClock. Each clock applies its own speed_multiplier, advances its microsecond counter, and fires boundary events on its own bus. You subscribe to clock.signal_bus from your game code and react.

engine delta
→ TimeHost.time_scale.delta_multiplier
→ GameClock.speed_multiplier
→ clock.signal_bus events

Use TimeHost.time_scale for host-wide calibration. Use GameClock.speed_multiplier when only one timeline should speed up, slow down, or pause.

Want the full picture? See time-model.md for boundary events, bus ownership, and save/load splits.

When do I need multiple clocks?

You don't, for most games.

One clock covers world calendar, ageing, and day/night.

The default default_time_host.tscn scene ships with a one-element ClockGroup.

You do need multiple clocks if your game wants different timelines to advance independently. Common scenarios:

  • World clock + festival timeline. World clock drives crops and day-night. Festival clock can pause independently.
  • Lockstep multiplayer. Each player has their own clock; the game syncs them at deterministic ticks.
  • Save-file-time vs game-time. A meta-game clock that runs only when the main menu is up, separate from the in-game clock.

For each additional clock, add a GameClock.tres to TimeHost.clocks.

Subscribe to each clock's signal_bus.

Use host.get_group_serializer() for unified save/load.

Saves are reorder-safe via stable clock_id.

Tip: Don't reach for multiple clocks unless you have a concrete reason. A single clock with time_scale tweaks covers most pacing needs. Multiple clocks add real complexity (event correlation, save ordering, multi-clock UI). Start with one.

What should use clock time vs engine delta?

For the decision list and examples, see What Should Drive What?.

How to react to time

For signal subscription examples, see Examples.

How to save and load

For save/load code examples, see Examples.

For save identity, reorder-safe saves, and legacy migration details, see Multi-clock Save Identity.

Common beginner questions

Q: My clock isn't ticking. What's wrong? A: Check that TimeHost.clocks has at least one GameClock assigned. The host refuses to advance without a clock.

Q: Can I have more than one clock? A: Yes — add more GameClock resources to TimeHost.clocks. Each clock is self-contained (its own calendar, signal bus, age registry); the host drives all of them automatically. See When do I need multiple clocks? above.

Q: How do I speed up or slow down time? A: For one timeline, change GameClock.speed_multiplier. For every clock on a host, change TimeHost.time_scale.delta_multiplier. Higher = faster game time. Use GameClockSpeedSlider-style controls for calendar speed; use the engine time-scale demo control only when you intentionally want to affect Godot itself.

Q: How do I pause game time without pausing the game? A: Set GameClock.speed_multiplier = 0.0 or TimeHost.time_scale.delta_multiplier = 0.0.

Rendering, physics, and input keep running — good for inventory menus.

Q: How do I attach an AgeComponent to my game object? A: In the Inspector, set @export var clock : GameClock to your my_clock.tres. The component binds to clock.age_registry automatically. Multi-clock scenes bind the clock your age service is configured for. Don't leave clock unset — the component will push_error and refuse to register state.

Q: My save uses multiple clocks. Will the data cross-pollinate if I reorder them? A: No. Each clock is identified by a stable clock_id (auto-derived from resource_path for .tres-loaded clocks, or from a monotonic counter for runtime clocks). Saves key per-clock by clock_id, so adding, removing, or reordering clocks between save and load does not cross-pollinate state. If you need explicit stable identity for a runtime clock, set clock_id_override on it (see Per-clock identity in the save/load section above).

Q: How do I make sure two separate game runs identify the same runtime clock? A: Set clock_id_override on the runtime clock:

func _ready()-> void:
    var clock:= GameClock.new(my_calendar)
    clock.clock_id_override= &"runtime_world_clock"  # any stable string
    $TimeHost.clocks= [clock]

Without the override, GameClock.new() gets a session-only runtime id.

The counter resets each process start.

Use clock_id_override to pin identity across runs.

Q: Where do I learn more?

  • time-model.md — the full time-model design doc
  • The addon README (addons/calendar_time/README.md) — full API reference