Skip to content

Game.UI.InGame.TimeUISystem

Assembly:
Assembly-CSharp (Game)

Namespace:
Game.UI.InGame

Type:
public class

Base:
UISystemBase

Summary:
TimeUISystem is a UI-facing ECS system that exposes the runtime time/state of the game to the UI layer (bindings under the "time" group). It synchronizes and reports simulation time, day/tick information, lighting state, and simulation pause/speed state. It also enforces a "paused barrier" behavior (used by other systems to force-pause the simulation) and reacts to application focus changes (constraining/unconstraining the app). The system provides read bindings for various time values and trigger/event bindings to change pause/speed from the UI.


Fields

  • private struct TimeSettings : IJsonWritable, IEquatable<TimeSettings>
    A nested struct used to serialize/return time configuration to the UI. Contains ticksPerDay, daysPerYear, epochTicks and epochYear. Implements JSON writing and equality comparison.

  • private const string kGroup = "time"
    Name of the binding group used by the UI.

  • private SimulationSystem m_SimulationSystem
    Reference to the SimulationSystem (used to read and set selectedSpeed, frameIndex, etc.).

  • private TimeSystem m_TimeSystem
    Reference to the TimeSystem (used for normalized time and other time utilities).

  • private LightingSystem m_LightingSystem
    Reference to the LightingSystem (used to read lighting state / day-night decision).

  • private EntityQuery m_TimeSettingsQuery
    EntityQuery to fetch TimeSettingsData components from the ECS world.

  • private EntityQuery m_TimeDataQuery
    EntityQuery to fetch TimeData components (singleton) used to compute epoch/offsets and day/tick conversions.

  • private EventBinding<bool> m_SimulationPausedBarrierBinding
    An event binding that represents a "paused barrier" subscription. When observerCount > 0 the system treats the simulation as forced-paused for UI/input handling.

  • private float m_SpeedBeforePause = 1f
    Stores the user-selected simulation speed prior to a pause/forced-pause so it can be restored on unpause.

  • private bool m_UnpausedBeforeForcedPause
    Tracks whether the simulation was unpaused before a forced pause started, used to restore state when forced pause ends.

  • private bool m_HasFocus = true
    Tracks whether the application has focus (used to auto-pause when focus is lost / constrained).

Properties

  • private bool pausedBarrierActive => m_SimulationPausedBarrierBinding.observerCount > 0
    Convenience getter that indicates whether any external observers have activated the simulation-paused barrier (i.e., a forced pause is active).

Constructors

  • public TimeUISystem()
    Default constructor. Marked with [Preserve] in the source. No special initialization is done here; initialization occurs in OnCreate.

Methods

  • protected override void OnCreate()
    Initializes references to SimulationSystem, TimeSystem and LightingSystem; sets up EntityQueries; and registers UI bindings under the "time" group:
  • Getter bindings:
    • "timeSettings" -> GetTimeSettings()
    • "ticks" -> GetTicks()
    • "day" -> GetDay()
    • "lightingState" -> GetLightingState()
    • "simulationPaused" -> IsPaused()
    • "simulationSpeed" -> GetSimulationSpeed()
  • Event/Trigger bindings:

    • "simulationPausedBarrier" (EventBinding) stored in m_SimulationPausedBarrierBinding
    • "setSimulationPaused" (TriggerBinding) -> SetSimulationPaused()
    • "setSimulationSpeed" (TriggerBinding) -> SetSimulationSpeed() Also subscribes to PlatformManager.instance.onAppStateChanged to detect focus changes.
  • private void HandleAppStateChanged(IPlatformServiceIntegration psi, AppState state)
    Callback for platform/app state changes. Updates m_HasFocus depending on AppState.Default (has focus) or AppState.Constrained (no focus). Used to automatically pause when the app is constrained/lost focus.

  • protected override void OnGameLoaded(Context serializationContext)
    Resets m_SpeedBeforePause to 1f when a saved game/scene is loaded.

  • protected override void OnUpdate()
    Runs each frame. Tracks and updates:

  • If user-selected speed > 0, stores it into m_SpeedBeforePause.
  • If app lacks focus or the paused barrier is active, forces the simulation speed to 0 (pauses) and remembers whether it was unpaused prior to forced pause (m_UnpausedBeforeForcedPause).
  • If focus returned and no barrier is active, restores the selected speed to m_SpeedBeforePause when appropriate.

  • private TimeSettings GetTimeSettings()
    Builds and returns a TimeSettings instance for the UI. Reads TimeSettingsData (days-per-year) and the TimeData singleton to compute:

  • ticksPerDay (hardcoded to 262144),
  • daysPerYear (from settings),
  • epochTicks (computed from TimeData offsets and ticksPerDay),
  • epochYear (starting year from TimeData).

  • public int GetTicks()
    Returns an integer tick count derived from the simulation frameIndex and TimeData. Uses an internal constant-like value (182.04445f) to compute a floored tick value based on frames elapsed since TimeData.m_FirstFrame.

  • public int GetDay()
    Returns the current day index via TimeSystem.GetDay using the current frame index and TimeData singleton.

  • public LightingSystem.State GetLightingState()
    Returns the lighting state for UI consumption. If LightingSystem.state is valid, returns that; otherwise it falls back to a simple day/night determination based on TimeSystem.normalizedTime (treating early morning and late night as Night, otherwise Day).

  • public bool IsPaused()
    Returns true if the simulation is currently paused (selectedSpeed == 0).

  • public int GetSimulationSpeed()
    Returns the UI speed index corresponding to the current speed. If currently paused, it maps m_SpeedBeforePause (the stored previous non-zero speed) into an index; otherwise maps the current selectedSpeed. Mapping is done via SpeedToIndex.

  • private TimeSettingsData GetTimeSettingsData()
    Returns the TimeSettingsData singleton from the world if present. If no entity with TimeSettingsData exists, returns a default with m_DaysPerYear = 12.

  • private void SetSimulationPaused(bool paused)
    Trigger handler invoked by the UI to pause or unpause. If no paused barrier is active, it sets m_SimulationSystem.selectedSpeed to 0 or m_SpeedBeforePause. If a paused barrier is active, it only records m_UnpausedBeforeForcedPause to ensure correct restoration behavior when the barrier clears.

  • private void SetSimulationSpeed(int speedIndex)
    Trigger handler invoked by the UI to set simulation speed by index. If no paused barrier is active, sets selectedSpeed to the mapped speed. If a barrier is active, records the requested speed into m_SpeedBeforePause (so it takes effect after the barrier ends) and marks m_UnpausedBeforeForcedPause true.

  • private static float IndexToSpeed(int index)
    Maps an integer index [0..2] to a speed factor via pow(2, index). (0 => 1, but note index clamp in code: Mathf.Clamp(index, 0, 2) then Mathf.Pow(2f, ...)).

  • private static int SpeedToIndex(float speed)
    Inverse mapping: converts a speed factor to an index (clamping result between 0 and 2). If speed <= 0f returns 0.

Usage Example

// Example: read runtime time state from the TimeUISystem from managed world
using Unity.Entities;
using Game.UI.InGame;

// get the managed system instance from the default world
var world = World.DefaultGameObjectInjectionWorld;
var timeUISystem = world.GetExistingSystemManaged<TimeUISystem>();

if (timeUISystem != null)
{
    int ticks = timeUISystem.GetTicks();
    int day = timeUISystem.GetDay();
    bool paused = timeUISystem.IsPaused();
    int speedIndex = timeUISystem.GetSimulationSpeed();

    Debug.Log($"Ticks: {ticks}, Day: {day}, Paused: {paused}, SpeedIndex: {speedIndex}");
}

Notes and implementation details: - The system exposes most UI interactions through bindings in the "time" group. UI code should use the binding names ("time.timeSettings", "time.ticks", "time.setSimulationPaused", "time.setSimulationSpeed", etc.) when interacting with the UI layer. - The class respects an external "paused barrier" (m_SimulationPausedBarrierBinding). When observers subscribe to that event the system will treat the simulation as force-paused and prevent direct UI triggers from immediately changing the selected speed — instead they update the stored m_SpeedBeforePause and unpause state is restored when the barrier clears. - Time ticks per day is hardcoded to 262144 in this system; epochTicks are computed using TimeData offsets and this ticksPerDay constant.