How to make a clock system that works with Ultra Dynamic Sky

Hi there,

I recently bought “Ultra Dynamic Sky” by Everett Gunther… it really works great, however i wanted to add a clock to display the time of day on the screen when in game, using widgets however there is no good tutorials on youtube on how to create a clock, i’ve been searching but found nothing…and also if i could add a system-time functionality to UDS that also be great… could some one give me a blueprint of how to set the clock up and have it functioning?

I have attached some screenshots,
Any help is greatly appreciated!

I do something like this for my time system

In my game instance i set up variables for my time and date and whenever one of them change i broadcast the new value. I tick it in my game mode btw, so stuff like the main menu doesn’t tick it and change the time. And then on my clock widget on begin play or whatever it is, i set it up to listen to those event dispatchers and set the time it shows to whatever time it currently is, so it doesn’t have to change the time displayed every frame if i did it with tick. I do the same thing to the sky sphere, make it listen to whenever the anim time changes and update the sphere then. In the default sky sphere I would do some math and update the sun angle or something, but for your sky I think what you’d do is set the “Time of Day” variable to animTime * 100 and then update it.

.h



DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAnimTimeChanged, float, animTime);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMilisecondsChanged, int32, miliseconds);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSecondsChanged, int32, seconds);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMinuteChanged, int32, minute);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FHourChanged, int32, hour);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDayChanged, int32, day);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSeasonChanged, class USeason*, season);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FYearChanged, int32, year);

/**
 * 
 */
UCLASS()
class YGGDRASIL_API UCustomGameInstance : public UGameInstance {
	GENERATED_BODY()
	
public:
	void Tick(float deltaSeconds);



protected:
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Time Date")
	uint32 timePaused : 1;
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Time Date", Meta = (ClampMin = "0"))
	float timeMultiplier = 1.0f;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Time Date", Meta = (ClampMin = "0"))
	int32 year;
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Time Date")
	USeason* currentSeason = Cast<USeason>(StaticLoadObject(USeason::StaticClass(), NULL, TEXT("Season'/Game/DataAssets/Seasons/Spring.Spring'")));
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Time Date", Meta = (ClampMin = "1"))
	int32 day = 1;
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Time Date", Meta = (ClampMin = "0", ClampMax = "23"))
	int32 hour;
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Time Date", Meta = (ClampMin = "0", ClampMax = "59"))
	int32 minute;
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Time Date", Meta = (ClampMin = "0", ClampMax = "59"))
	int32 seconds;
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Time Date", Meta = (ClampMin = "0", ClampMax = "99"))
	int32 miliseconds;

public:
	bool GetTimePaused() const { return timePaused; }
	UFUNCTION(BlueprintCallable, Category = "Time Date")
	void SetTimePaused(bool inTimePaused) { timePaused = inTimePaused; }
	float GetTimeMultiplier() const { return timeMultiplier; }
	UFUNCTION(BlueprintCallable, Category = "Time Date")
	void SetTimeMultiplier(float inTimeMultiplier);

	int32 GetYear() const { return year; }
	USeason* GetCurrentSeason() const;
	int32 GetDay() const { return day; }
	int32 GetHour() const { return hour; }
	int32 GetMinute() const { return minute; }
	int32 GetSeconds() const { return seconds; }
	int32 GetMiliseconds() const { return miliseconds; }
	float GetAnimTime() const;

	UFUNCTION(BlueprintPure, Category = "Time Date")
	static Days GetDayOfTheWeek (int32 inYear, USeason* inSeason, int32 inDay = 1);

	UFUNCTION(BlueprintCallable, Category = "Time Date")
	void SetTimeDate(int32 inYear, USeason* inSeason, int32 inDay, int32 inHour,
					 int32 inMinute = 0, int32 inSeconds = 0, int32 inMiliseconds = 0);

	UPROPERTY(BlueprintAssignable, Category = "Time Date")
	FAnimTimeChanged AnimTimeChanged;
	UPROPERTY(BlueprintAssignable, Category = "Time Date")
	FMilisecondsChanged MilisecondsChanged;
	UPROPERTY(BlueprintAssignable, Category = "Time Date")
	FSecondsChanged SecondsChanged;
	UPROPERTY(BlueprintAssignable, Category = "Time Date")
	FMinuteChanged MinuteChanged;
	UPROPERTY(BlueprintAssignable, Category = "Time Date")
	FHourChanged HourChanged;
	UPROPERTY(BlueprintAssignable, Category = "Time Date")
	FDayChanged DayChanged;
	UPROPERTY(BlueprintAssignable, Category = "Time Date")
	FSeasonChanged SeasonChanged;
	UPROPERTY(BlueprintAssignable, Category = "Time Date")
	FYearChanged YearChanged;
};


.cpp



void UCustomGameInstance ::Tick(float deltaSeconds) {
	if (deltaSeconds <= 0) { LOG_ERROR("Delta Seconds <= 0."); }
	else if (!timePaused) {
		miliseconds += FMath::FloorToInt(deltaSeconds * 6000 * timeMultiplier);
		if (miliseconds >= 100) {
			seconds += miliseconds / 100;
			miliseconds = miliseconds % 100;
			if (seconds >= 60) {
				minute += seconds / 60;
				seconds = seconds % 60;
				if (minute >= 60) {
					hour += minute / 60;
					minute = minute % 60;
					if (hour >= 24) {
						day += hour / 24;
						hour = hour % 24;
						while (GetCurrentSeason() && day > currentSeason->GetDays()) {
							day -= currentSeason->GetDays();
							if (!currentSeason->GetNextSeason()) { LOG_ERROR("Current Season has no next season."); }
							else {
								currentSeason = currentSeason->GetNextSeason();
								if (currentSeason->GetIsFirstSeason()) {
									year++;
									YearChanged.Broadcast(year);
								}
								SeasonChanged.Broadcast(currentSeason);
							}
						}
						DayChanged.Broadcast(day);
					}
					HourChanged.Broadcast(hour);
				}
				MinuteChanged.Broadcast(minute);
			}
			SecondsChanged.Broadcast(seconds);
		}
		MilisecondsChanged.Broadcast(miliseconds);
		AnimTimeChanged.Broadcast(GetAnimTime());
	}
}



void UCustomGameInstance ::SetTimeMultiplier(float inTimeMultiplier) {
	if (inTimeMultiplier < 0) { LOG_ERROR("InTimeMultiplier < 0."); }
	else timeMultiplier = inTimeMultiplier;
}



USeason* UCustomGameInstance ::GetCurrentSeason() const {
	if (!currentSeason) { LOG_ERROR("CurrentSeason == nullptr."); }
	return currentSeason;
}



float UCustomGameInstance ::GetAnimTime() const {
	return (float)hour + (float)minute / 60 + (float)seconds / 3600 + (float)miliseconds / 360000;
}



Days UCustomGameInstance ::GetDayOfTheWeek(int32 inYear, USeason* inSeason, int32 inDay) {
	// ERROR CHECKING
	if (inYear < 0) { LOG_ERROR("InYear < 0."); return Days::Sunday; }
	else if (!inSeason) { LOG_ERROR("InSeason == nullptr."); return Days::Sunday; }
	else if (inDay > inSeason->GetDays()) { LOG_ERROR("InDay > InSeason.Days"); return Days::Sunday; }

	// Get Days in Year
	int32 daysInYear = 0;
	USeason* checkingSeason = inSeason;
	do {
		daysInYear += checkingSeason->GetDays();
		if (!checkingSeason->GetPrevSeason()) { LOG_ERROR(*checkingSeason->GET_DISPLAY_NAME + " has no previous season"); return Days::Sunday; break; }
		else { checkingSeason = checkingSeason->GetNextSeason(); }
	} while (checkingSeason != inSeason);

	// Calculate the number of days up until now
	int32 daysUpUntilNow = inDay - 1;
	daysUpUntilNow += daysInYear * inYear;
	checkingSeason = inSeason;
	while (!checkingSeason->GetIsFirstSeason()) {
		checkingSeason = checkingSeason->GetPrevSeason();
		daysUpUntilNow += checkingSeason->GetDays();
	}

	// Convert the days up until now to a day of the week and return it
	return (Days)(daysUpUntilNow % 7);
}



void UCustomGameInstance ::SetTimeDate(int32 inYear, USeason* inSeason, int32 inDay, int32 inHour,
										 int32 inMinute, int32 inSeconds, int32 inMiliseconds) {
	if (inYear < 0) { LOG_ERROR("InYear < 0."); }
	else { year = inYear; YearChanged.Broadcast(year); }

	if (!inSeason) { LOG_ERROR("InSeason == nullptr."); }
	else { currentSeason = inSeason; SeasonChanged.Broadcast(currentSeason); }

	if (inDay <= 0) { LOG_ERROR("InDay <= 0."); }
	else if (GetCurrentSeason() && inDay > currentSeason->GetDays()) { LOG_ERROR("InDay > Season's days."); }
	else { day = inDay; DayChanged.Broadcast(day); }

	if (inHour < 0) { LOG_ERROR("InHour < 0."); }
	else if (inHour >= 24) { LOG_ERROR("InHour >= 24."); }
	else { hour = inHour; HourChanged.Broadcast(hour); }

	if (inMinute < 0) { LOG_ERROR("InMinute < 0."); }
	else if (inMinute >= 60) { LOG_ERROR("InMinute >= 60."); }
	else { minute = inMinute; MinuteChanged.Broadcast(minute); }

	if (inSeconds < 0) { LOG_ERROR("InSeconds < 0."); }
	else if (inSeconds >= 60) { LOG_ERROR("InSeconds >= 60"); }
	else { seconds = inSeconds; SecondsChanged.Broadcast(seconds); }

	if (inMiliseconds < 0) { LOG_ERROR("InMiliseconds < 0."); }
	else if (inMiliseconds >= 100) { LOG_ERROR("InMiliseconds >= 100."); }
	else { miliseconds = inMiliseconds; MilisecondsChanged.Broadcast(miliseconds); }

	AnimTimeChanged.Broadcast(GetAnimTime());
}


i know this is code, but i dont understand any of it :(… could you perhaps show me in BPs?

Thanks,
Ken