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.
- "simulationPausedBarrier" (EventBinding
-
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.