How Implement a virtual date and time system?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By WellStacked

I’m trying to implement a virtual date and time system for an game (like Sims, Simcity) time can be pause, speed up. And I do not quite understand how it works. And how I can you trigger an action, for example, at 19:30 replace label text.
I would be grateful for any help, thanks

:bust_in_silhouette: Reply From: SIsilicon

Well, first things first, you’ll need a script to manage time. A time managing script. It should hold a variable to store the time.
Assuming your game will be saved and loaded back to where the player left off, you’ll also need to make a saving and loading system to make sure your time isn’t lost.
Normally your time variable will increment at the same pace as the game; using _process to do so.

var time = 0

func _process(delta):
    time += delta

Implementing speed is as easy as multiplying that delta variable.

time += delta * speed

Finally concerning triggers, signals are your best option. Now remember that time here is represented with one number right now. I think it’s a good idea to make a DateTime inner class for convenience.

class DateTime:
    var second
    var minute
    var hour
    var day # if your game is expected to run longer, add year while you're at it.

    func _init(time):
        var int_time = int(floor(time)) # time is a decimal so we'll round it.
        
        second = int_time % 60
        minute = (int_time / 60) % 60
        hour = (int_time / (60 * 60)) % 24
        day = (int_time / (60 * 60 * 24))

    func equals(second, minute, hour, day):
        return self.second == second and self.minute == minute and self.hour == hour and self.day == day

Then we can send this through a signal, and anything connected to it will get a constant update of the current time.

signal time_passed(date_time)

func _process(delta):
    ...
    emit_signal("time_passed", DateTime.new(time))

However, there is a flaw in the current design. If the game were to be sped up, a lot of time will be skipped and events will be very likely to be missed. So instead of passing one DateTime, we’ll pass an array of it. This will require us to hold another variable to hold the time on the previous frame.

var prev_time = 0
...
func _process(delta):
    time += delta * speed

    var date_times = []
    var int_time = floor(time) + 1
    var prev_int_time = floor(prev_time) + 1
    while prev_int_time < int_time:
        date_times.append(prev_int_time)
        prev_int_time += 1

   emit_signal("time_passed", date_times)

   prev_time = time

Congratulations! Your sinple-ish time manager is practically complete. Now for whatever is connected to that signal, you must now do the following.

func _on_time_passed(date_times): this is function is connected to the signal
    for dt in date_times:
        if it.equals(second, minute, hour, day):
            # then do this event

Note that all of this was on the top of my head so it hasn’t been tested yet. Also, check out the Godot docs for any gdscript stuff you don’t understand.

SIsilicon | 2019-03-25 13:19

sorry, i try this, but seems, not working.
can you test it and fix it?
great thanks!

huahuapro | 2020-03-06 10:39

See my answer over here for a working version of this script.

njamster | 2020-03-25 12:41