In-game Clock Object

A lot of games make use of a time and date system. Here’s a quick GML tutorial for a variable speed time and date system for Game Maker Studio 2.

The final result looks a little like this:

First, create an object called Clock and place this code in the Create event:

/// @description Init Clock
delta = 0;

seconds = 0;
minutes = 0;
hours = 6;
days = 1;
months = 1;
years = 1;

monthData = generate_month_data();

clockSpeed = room_speed * 100;

generate_clock_strings();

This will initialize the Clock and give us variables to store the current date and time. We’ll add the generate_month_data() and generate_clock_strings() scripts once we’re done with this object. Take note of the clockSpeed variable. This controls how fast the clock runs. Setting this to 1 will make the clock realtime. I have it set to room_speed * 100 for testing purposes.

Next, add this code to the Step event:

/// @description Run Clock

delta += clockSpeed;

if(delta >= room_speed){
    var deltaLeft = delta % room_speed;
    seconds += floor(delta / room_speed);
    delta = deltaLeft;
}

if(seconds >= 60){
    var secondsLeft = seconds % 60;
    minutes += floor(seconds / 60);
    seconds = secondsLeft;
}

if(minutes >= 60){
    var minutesLeft = minutes % 60;
    hours += floor(minutes / 60);
    minutes = minutesLeft;
}

if(hours >= 24){
    var hoursLeft = hours % 24;
    days += floor(hours / 24);
    hours = hoursLeft;
}

var monthMeta = monthData[| months - 1];

if(days > monthMeta[? "days"]){
    var daysLeft = days % monthMeta[? "days"];
    months += floor(days / monthMeta[? "days"]);
    days = daysLeft;
}

if(months > 12){
    var monthsLeft = months % 12;
    years += floor(months / 12);
    months = monthsLeft;
}

generate_clock_strings();

This will add our clockSpeed to the Clock’s delta, which is then calculated in a waterfall that updates all of the following time and date variables.

Now, we add two helper scripts, generate_month_data() and generate_clock_strings(),  that just add some extra functionality to our time and date system.

generate_month_data() is just a ds_list of ds_maps that contain information on each month of the year. I added actual real-world month data, but this can be modified to suit any scenario.

///generate_month_data

var monthData = ds_list_create();

var jan = ds_map_create();
ds_map_add(jan, "name", "January");
ds_map_add(jan, "shortName", "Jan");
ds_map_add(jan, "month", 1);
ds_map_add(jan, "days", 31);
ds_list_add(monthData, jan);

var feb = ds_map_create();
ds_map_add(feb, "name", "February");
ds_map_add(feb, "shortName", "Feb");
ds_map_add(feb, "month", 2);
ds_map_add(feb, "days", 28);
ds_list_add(monthData, feb);

var mar = ds_map_create();
ds_map_add(mar, "name", "March");
ds_map_add(mar, "shortName", "Mar");
ds_map_add(mar, "month", 3);
ds_map_add(mar, "days", 31);
ds_list_add(monthData, mar);

var apr = ds_map_create();
ds_map_add(apr, "name", "April");
ds_map_add(apr, "shortName", "Apr");
ds_map_add(apr, "month", 4);
ds_map_add(apr, "days", 30);
ds_list_add(monthData, apr);

var may = ds_map_create();
ds_map_add(may, "name", "May");
ds_map_add(may, "shortName", "May");
ds_map_add(may, "month", 5);
ds_map_add(may, "days", 31);
ds_list_add(monthData, may);

var jun = ds_map_create();
ds_map_add(jun, "name", "June");
ds_map_add(jun, "shortName", "Jun");
ds_map_add(jun, "month", 6);
ds_map_add(jun, "days", 30);
ds_list_add(monthData, jun);

var jul = ds_map_create();
ds_map_add(jul, "name", "July");
ds_map_add(jul, "shortName", "Jul");
ds_map_add(jul, "month", 7);
ds_map_add(jul, "days", 31);
ds_list_add(monthData, jul);

var aug = ds_map_create();
ds_map_add(aug, "name", "August");
ds_map_add(aug, "shortName", "Aug");
ds_map_add(aug, "month", 8);
ds_map_add(aug, "days", 31);
ds_list_add(monthData, aug);

var sep = ds_map_create();
ds_map_add(sep, "name", "September");
ds_map_add(sep, "shortName", "Sep");
ds_map_add(sep, "month", 9);
ds_map_add(sep, "days", 30);
ds_list_add(monthData, sep);

var oct = ds_map_create();
ds_map_add(oct, "name", "October");
ds_map_add(oct, "shortName", "Oct");
ds_map_add(oct, "month", 10);
ds_map_add(oct, "days", 31);
ds_list_add(monthData, oct);

var nov = ds_map_create();
ds_map_add(mar, "name", "November");
ds_map_add(mar, "shortName", "Nov");
ds_map_add(mar, "month", 11);
ds_map_add(mar, "days", 30);
ds_list_add(monthData, mar);

var dec = ds_map_create();
ds_map_add(dec, "name", "December");
ds_map_add(dec, "shortName", "Dec");
ds_map_add(dec, "month", 12);
ds_map_add(dec, "days", 31);
ds_list_add(monthData, dec);

return monthData;

This could probably be refactored into a less redundant script using another helper function, or even converted into a json/csv file that is read and imported.

Finally, we add the generate_clock_strings() script.

generate_clock_strings() simply takes the current date and time and generates useful strings for use in draw_text or other scenarios where a string representation of the time and date is needed.

///generate_clock_strings();

//generate seconds string
if(seconds < 10){
    secondsString = "0" + string(seconds);	
} else {
    secondsString = string(seconds);	
}

//generate minutes string
if(minutes < 10){
    minutesString = "0" + string(minutes);	
} else {
    minutesString = string(minutes);	
}


if(Game.timeFormat == "TimeFormat12"){
    //generate hours string in 12 hour format
    if(hours > 12){
        var tmpHours = hours - 12;	
    } else if(hours == 0){
        var tmpHours = 12;	
    } else {
        var tmpHours = hours;
    }
    if(hours < 12){
        period = "AM";
    }
    if(hours >= 12){
        period = "PM";	
    }
    if(tmpHours < 10){
        hoursString = "0" + string(tmpHours);	
    } else {
        hoursString = string(tmpHours);
    }
    timeString = hoursString + ":" + minutesString + ":" + secondsString + " " + period;
} else {
    //generate hours string in 24 hour format
    if(hours < 10){
        hoursString = "0" + string(hours);	
    } else {
        hoursString = string(hours);	
    }
    timeString = hoursString + ":" + minutesString + ":" + secondsString;
}

//generate days string
if(days < 10){
    daysString = "0" + string(days);	
} else {
    daysString = string(days);
}

//generate months string
if(months < 10){
    monthsString = "0" + string(months);	
} else {
    monthsString = string(months);
}

//generate date string in compact format
if(Game.dateFormat == "DateFormatUS"){
    dateString = monthsString + "/" + daysString + "/" + string(years);	
} else {
    dateString = daysString + "/" + monthsString + "/" + string(years);
}

//generate date string in long format
var monthMeta = monthData[| months - 1];
if(Game.dateFormat == "DateFormatUS"){
    longDateString = monthMeta[? "name"] + " " + daysString + " Year " + string(years);
} else {
    longDateString =  + daysString + " " + monthMeta[? "name"] + " Year " + string(years);
}

One thing to note is the usage of two more variables: Game.timeFormat and Game.dateFormat. I have these set in the Game object to control whether the user wants to see the 12-hour/24-hour time format and the MM/DD or DD/MM date format.

Leave a Reply

Your email address will not be published. Required fields are marked *