// Fill out your copyright notice in the Description page of Project Settings. #include "TimeofDayManager.h" #include "DayNightGameStateBase.h" #include "Engine/DirectionalLight.h" #include "Engine/SkyLight.h" #include "Components/SkyLightComponent.h" #include "EngineUtils.h" #include "Engine.h" #include "GameFramework/WorldSettings.h" #include "Kismet/GameplayStatics.h" ATimeofDayManager::ATimeofDayManager() { PrimaryActorTick.bCanEverTick = true; m_RequiredCaptureDelta = 0.01f; SetReplicates(true); } void ATimeofDayManager::BeginPlay() { m_GS = Cast(GetWorld()->GetGameState()); GatherConfig(); Super::BeginPlay(); if (m_PrimarySunLight) { m_OriginalSunBrightness = m_PrimarySunLight->GetBrightness(); m_TargetSunBrightness = m_OriginalSunBrightness; } } void ATimeofDayManager::Tick(float DeltaTime) { Super::Tick(DeltaTime); if (m_PrimarySunLight) { if (m_LastTimeOfDay == m_GS->GetElapsedGameMinutes()) { m_TimeSinceLastIncrement += DeltaTime; } else { m_TimeSinceLastIncrement = 0; } /** Predict the movement of the sun to smooth out the rotations between replication updates of the actual time of day */ const float PredictedIncrement = m_GS->GetTimeOfDayIncrement() * m_TimeSinceLastIncrement; /** TimeOfDay is expressed in minutes, we need to convert this into a pitch rotation */ const float MinutesInDay = 24 * 60; const float PitchOffset = 90; /* The offset to account for time of day 0 should equal midnight */ const float PitchRotation = 360 * ((m_GS->GetElapsedGameMinutes() + PredictedIncrement) / MinutesInDay); FRotator NewSunRotation = FRotator(PitchRotation + PitchOffset, 45.0f, 0); m_PrimarySunLight->SetActorRelativeRotation(NewSunRotation); m_LastTimeOfDay = m_GS->GetElapsedGameMinutes(); } bool CurrentNightState = m_GS->IsNight(); if (CurrentNightState != m_bLastNightState) { if (CurrentNightState) { m_TargetSunBrightness = 0.01f; } else { m_TargetSunBrightness = m_OriginalSunBrightness; } } // TODO: Check to make sure ptrs aren't null etc. if (m_PrimarySunLight == nullptr) return; /* Update sun brightness to transition between day and night (Note: We cannot disable the sunlight because BP_SkySphere depends on an enabled light to update the skydome) */ const float LerpSpeed = 0.1f * GetWorldSettings()->GetEffectiveTimeDilation(); float CurrentSunBrightness = m_PrimarySunLight->GetBrightness(); float NewSunBrightness = FMath::Lerp(CurrentSunBrightness, m_TargetSunBrightness, LerpSpeed); m_PrimarySunLight->SetBrightness(NewSunBrightness); m_bLastNightState = CurrentNightState; UpdateSkylight(); } void ATimeofDayManager::GatherConfig() { TArray SkySpheres = GetAllActorsWithName(*m_GS->m_SkySphereName); TArray DirectionalLights = GetAllActorsWithName(*m_GS->m_PrimarySunLightName); TArray SkyLights = GetAllActorsWithName(*m_GS->m_SkyLightName); if (SkySpheres.Num() > 0) { m_SkySphere = SkySpheres[0]; } if (DirectionalLights.Num() > 0) { m_PrimarySunLight = DirectionalLights[0]; } if (SkyLights.Num() > 0) { m_SkyLightActor = SkyLights[0]; } } void ATimeofDayManager::UpdateSkylight() { if (m_SkyLightActor) { if (m_GS) { const float NewIntensity = FMath::Lerp(0.1, 1.0, FMath::Sin((m_GS->GetElapsedMinutesCurrentDay() / m_GS->MINUTESINDAY) * PI)); m_SkyLightActor->GetLightComponent()->SetIntensity(NewIntensity); // possible to uncomment without breaking, see what looks better. float ColorChannel = 1.0f * NewIntensity; m_SkyLightActor->GetLightComponent()->SetLightColor(FLinearColor(ColorChannel, ColorChannel, ColorChannel, 1.0f)); if (m_RequiredCaptureDelta < FMath::Abs(NewIntensity - m_LastCapturedIntensity)) { /** Force re-capture of the sky w/ new intensity */ /** Hacky and costly solution to recapturing the sky, official support NYI */ m_SkyLightActor->GetLightComponent()->RecaptureSky(); m_LastCapturedIntensity = NewIntensity; } } } } template TArray ATimeofDayManager::GetAllActorsWithName(const FName& NameOfActor) { TArray TempArray; for (TActorIterator It(GetWorld()); It; ++It) { T* actor = *It; if (actor && actor->GetName().Contains(NameOfActor.ToString())) { TempArray.Add(actor); } } return TempArray; }