riversong code showcase
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
// This file remains only to satisfy a stale generated project reference outside Assets.
|
||||
// Night blend is now computed in WorldTimeSystem and stored on WorldTimeState.
|
||||
}
|
||||
133
Source/Riversong/Game/World/Time/DayNightCycleLightingSystem.cs
Normal file
133
Source/Riversong/Game/World/Time/DayNightCycleLightingSystem.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
[RequiresWorldReadyForUpdate]
|
||||
[GameSystemGroup(typeof(LateGameSystemGroup))]
|
||||
[UpdateAfter(typeof(WorldTimeSystem))]
|
||||
public class DayNightCycleLightingSystem : GameSystem, IInitializable, IDisposable, IUpdatable
|
||||
{
|
||||
[InjectService]
|
||||
private GameConfig _config;
|
||||
|
||||
[InjectService]
|
||||
private IScene _scene;
|
||||
|
||||
[InjectService]
|
||||
private World _world;
|
||||
|
||||
private Quaternion _initialLightRotation;
|
||||
|
||||
public DayNightCycleLightingSystem(IServiceLocator serviceLocator) : base(serviceLocator)
|
||||
{
|
||||
}
|
||||
|
||||
public UniTask InitializeAsync()
|
||||
{
|
||||
_initialLightRotation = _scene.MainLight.transform.localRotation;
|
||||
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Shader.SetGlobalFloat(ShaderProperties.NightBlend, 0);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var nightBlend = _world.TimeState.NightBlend;
|
||||
Shader.SetGlobalFloat(ShaderProperties.NightBlend, nightBlend);
|
||||
|
||||
UpdateLightRig(nightBlend);
|
||||
UpdateShadows(nightBlend);
|
||||
UpdateAmbientLighting(nightBlend);
|
||||
UpdatePostProcessing(nightBlend);
|
||||
}
|
||||
|
||||
private void UpdateLightRig(float nightBlend)
|
||||
{
|
||||
_scene.LightRig.transform.localRotation = _world.TimeState.DayNightCycleStep switch
|
||||
{
|
||||
DayNightCycleStep.Day => Quaternion.identity,
|
||||
DayNightCycleStep.DayToNight => Quaternion.Euler(0, nightBlend * 360, 0),
|
||||
DayNightCycleStep.Night => Quaternion.identity,
|
||||
DayNightCycleStep.NightToDay => Quaternion.Euler(0, -nightBlend * 360, 0),
|
||||
_ => throw new ArgumentException()
|
||||
};
|
||||
|
||||
var dayRotation = _initialLightRotation;
|
||||
var nightRotation = _initialLightRotation * Quaternion.Euler(-90, 0, 0);
|
||||
|
||||
_scene.MainLight.transform.localRotation = _world.TimeState.DayNightCycleStep switch
|
||||
{
|
||||
DayNightCycleStep.Day => dayRotation,
|
||||
DayNightCycleStep.DayToNight => Quaternion.Slerp(dayRotation, nightRotation, nightBlend),
|
||||
DayNightCycleStep.Night => nightRotation,
|
||||
DayNightCycleStep.NightToDay => Quaternion.Slerp(nightRotation, dayRotation, 1 - nightBlend),
|
||||
_ => throw new ArgumentException()
|
||||
};
|
||||
|
||||
_scene.NightLight.transform.localRotation = _world.TimeState.DayNightCycleStep switch
|
||||
{
|
||||
DayNightCycleStep.Day => nightRotation,
|
||||
DayNightCycleStep.DayToNight => Quaternion.Slerp(nightRotation, dayRotation, nightBlend),
|
||||
DayNightCycleStep.Night => dayRotation,
|
||||
DayNightCycleStep.NightToDay => Quaternion.Slerp(dayRotation, nightRotation, 1 - nightBlend),
|
||||
_ => throw new ArgumentException()
|
||||
};
|
||||
}
|
||||
|
||||
private void UpdateShadows(float nightBlend)
|
||||
{
|
||||
const float fadeSpeed = 2;
|
||||
|
||||
var mainLightT = Mathf.Clamp01(fadeSpeed * nightBlend);
|
||||
UpdateShadows(_scene.MainLight, Mathf.Lerp(_config.Time.DayLighting.ShadowStrength, 0, mainLightT));
|
||||
|
||||
var nightLightT = Mathf.Clamp01(fadeSpeed * (1 - nightBlend));
|
||||
UpdateShadows(_scene.NightLight, Mathf.Lerp(_config.Time.NightLighting.ShadowStrength, 0, nightLightT));
|
||||
}
|
||||
|
||||
private static void UpdateShadows(Light light, float shadowStrength)
|
||||
{
|
||||
light.shadowStrength = shadowStrength;
|
||||
light.shadows = shadowStrength > 0 ? LightShadows.Soft : LightShadows.None;
|
||||
}
|
||||
|
||||
private void UpdateAmbientLighting(float nightBlend)
|
||||
{
|
||||
RenderSettings.ambientLight = Color.Lerp(_config.Time.DayLighting.AmbientColor, _config.Time.NightLighting.AmbientColor, nightBlend);
|
||||
}
|
||||
|
||||
private void UpdatePostProcessing(float nightBlend)
|
||||
{
|
||||
_scene.NightVolume.weight = nightBlend;
|
||||
_scene.BloomVolume.weight = nightBlend;
|
||||
_scene.WarmTintVolume.weight = GetWarmTintBlend();
|
||||
}
|
||||
|
||||
private float GetWarmTintBlend()
|
||||
{
|
||||
var step = _world.TimeState.DayNightCycleStep;
|
||||
if (step != DayNightCycleStep.DayToNight && step != DayNightCycleStep.NightToDay) return 0;
|
||||
|
||||
var config = _config.Time;
|
||||
|
||||
var time = _world.TimeState.DayNightCycleTime;
|
||||
var blend = step == DayNightCycleStep.DayToNight ? Mathf.Clamp01(time / config.DayToNightDuration) : Mathf.Clamp01(time / config.NightToDayDuration);
|
||||
|
||||
var rampUp = config.WarmTintRampUp;
|
||||
var rampDown = config.WarmTintRampDown;
|
||||
|
||||
if (blend <= rampUp.x) return 0;
|
||||
if (blend < rampUp.y) return Mathf.SmoothStep(0, 1, Mathf.InverseLerp(rampUp.x, rampUp.y, blend));
|
||||
if (blend <= rampDown.x) return 1;
|
||||
if (blend < rampDown.y) return Mathf.SmoothStep(1, 0, Mathf.InverseLerp(rampDown.x, rampDown.y, blend));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Source/Riversong/Game/World/Time/DayNightCycleStep.cs
Normal file
13
Source/Riversong/Game/World/Time/DayNightCycleStep.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public enum DayNightCycleStep
|
||||
{
|
||||
Day,
|
||||
|
||||
DayToNight,
|
||||
|
||||
Night,
|
||||
|
||||
NightToDay
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public struct DayNightCycleStepChangedSignal
|
||||
{
|
||||
public DayNightCycleStep Step;
|
||||
|
||||
public DayNightCycleStepChangedSignal(DayNightCycleStep step)
|
||||
{
|
||||
Step = step;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Source/Riversong/Game/World/Time/DayStartedSignal.cs
Normal file
6
Source/Riversong/Game/World/Time/DayStartedSignal.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public struct DayStartedSignal
|
||||
{
|
||||
}
|
||||
}
|
||||
6
Source/Riversong/Game/World/Time/EndOfMonthSignal.cs
Normal file
6
Source/Riversong/Game/World/Time/EndOfMonthSignal.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public struct EndOfMonthSignal
|
||||
{
|
||||
}
|
||||
}
|
||||
6
Source/Riversong/Game/World/Time/EndOfWeekSignal.cs
Normal file
6
Source/Riversong/Game/World/Time/EndOfWeekSignal.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public struct EndOfWeekSignal
|
||||
{
|
||||
}
|
||||
}
|
||||
6
Source/Riversong/Game/World/Time/EndOfYearSignal.cs
Normal file
6
Source/Riversong/Game/World/Time/EndOfYearSignal.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public struct EndOfYearSignal
|
||||
{
|
||||
}
|
||||
}
|
||||
6
Source/Riversong/Game/World/Time/NightStartedSignal.cs
Normal file
6
Source/Riversong/Game/World/Time/NightStartedSignal.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public struct NightStartedSignal
|
||||
{
|
||||
}
|
||||
}
|
||||
21
Source/Riversong/Game/World/Time/WorldTimeState.cs
Normal file
21
Source/Riversong/Game/World/Time/WorldTimeState.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
public class WorldTimeState
|
||||
{
|
||||
public int TotalWeeks { get; set; }
|
||||
|
||||
public int WeekNumber { get; set; }
|
||||
|
||||
public int MonthNumber { get; set; }
|
||||
|
||||
public int YearNumber { get; set; }
|
||||
|
||||
public float WeekTime { get; set; }
|
||||
|
||||
public DayNightCycleStep DayNightCycleStep { get; set; } = DayNightCycleStep.Day;
|
||||
|
||||
public float DayNightCycleTime { get; set; }
|
||||
|
||||
public float NightBlend { get; set; }
|
||||
}
|
||||
}
|
||||
110
Source/Riversong/Game/World/Time/WorldTimeSystem.cs
Normal file
110
Source/Riversong/Game/World/Time/WorldTimeSystem.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DanieleMarotta.RiversongCodeShowcase
|
||||
{
|
||||
[RequiresWorldReadyForUpdate]
|
||||
[GameSystemGroup(typeof(EarlyGameSystemGroup))]
|
||||
public class WorldTimeSystem : GameSystem, IUpdatable
|
||||
{
|
||||
[InjectService]
|
||||
private GameConfig _config;
|
||||
|
||||
[InjectService]
|
||||
private World _world;
|
||||
|
||||
[InjectService]
|
||||
private ISignalBus _signalBus;
|
||||
|
||||
public WorldTimeSystem(IServiceLocator serviceLocator) : base(serviceLocator)
|
||||
{
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
AdvanceCalendar();
|
||||
AdvanceDayNightCycle();
|
||||
}
|
||||
|
||||
private void AdvanceCalendar()
|
||||
{
|
||||
var timeState = _world.TimeState;
|
||||
if (timeState.DayNightCycleStep != DayNightCycleStep.Day) return;
|
||||
|
||||
var weekDuration = _config.Time.WeekDuration;
|
||||
|
||||
timeState.WeekTime += Time.deltaTime;
|
||||
|
||||
if (timeState.WeekTime > weekDuration)
|
||||
{
|
||||
timeState.WeekTime -= weekDuration;
|
||||
|
||||
timeState.TotalWeeks++;
|
||||
|
||||
timeState.WeekNumber = (timeState.WeekNumber + 1) % 4;
|
||||
if (timeState.WeekNumber > 0)
|
||||
{
|
||||
_signalBus.Raise(new EndOfWeekSignal());
|
||||
return;
|
||||
}
|
||||
|
||||
timeState.MonthNumber = (timeState.MonthNumber + 1) % 12;
|
||||
if (timeState.MonthNumber > 0)
|
||||
{
|
||||
_signalBus.Raise(new EndOfWeekSignal());
|
||||
_signalBus.Raise(new EndOfMonthSignal());
|
||||
return;
|
||||
}
|
||||
|
||||
timeState.YearNumber++;
|
||||
_signalBus.Raise(new EndOfWeekSignal());
|
||||
_signalBus.Raise(new EndOfMonthSignal());
|
||||
_signalBus.Raise(new EndOfYearSignal());
|
||||
}
|
||||
}
|
||||
|
||||
private void AdvanceDayNightCycle()
|
||||
{
|
||||
var timeState = _world.TimeState;
|
||||
var config = _config.Time;
|
||||
|
||||
timeState.DayNightCycleTime += Time.deltaTime;
|
||||
|
||||
var stepDuration = timeState.DayNightCycleStep switch
|
||||
{
|
||||
DayNightCycleStep.Day => config.DayDuration,
|
||||
DayNightCycleStep.DayToNight => config.DayToNightDuration,
|
||||
DayNightCycleStep.Night => config.NightDuration,
|
||||
DayNightCycleStep.NightToDay => config.NightToDayDuration,
|
||||
_ => throw new ArgumentException()
|
||||
};
|
||||
|
||||
while (timeState.DayNightCycleTime > stepDuration)
|
||||
{
|
||||
timeState.DayNightCycleTime -= stepDuration;
|
||||
timeState.DayNightCycleStep = (DayNightCycleStep)(((int)timeState.DayNightCycleStep + 1) % 4);
|
||||
|
||||
_signalBus.Raise(new DayNightCycleStepChangedSignal(timeState.DayNightCycleStep));
|
||||
|
||||
switch (timeState.DayNightCycleStep)
|
||||
{
|
||||
case DayNightCycleStep.Night:
|
||||
_signalBus.Raise(new NightStartedSignal());
|
||||
break;
|
||||
case DayNightCycleStep.Day:
|
||||
_signalBus.Raise(new DayStartedSignal());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
timeState.NightBlend = timeState.DayNightCycleStep switch
|
||||
{
|
||||
DayNightCycleStep.Day => 0,
|
||||
DayNightCycleStep.DayToNight => Mathf.Clamp01(timeState.DayNightCycleTime / config.DayToNightDuration),
|
||||
DayNightCycleStep.Night => 1,
|
||||
DayNightCycleStep.NightToDay => 1 - Mathf.Clamp01(timeState.DayNightCycleTime / config.NightToDayDuration),
|
||||
_ => throw new ArgumentException()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user