Let’s create a beautiful day and night cycle for a multiplayer game in Unreal 4. Our night will be a pitch black and day will have a spectacular sky and lighting. In bonus part we’ll create a moon and lighting and to go even further on i’ll show you how to do it in C++. This system will have a full dynamic lighting so no baking and sitting and waiting for hours, you’ll have a new feature in your game before you go to bed.
Blueprint Only Project Files
https://levelparadox.gumroad.com/l/yuhsi
First we need to enable Sun Position Plugin.
Goto Settings -> Plugins and enable Sun Position Calculator.
Add Sun and Sky to your level.
When you add SunSky to your level it will get really bright, reset the DirectionalLight Intensity inside the blueprint and then remove SkyLight, DirectionalLight and SkyAtmosphere from the Level.
Select View Options in Content Browser and turn on Show Plugin Content.
Right click SunSky in World Outliner and Browse to Asset.
Dublicate SunSky and replace SunSky from level with dublicate. Also move it to Project Content folder.
Make your level look like this, so remove Directional Light, Skylight, Atmospheric Fog…
Then Build Lighting. Level is going to be pitch black but that’s what we wan’t for now.
Then Edit SunSky (the one you dublicated and added to level).
Now in SunSky add function named FromDateTime.
FromDateTime will have one input variable:
Then break it like this into year, month, day and solar time of SunSky variables.
Make sure you use 59.999 value or otherwise you’ll see shadows jumping.
Also set enable Replicates so we don’t forget it.
You may wan’t to set DirectionalLight Intensity to 10 lux
or Enable Extend default luminance range in Auto Exposure settings.
For time sync we need Player Controller Class so create a new one:
Now your project should look like this.
Open it and enable Replicates.
In DN_PlayerController we create one variable, event dispatcher and function.
TimeDifferenceToServer is Timespan:
In next part we can’t do some things because Epic broke some things.
So let’s quickly create Blueprint Function Library in C++
You need Visual Studio Community https://docs.unrealengine.com/4.26/en-US/ProductionPipelines/DevelopmentSetup/VisualStudioSetup/
Select Blueprint Function Library.
Replace UMyBlueprintFunctionLibrary with your class name.
#pragma once
#include "CoreMinimal.h"
#include "Misc/DateTime.h"
#include "Misc/Timespan.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "MyBlueprintFunctionLibrary.generated.h"
/**
*
*/
UCLASS()
class UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure, category="DateTime Helpers")
static void DateTimeSubtractSpan(FDateTime A, FDateTime B, FTimespan& Out);
UFUNCTION(BlueprintPure, category = "DateTime Helpers")
static void DateTimeAdd(FDateTime A, FTimespan B, FDateTime& Out);
UFUNCTION(BlueprintPure, category = "DateTime Helpers")
static void DateTimeSubtract(FDateTime A, FTimespan B, FDateTime& Out);
};
#include "MyBlueprintFunctionLibrary.h"
void UMyBlueprintFunctionLibrary::DateTimeSubtractSpan(FDateTime A, FDateTime B, FTimespan& Out)
{
Out = A - B;
}
void UMyBlueprintFunctionLibrary::DateTimeAdd(FDateTime A, FTimespan B, FDateTime& Out)
{
Out = A + B;
}
void UMyBlueprintFunctionLibrary::DateTimeSubtract(FDateTime A, FTimespan B, FDateTime& Out)
{
Out = A - B;
}
It should look like this.
You can also try to copy paste ue4 k2nodes
Date Time – Date Time = Time Span
Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name="K2Node_CallFunction_9"
bIsPureFunc=True
FunctionReference=(MemberParent=Class'"/Script/Engine.KismetMathLibrary"',MemberName="Subtract_DateTimeDateTime")
NodePosX=3392
NodePosY=-368
NodeGuid=1437240543881B9C2B55C89C6066EE6B
CustomProperties Pin (PinId=A226721641712E74318796A3D7860FE9,PinName="self",PinFriendlyName=NSLOCTEXT("K2Node", "Target", "Target"),PinToolTip="Target\nKismet Math Library Object Reference",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=Class'"/Script/Engine.KismetMathLibrary"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Script/Engine.Default__KismetMathLibrary",PersistentGuid=00000000000000000000000000000000,bHidden=True,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=FF72B3DD42DABC31D6158DBD0F6B5CC6,PinName="A",PinToolTip="A\nDate Time Structure",PinType.PinCategory="struct",PinType.PinSubCategory="",PinType.PinSubCategoryObject=ScriptStruct'"/Script/CoreUObject.DateTime"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_13 45A86EBF4EDF0494CEB96799507BA994,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=A50CD7F94D5E88ACE6AADCA5B54D5353,PinName="B",PinToolTip="B\nDate Time Structure",PinType.PinCategory="struct",PinType.PinSubCategory="",PinType.PinSubCategoryObject=ScriptStruct'"/Script/CoreUObject.DateTime"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_BreakStruct_1 BBB831BC40D78165971D78B700EAD200,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=26EA064C4FCE1BD08137A78ACB103881,PinName="ReturnValue",PinToolTip="Return Value\nTimespan Structure\n\nSubtraction (A - B)",Direction="EGPD_Output",PinType.PinCategory="struct",PinType.PinSubCategory="",PinType.PinSubCategoryObject=ScriptStruct'"/Script/CoreUObject.Timespan"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_10 19134AF449237458A0BE7E9897C3D4A1,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
Date Time + Time Span = Date Time
Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name="K2Node_CallFunction_16"
bIsPureFunc=True
FunctionReference=(MemberParent=Class'"/Script/Engine.KismetMathLibrary"',MemberName="Add_DateTimeTimespan")
NodePosX=3552
NodePosY=144
NodeGuid=91883B064B6FF37C30094194A576AF1B
CustomProperties Pin (PinId=555A73674F36839DD120B6A45BEC3C06,PinName="self",PinFriendlyName=NSLOCTEXT("K2Node", "Target", "Target"),PinToolTip="Target\nKismet Math Library Object Reference",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=Class'"/Script/Engine.KismetMathLibrary"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Script/Engine.Default__KismetMathLibrary",PersistentGuid=00000000000000000000000000000000,bHidden=True,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=441533544BFAFDB716845E9D9F6C2E18,PinName="A",PinToolTip="A\nDate Time Structure",PinType.PinCategory="struct",PinType.PinSubCategory="",PinType.PinSubCategoryObject=ScriptStruct'"/Script/CoreUObject.DateTime"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_VariableGet_3 5B0B8B9E4F857D5210B6C688114ACCFC,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=839C3EFB4989A6594730AD94EFD1C843,PinName="B",PinToolTip="B\nTimespan Structure",PinType.PinCategory="struct",PinType.PinSubCategory="",PinType.PinSubCategoryObject=ScriptStruct'"/Script/CoreUObject.Timespan"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_22 16EAF2F94817C040E6FA1D9E86BF69E1,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=3EE2F50F43E61FE153A778806F06457B,PinName="ReturnValue",PinToolTip="Return Value\nDate Time Structure\n\nAddition (A + B)",Direction="EGPD_Output",PinType.PinCategory="struct",PinType.PinSubCategory="",PinType.PinSubCategoryObject=ScriptStruct'"/Script/CoreUObject.DateTime"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_FunctionResult_0 8F91C9F64DAB875546DE24AE82506052,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
Time Span + Time Span = Time Span
Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name="K2Node_CallFunction_4"
bIsPureFunc=True
FunctionReference=(MemberParent=Class'"/Script/Engine.KismetMathLibrary"',MemberName="Add_TimespanTimespan")
NodePosX=896
NodePosY=352
NodeGuid=8FD2754046F78C64F2F30AB37A1C41D0
CustomProperties Pin (PinId=6879712D435580C73F8B2BAE306945AC,PinName="self",PinFriendlyName=NSLOCTEXT("K2Node", "Target", "Target"),PinToolTip="Target\nKismet Math Library Object Reference",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=Class'"/Script/Engine.KismetMathLibrary"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Script/Engine.Default__KismetMathLibrary",PersistentGuid=00000000000000000000000000000000,bHidden=True,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=8A61620F4F57821340F02AAA35637496,PinName="A",PinToolTip="A\nTimespan Structure",PinType.PinCategory="struct",PinType.PinSubCategory="",PinType.PinSubCategoryObject=ScriptStruct'"/Script/CoreUObject.Timespan"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_VariableGet_1 1CCC1959414323E03E6927BAC739BC81,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=DFC8E4FF49AB750EF25AF791401DB6B0,PinName="B",PinToolTip="B\nTimespan Structure",PinType.PinCategory="struct",PinType.PinSubCategory="",PinType.PinSubCategoryObject=ScriptStruct'"/Script/CoreUObject.Timespan"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_7 F0C4ACDB48D3C1F9030F14B662131387,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=B53DAB1E490D6D27D2F06F907360ED20,PinName="ReturnValue",PinToolTip="Return Value\nTimespan Structure\n\nAddition (A + B)",Direction="EGPD_Output",PinType.PinCategory="struct",PinType.PinSubCategory="",PinType.PinSubCategoryObject=ScriptStruct'"/Script/CoreUObject.Timespan"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_CallFunction_0 36A5E71C41DFDF3AF993DCA90D8544B6,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
ClientTimetoServerDifference function:
Then we have two events GetServerTime (Run On Server, Reliable) and ReceiveServerTime (Run on owning client, Reliable):
Then create event SendDifferenceToSunSky:
We’ll come back to this once we finish coding.
Now create new Game Mode. Add New -> Blueprint Class -> Game Mode Base.
Name it as DN_GameMode
Then open it and Select Class Defaults and Set Player Controller Class to DN_PlayerController.
And go to Project Settings.
In Project -> Maps & Modes -> Set Default GameMode to DN_GameMode.
where did that “Update SunSky” function come from? is that an older version of Sun Position Calculator?
oh shoot posted this on the wrong page
Do you mean “Update Sky”? Because i can’t find “Update SunSky”.
Unmodified version looks like this:
In 4.26 and 4.27
This was probably made in 4.25. Because Epic added “Real Time Capture” into SkyLight
https://docs.unrealengine.com/4.27/en-US/WhatsNew/Builds/ReleaseNotes/4_26/#real-timeskylightcapture
Which is why “Update Sky” is not in 4.26 and latest:
Do you think this will be the proper system for Unreal Engine 5 as well? Or did they add some new way to do it even better?
Thanks for your hard work
For some reason in UE5 you can’t add Make Timespan and Date Time as they are incompatible
Make sure you got the variable type right, best way to do variables is to use “Promote to Variable” which you can get by pulling out from endpoint.
In this case find AsTimespawn (text) or AsTime (text) and try pulling out from these to create variables.
https://docs.unrealengine.com/4.26/en-US/BlueprintAPI/Utilities/Text/AsTime/
https://docs.unrealengine.com/4.26/en-US/BlueprintAPI/Utilities/Text/AsTimespan/
Thanks for the quick responses, appreciate it!
It seems like a UE5 bug to be honest.
When I drag Server Game Time to A position (Date Time structure) of the ADD node, the B position shows that it’s timespan structure, which it should be. But when I connect the Make Timespan endpoint to it, the A (Server Game Time) connection is broken, and it changes every pins structure type to timespan.
I found some other post showing the same problem :
https://forums.unrealengine.com/t/adding-timespan-to-date-time-not-working/256329
Sorry to spam your tutorial about UE5 stuff, I might have to build it in UE4 and migrate to UE5 to make it work
Oh yeah, i found the same thing from UE5 EA2, it’s actually missing Date Time and Timespan combining/add node.
You could create Blueprint Function Library in C++ and then create custom function which combines them and returns result or… just try the full C++ version:
https://levelparadox.com/2020/11/13/ue4-day-and-night-cycle-c-part-2/
you just need to create material, material instance and 2 curves and it should work out of box.
Add me on Discord if you need more help: nuclearlocket#9741
Haha, I waited with messing around with it until the actual release of UE5, but it seems like they didn’t fix this issue, this node is still bugged for me.
I have no experience with C++ in Unreal, but gonna give it a shot, thanks!
Well haven’t seen anything new or better anywhere else and seem’s like everyone else is also using Sun Position Plugin tools to do Day Night Cycle in UE5. Even in UE5 + Lumen you still need Sun Position Plugin, Exponential Height Fog and Sky Athmosphere if you wan’t Dynamic Day Night Cycle.
This project in UE5:
https://www.youtube.com/watch?v=nkPnIlPtg8Y
Someone else using Sun Position Plugin in UE5:
https://www.youtube.com/watch?v=kcV5XOqX9i8