Feat - Add Loading menju

This commit is contained in:
2026-06-24 14:04:12 +02:00
parent c9b085ebea
commit 48588ccfed
935 changed files with 433163 additions and 602 deletions
@@ -0,0 +1,252 @@
using FishNet.Managing.Timing;
using FishNet.Object;
using FishNet.Object.Synchronizing;
using UnityEngine;
namespace Ashwild.Environment
{
/// <summary>
/// Owns the synchronized day/night clock and drives the stylized procedural skybox plus the
/// directional sun. The whole sky is a pure function of a single time value, so the server only
/// replicates one epoch (SyncVar) and every client reconstructs the identical sky locally — no
/// per-frame traffic, and late joiners snap to the correct time of day from the replicated epoch.
/// Mirrors SeasonManager's global-driving / [ExecuteAlways] style but networked like the registries.
/// </summary>
[ExecuteAlways]
[RequireComponent(typeof(NetworkObject))]
[AddComponentMenu("GAME/Environment/Sky Time Manager")]
public class SkyTimeManager : NetworkBehaviour
{
#region Serialized Fields
[Header("Cycle")]
[Tooltip("Durée d'une journée complète (24 h de jeu) en secondes réelles.")]
[SerializeField] private float dayLengthSeconds = 1200f;
[Tooltip("Heure de départ du serveur. 0 = minuit, 0.25 = aube, 0.5 = midi, 0.75 = crépuscule.")]
[Range(0f, 1f)] [SerializeField] private float initialTimeOfDay = 0.3f;
[Header("Rendu")]
[SerializeField] private Material skyboxMaterial;
[SerializeField] private Light sunLight;
[Tooltip("Orientation (azimut) de l'arc du soleil, en degrés.")]
[SerializeField] private float sunAzimuthDegrees = 30f;
[Tooltip("Intensité de la lumière directionnelle en plein jour (s'éteint la nuit).")]
[SerializeField] private float maxSunIntensity = 1.1f;
[Tooltip("Intervalle (s) de rafraîchissement de l'ambient/reflection — coûteux, pas chaque frame.")]
[SerializeField] private float ambientRefreshInterval = 0.25f;
[Header("Aperçu Éditeur")]
[Tooltip("Heure simulée hors Play pour prévisualiser le ciel dans la Scene view.")]
[Range(0f, 1f)] [SerializeField] private float editorPreviewTime = 0.3f;
#endregion
#region Networked State
/// <summary>
/// Server time (synchronized seconds) captured when the cycle's zero-point was set. Written
/// once on server start and only again on an explicit SetTimeOfDay. Clients derive the current
/// time of day from this plus their own corrected TimeManager tick.
/// </summary>
private readonly SyncVar<double> startServerTime = new SyncVar<double>();
#endregion
#region State
private static readonly int TimeOfDayId = Shader.PropertyToID("_TimeOfDay");
private static readonly int SunDirId = Shader.PropertyToID("_SunDirection");
private static readonly int SkyTimeId = Shader.PropertyToID("_SkyTime");
private float ambientTimer;
#endregion
#region Public State
public static SkyTimeManager Instance { get; private set; }
/// <summary>
/// Current normalized time of day (0..1, midnight → midnight), identical on every client.
/// </summary>
public float TimeOfDay01 { get; private set; }
#endregion
#region Unity Lifecycle
/// <summary>
/// Validates the editor references and drives the editor preview when the game is not running.
/// </summary>
private void OnEnable()
{
WarnIfUnassigned();
}
/// <summary>
/// Clamps tuning and refreshes the sky preview live while editing. Does not rotate the sun
/// transform here — Unity forbids transform writes during OnValidate; the shader reads the sun
/// direction from a global computed analytically, and Update rotates the actual Light.
/// </summary>
private void OnValidate()
{
dayLengthSeconds = Mathf.Max(1f, dayLengthSeconds);
ambientRefreshInterval = Mathf.Max(0.01f, ambientRefreshInterval);
if (!Application.isPlaying) ApplyEditorPreview(false);
}
/// <summary>
/// In Play, recomputes the synced time of day from the network clock and drives the sky; out
/// of Play, drives the sky from the editor preview slider without touching the network stack.
/// </summary>
private void Update()
{
if (!Application.isPlaying)
{
ApplyEditorPreview(true);
return;
}
if (!base.IsSpawned || base.TimeManager == null) return;
double now = base.TimeManager.TicksToTime(TickType.Tick);
double elapsed = now - startServerTime.Value;
TimeOfDay01 = Mathf.Repeat(initialTimeOfDay + (float)(elapsed / dayLengthSeconds), 1f);
ApplyVisuals(TimeOfDay01, (float)elapsed, true);
RefreshAmbientThrottled(Time.deltaTime);
}
#endregion
#region Network Lifecycle
/// <summary>
/// Claims the singleton and binds the skybox material + sun so every client renders the sky.
/// </summary>
public override void OnStartNetwork()
{
base.OnStartNetwork();
Instance = this;
BindRenderSettings();
}
/// <summary>
/// Server-only: stamps the cycle's zero-point once so the SyncVar replicates the current time
/// of day (offset by initialTimeOfDay) to every present and future client.
/// </summary>
public override void OnStartServer()
{
base.OnStartServer();
startServerTime.Value = base.TimeManager.TicksToTime(TickType.Tick);
}
/// <summary>
/// Clears the singleton — mirrors OnStartNetwork.
/// </summary>
public override void OnStopNetwork()
{
base.OnStopNetwork();
if (Instance == this) Instance = null;
}
#endregion
#region Public API
/// <summary>
/// Server-only: jumps the whole world to a given time of day by rewriting the epoch, which the
/// SyncVar replicates so all clients snap together. Ignored when called off the server.
/// </summary>
public void SetTimeOfDay(float timeOfDay01)
{
if (!base.IsServerInitialized)
{
Debug.LogWarning("[SkyTimeManager] SetTimeOfDay ignored — only the server owns the clock.", this);
return;
}
float target = Mathf.Repeat(timeOfDay01, 1f);
double now = base.TimeManager.TicksToTime(TickType.Tick);
startServerTime.Value = now - (target - initialTimeOfDay) * dayLengthSeconds;
}
#endregion
#region Internal Helpers
/// <summary>
/// Assigns the procedural sky to the lighting settings and registers the sun for URP ambient.
/// </summary>
private void BindRenderSettings()
{
if (skyboxMaterial != null) RenderSettings.skybox = skyboxMaterial;
if (sunLight != null) RenderSettings.sun = sunLight;
}
/// <summary>
/// Drives the sky from the editor preview slider, repainting the Scene view so the look updates
/// live without entering Play (matches SeasonManager's preview). rotateSun is false when called
/// from OnValidate, where rotating the Light transform is disallowed.
/// </summary>
private void ApplyEditorPreview(bool rotateSun)
{
BindRenderSettings();
TimeOfDay01 = editorPreviewTime;
ApplyVisuals(editorPreviewTime, editorPreviewTime * dayLengthSeconds, rotateSun);
#if UNITY_EDITOR
if (!Application.isPlaying) UnityEditor.SceneView.RepaintAll();
#endif
}
/// <summary>
/// Fades the sun intensity below the horizon, pushes the synced time/sun-direction/animation
/// globals the skybox shader reads, and (when rotateSun) rotates the Light along its arc. The
/// sun direction is computed analytically so the shader is correct even when the transform is
/// not touched. skyTime is the continuous synced seconds driving cloud drift and shooting stars,
/// so those events match across clients.
/// </summary>
private void ApplyVisuals(float timeOfDay01, float skyTime, bool rotateSun)
{
float altitude = Mathf.Sin((timeOfDay01 - 0.25f) * 2f * Mathf.PI);
Quaternion sunRotation = Quaternion.Euler(altitude * 90f, sunAzimuthDegrees, 0f);
Vector3 sunDirection = -(sunRotation * Vector3.forward);
if (sunLight != null)
{
if (rotateSun) sunLight.transform.rotation = sunRotation;
sunLight.intensity = maxSunIntensity * Mathf.Clamp01(altitude * 1.5f + 0.05f);
}
Shader.SetGlobalFloat(TimeOfDayId, timeOfDay01);
Shader.SetGlobalVector(SunDirId, sunDirection);
Shader.SetGlobalFloat(SkyTimeId, skyTime);
}
/// <summary>
/// Refreshes skybox-based ambient and reflections on a fixed cadence — UpdateEnvironment is
/// expensive, so it must never run every frame.
/// </summary>
private void RefreshAmbientThrottled(float dt)
{
ambientTimer += dt;
if (ambientTimer < ambientRefreshInterval) return;
ambientTimer = 0f;
DynamicGI.UpdateEnvironment();
}
/// <summary>
/// Logs a clear error when the inspector references are missing so the sky never fails silently.
/// </summary>
private void WarnIfUnassigned()
{
if (skyboxMaterial == null)
Debug.LogError("[SkyTimeManager] No skybox material assigned — the sky will not render.", this);
if (sunLight == null)
Debug.LogError("[SkyTimeManager] No directional sun Light assigned — day/night lighting will not work.", this);
}
#endregion
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 541147133c27b8748b7b6b8da351322c