Time: Difference between revisions
Initial addition of script API for system time. |
|||
| (23 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
== Introduction == | == Introduction == | ||
Timing is crucial to OpenBOR, as it is to most game engines. Understanding how the engine measures time, and how those measurements affect a project, is therefore essential. | Timing is crucial to OpenBOR, as it is to most game engines. Understanding how the engine measures time, and how those measurements affect a project, is therefore essential. | ||
OpenBOR uses a modern decoupled timing model by default. This means gameplay simulation and display rendering operate as independent loops, with the simulation advancing at regular 5-millisecond intervals. | |||
The gameplay loop advances movement, animation, collision, combat, entity behavior, and other world systems according to the logical clock. The rendering loop draws the latest available world state according to the selected display rate. Since neither loop depends on the other to advance, changes in display performance, refresh rate, or VSync behavior do not ordinarily change the speed or timing of gameplay. | |||
=== Logical Time vs. FPS === | |||
Logical time controls how frequently OpenBOR updates the simulated game world. Frames per second, or FPS, control how frequently the display loop renders that world. These are separate rates serving different purposes. | |||
OpenBOR’s logical clock runs at 200 ticks per second by default. Each logical tick therefore represents 5 milliseconds. The display loop, running at 60 FPS, renders once every 16.67 milliseconds. Since the two loops are decoupled, they do not need to align one-to-one. Under these default rates, OpenBOR ordinarily processes three or four logical ticks between displayed frames. | |||
Gameplay durations remain based on logical time regardless of how often the world is drawn. Running the display loop at 60, 200, 500, VSync, or an unrestricted rate does not reinterpret an animation delay, effect duration, movement timer, or other logical-time value. | |||
OpenBOR also provides an optional frame-aligned timing mode for projects that use traditional frame-count conventions. Creators may configure the logical clock to a rate evenly divisible by the desired frame rate and express animation delays as direct logical ticks. For example, a 180 Hz logical clock provides exactly three logical ticks for every interval of a 60 Hz timeline: | |||
<code>delay 3 direct</code> | |||
This represents one 60 Hz frame interval. Values of <code>6 direct</code>, <code>9 direct</code>, and <code>12 direct</code> represent two, three, and four frame intervals respectively. Frame-based timing can therefore be expressed exactly without coupling gameplay simulation to display rendering. | |||
See [[Animation Overview#Logical clock precision|Logical clock precision]] for more information about animation timing and direct delays. | |||
OpenBOR exposes the following primary measurements of time: | |||
=== Game Time === | |||
Game Time is the traditional beat ’em up countdown used to maintain the pace of a level. It is a gameplay rule rather than the engine’s logical clock. | |||
When Game Time expires, players ordinarily lose a life. Creators may disable, remove, replace, or otherwise modify this behavior. | |||
See [[#Game Time|Game Time]]. | |||
=== Elapsed Time === | |||
Elapsed Time is the current level’s logical clock. It begins at `0` when active level gameplay starts and increments once for every processed logical tick. | |||
At the default 200 Hz logical rate, each increment represents 5 milliseconds. Animation timing, effect durations, entity simulation, and most other gameplay processes use Elapsed Time or values derived from it. | |||
Time | See [[#Elapsed Time|Elapsed Time]]. | ||
=== Ticks === | |||
Ticks measure the number of milliseconds elapsed since OpenBOR started. Unlike Elapsed Time, Ticks are not limited to an active level and do not reset when a level begins. | |||
Ticks are useful for loading operations, menus, recorded input, diagnostics, and script timers that must remain available outside active gameplay. | |||
See [[#Ticks|Ticks]]. | |||
=== System Time === | |||
System Time provides optional access to the host device’s real-world clock, including Unix timestamps, calendar dates, local time, and Coordinated Universal Time. | |||
Native gameplay does not depend on System Time. Creators may use it through script for features such as dated save files, logging, calendar events, or other intentionally real-world behavior. | |||
See [[#System Time|System Time]]. | |||
== Game Time == | == Game Time == | ||
Game time is a default countdown timer that appears at top center of the screen during an active level. It starts at 99 and decrements once every two real world seconds. At 0, all active players take damage equal to their current HP, and the game is over if no lives or credits remain. Time resets automatically when players clear a designated level wait and at the start of each new level. [[File:Batman savage dawn a.png|alt=Batman Savage Dawn screenshot.|center|frame|The Batman Savage Dawn fan game demonstrates OpenBOR's default game timer in action at top center of the screen.]] | Game time is a default countdown timer that appears at top center of the screen during an active level. It starts at 99 and decrements once every two real world seconds. At 0, all active players take damage equal to their current HP, and the game is over if no lives or credits remain. Time resets automatically when players clear a designated level wait and at the start of each new level. [[File:Batman savage dawn a.png|alt=Batman Savage Dawn screenshot.|center|frame|The Batman Savage Dawn fan game demonstrates OpenBOR's default game timer in action at top center of the screen.]] | ||
| Line 54: | Line 96: | ||
Attack types applied to players when game time expires. | Attack types applied to players when game time expires. | ||
* openborconstant("ATK_TIMEOVER") - Applied to players without a lose animation. | * <code>openborconstant("ATK_TIMEOVER")</code> - Applied to players without a lose animation. | ||
* openborconstant("ATK_LOSE") - Applied to players with a lose animation. | * <code>openborconstant("ATK_LOSE")</code> - Applied to players with a lose animation. | ||
=== Script API === | === Script API === | ||
| Line 80: | Line 122: | ||
== Elapsed Time == | == Elapsed Time == | ||
In terms of game mechanics, | In terms of game mechanics, Elapsed Time is OpenBOR’s most important and frequently used time measurement because it represents the engine’s logical clock. Its implementation, however, is relatively simple. | ||
At the beginning of each level, a global elapsed-time counter is initialized to <code>0</code>. OpenBOR’s logical clock runs at 200 ticks per second (200 Hz) by default, and the counter increments by <code>1</code> for every processed logical tick. | |||
At 200 Hz, each logical tick represents 5 milliseconds, or one-half of a centisecond. Therefore: | |||
* 2 elapsed-time ticks equal 1 centisecond. | |||
* 200 elapsed-time ticks equal 1 second. | |||
OpenBOR’s logical clock operates independently of its outer update and rendering rate. Depending on the selected FPS setting and display synchronization, the outer update cycle may execute faster or slower than the logical clock. Available FPS settings include VSync, fixed limits of 60, 200, or 500 FPS, and an unrestricted option. | |||
Some operations execute once per outer update cycle. These include the global <code>update.c</code> and <code>updated.c</code> script events. Such scripts may therefore execute at a different frequency than the logical clock. | |||
Other operations, including entity simulation and entity update scripts, execute according to logical ticks. | |||
'''Note:''' In case it matters, the | Elapsed Time is therefore reliable as a gameplay timer regardless of the selected outer frame rate. Manually incremented script timers may produce inconsistent results when incremented from an event that executes once per outer update cycle. For timing operations outside active gameplay, where Elapsed Time is unavailable, consider using [[Time#Ticks|Ticks]] instead. | ||
=== Duration Testing === | |||
Most timed systems operate by setting an expiration value equal to the current Elapsed Time plus a predetermined duration. The engine then compares that expiration value against the elapsed-time counter. Once Elapsed Time reaches or passes the expiration value, the timed item may complete, expire, continue to another state, or perform whatever other action its system requires.<syntaxhighlight lang="c" line="1"> | |||
/* Set expiration. */ | |||
int expiration = elapsed_time + duration; | |||
/* | |||
* "Reach or exceed" logic downstream. | |||
*/ | |||
if(elapsed_time >= expiration) { | |||
// Take action. | |||
} | |||
</syntaxhighlight>'''Note:''' In case it matters, the Elapsed Time counter is stored as a 64-bit unsigned integer. At 200 increments per second, a single level could run continuously for roughly 2.9 billion years before the counter rolls over. | |||
=== Script API === | === Script API === | ||
| Line 111: | Line 167: | ||
== Ticks == | == Ticks == | ||
Ticks is a read only measure of milliseconds starting from game boot. OpenBOR natively uses ticks for the player input recording feature and for timing during load screens. Ticks therefore do not generally affect active game-play, but are useful for user content, scripting, and keeping time outside of active game-play elements when | Ticks is a read only measure of milliseconds starting from game boot. OpenBOR natively uses ticks for the player input recording feature and for timing during load screens. Ticks therefore do not generally affect active game-play, but are useful for user content, scripting, and keeping time outside of active game-play elements when Elapsed Time is not natively incremented. | ||
=== Script API === | === Script API === | ||
| Line 121: | Line 177: | ||
== System Time == | == System Time == | ||
System time is entirely optional and provided for creator defined script use. It is not accessed by native engine logic in any way. System is available as a Unix timestamp in either seconds or milliseconds where supported by hardware. Conversion functions allow outputting a custom formatted string or time parts as integer values for use in logic expressions. | System time is entirely optional and provided for creator defined script use. It is not accessed by native engine logic in any way. System time is available as a Unix timestamp in either seconds or milliseconds where supported by hardware. Conversion functions allow outputting a custom formatted string or time parts as integer values for use in logic expressions. | ||
=== Script API === | === Script API === | ||
| Line 127: | Line 183: | ||
==== Get Current Unix Timestamp ==== | ==== Get Current Unix Timestamp ==== | ||
Returns the current system time as the number of seconds elapsed since | Returns the current system time as the number of seconds elapsed since <code>1970-01-01 00:00:00 UTC</code>. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c" line="1"> | ||
int timestamp = datetime_gettimestamp(); | int timestamp = datetime_gettimestamp(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 135: | Line 191: | ||
==== Get Current Unix Timestamp in Milliseconds ==== | ==== Get Current Unix Timestamp in Milliseconds ==== | ||
Returns the current system time as the number of milliseconds elapsed since | Returns the current system time as the number of milliseconds elapsed since <code>1970-01-01 00:00:00 UTC</code>. | ||
'''Note:''' Millisecond Unix timestamps require a 64-bit integer value. | '''Note:''' Millisecond Unix timestamps require a 64-bit integer value. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c" line="1"> | ||
int timestamp = datetime_gettimestampms(); | int timestamp = datetime_gettimestampms(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 145: | Line 201: | ||
==== Format Current Local Date and Time ==== | ==== Format Current Local Date and Time ==== | ||
Formats the current system time as a string using the host system's local time standard. | Formats the current system time as a string using the host system's local time standard. The format string uses standard [https://pubs.opengroup.org/onlinepubs/007904875/utilities/date.html datetime conversion codes]. | ||
'''Note:''' Names such as weekdays and months may vary according to the host system's configured language and locale. | |||
<syntaxhighlight lang="c" line="1"> | |||
void date_time = datetime_format("%Y-%m-%d %H:%M:%S"); | |||
</syntaxhighlight> | |||
The following examples demonstrate several common formats: | |||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
/* | |||
* ISO-style date and 24-hour time: | |||
* 2026-07-12 14:35:08 | |||
*/ | |||
void date_time = datetime_format("%Y-%m-%d %H:%M:%S"); | |||
/* | |||
* Month, day, and year: | |||
* 07/12/2026 | |||
*/ | |||
void date = datetime_format("%m/%d/%Y"); | |||
/* | |||
* Full weekday and month names: | |||
* Sunday, July 12, 2026 | |||
*/ | |||
void written_date = datetime_format("%A, %B %d, %Y"); | |||
/* | |||
* Abbreviated weekday and month names: | |||
* Sun, Jul 12, 2026 | |||
*/ | |||
void short_date = datetime_format("%a, %b %d, %Y"); | |||
/* | |||
* 12-hour time with AM or PM: | |||
* 02:35:08 PM | |||
*/ | |||
void time_12_hour = datetime_format("%I:%M:%S %p"); | |||
/* | |||
* 24-hour time: | |||
* 14:35:08 | |||
*/ | |||
void time_24_hour = datetime_format("%H:%M:%S"); | |||
/* | |||
* Year, month, day, hour, and minute: | |||
* 20260712_1435 | |||
* | |||
* Useful for save names, logs, and generated filenames. | |||
*/ | |||
void file_timestamp = datetime_format("%Y%m%d_%H%M"); | |||
/* | |||
* Day of the year: | |||
* Day 193 of 2026 | |||
*/ | |||
void year_progress = datetime_format("Day %j of %Y"); | |||
/* | |||
* Weekday name: | |||
* Sunday | |||
*/ | |||
void weekday = datetime_format("%A"); | |||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 155: | Line 273: | ||
Formats a supplied Unix timestamp expressed in milliseconds. | Formats a supplied Unix timestamp expressed in milliseconds. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c" line="1"> | ||
int timestamp = 1783890000000; | int timestamp = 1783890000000; | ||
void date_time = datetime_format("%Y-%m-%d %H:%M:%S", timestamp); | |||
); | |||
</syntaxhighlight> | </syntaxhighlight> | ||
==== Format Date and Time as UTC ==== | ==== Format Date and Time as UTC ==== | ||
All format and date part functions can accept the following constants: | |||
* <code>opeborcosntat("DATETIME_STANDARD_LOCAL")</code> - Use local system time (default). | |||
* <code>opeborcosntat("DATETIME_STANDARD_UTC")</code> - Use Coordinated Universal Time. | |||
Formats a supplied Unix timestamp using Coordinated Universal Time instead of the host system's local time. | Formats a supplied Unix timestamp using Coordinated Universal Time instead of the host system's local time. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c" line="1"> | ||
int timestamp = datetime_gettimestampms(); | int timestamp = datetime_gettimestampms(); | ||
void date_time = datetime_format("%Y-%m-%d %H:%M:%S", timestamp, opeborcosntat("DATETIME_STANDARD_UTC")); </syntaxhighlight> | |||
"%Y-%m-%d %H:%M:%S", | |||
timestamp, | |||
DATETIME_STANDARD_UTC | |||
); </syntaxhighlight> | |||
==== Get Current Year ==== | ==== Get Current Year ==== | ||
| Line 180: | Line 296: | ||
Returns the current four-digit year using the host system's local time standard. | Returns the current four-digit year using the host system's local time standard. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c" line="1"> | ||
int year = datetime_getyear(); | int year = datetime_getyear(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 188: | Line 304: | ||
Returns the current calendar month as an integer. | Returns the current calendar month as an integer. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c" line="1"> | ||
int month = datetime_getmonth(); | int month = datetime_getmonth(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 196: | Line 312: | ||
Returns the current day of the month as an integer. | Returns the current day of the month as an integer. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c" line="1"> | ||
int day = datetime_getday(); | int day = datetime_getday(); | ||
</syntaxhighlight> | |||
==== Get Current Weekday ==== | |||
Return the current day of the week as an integer corresponding to one of the following constants:<syntaxhighlight lang="c" line="1"> | |||
openborconstant("DATETIME_WEEKDAY_SUNDAY") | |||
openborconstant("DATETIME_WEEKDAY_MONDAY") | |||
openborconstant("DATETIME_WEEKDAY_TUESDAY") | |||
openborconstant("DATETIME_WEEKDAY_WEDNESDAY") | |||
openborconstant("DATETIME_WEEKDAY_THURSDAY") | |||
openborconstant("DATETIME_WEEKDAY_FRIDAY") | |||
openborconstant("DATETIME_WEEKDAY_SATURDAY") | |||
</syntaxhighlight><syntaxhighlight lang="c" line="1"> | |||
int weekday = datetime_getweekday(); | |||
/* Is it Monday? */ | |||
if(weekday == openborconstant("DATETIME_WEEKDAY_MONDAY")){ | |||
log("Just another Manic Monday."); | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 204: | Line 338: | ||
Returns the current hour using the host system's local time standard. | Returns the current hour using the host system's local time standard. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c" line="1"> | ||
int hour = datetime_gethour(); | int hour = datetime_gethour(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 212: | Line 346: | ||
Returns the current minute of the hour. | Returns the current minute of the hour. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c" line="1"> | ||
int minute = datetime_getminute(); | int minute = datetime_getminute(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 220: | Line 354: | ||
Returns the current second of the minute. | Returns the current second of the minute. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c" line="1"> | ||
int second = datetime_getsecond(); | int second = datetime_getsecond(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 228: | Line 362: | ||
Each component function optionally accepts a Unix timestamp expressed in milliseconds. This allows creators to extract individual date and time values from a stored timestamp. | Each component function optionally accepts a Unix timestamp expressed in milliseconds. This allows creators to extract individual date and time values from a stored timestamp. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c" line="1"> | ||
int timestamp = 1783890000000; | int timestamp = 1783890000000; | ||
| Line 240: | Line 374: | ||
==== Get Date and Time Parts as UTC ==== | ==== Get Date and Time Parts as UTC ==== | ||
Pass | Pass <code>opeborcosntat("DATETIME_STANDARD_UTC")</code> as the second argument to interpret the supplied timestamp as Coordinated Universal Time. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c" line="1"> | ||
int timestamp = datetime_gettimestampms(); | int timestamp = datetime_gettimestampms(); | ||
int year = datetime_getyear( | int year = datetime_getyear(timestamp, opeborcosntat("DATETIME_STANDARD_UTC")); | ||
timestamp, | |||
DATETIME_STANDARD_UTC | |||
); | |||
int month = datetime_getmonth( | int month = datetime_getmonth(timestamp, opeborcosntat("DATETIME_STANDARD_UTC")); | ||
timestamp, | |||
DATETIME_STANDARD_UTC | |||
); | |||
int day = datetime_getday( | int day = datetime_getday(timestamp, opeborcosntat("DATETIME_STANDARD_UTC")); | ||
timestamp, | |||
DATETIME_STANDARD_UTC | |||
); | |||
int hour = datetime_gethour( | int hour = datetime_gethour(timestamp, opeborcosntat("DATETIME_STANDARD_UTC")); </syntaxhighlight> | ||
timestamp, | |||
DATETIME_STANDARD_UTC | |||
); </syntaxhighlight> | |||
==== Use Current Time with an Explicit Time Standard ==== | ==== Use Current Time with an Explicit Time Standard ==== | ||
| Line 269: | Line 391: | ||
Pass `NULL()` for the optional timestamp argument when selecting a time standard without supplying a custom timestamp. | Pass `NULL()` for the optional timestamp argument when selecting a time standard without supplying a custom timestamp. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c" line="1"> | ||
int utc_hour = datetime_gethour( | int utc_hour = datetime_gethour(NULL(), opeborcosntat("DATETIME_STANDARD_UTC")); | ||
); | |||
</syntaxhighlight> | </syntaxhighlight> | ||
[[Category:Script]] | [[Category:Script]] | ||
[[Category: | [[Category:Openbor]] | ||
[[Category:Game Mechanics]] | |||
Latest revision as of 11:29, 26 August 2026
Introduction
Timing is crucial to OpenBOR, as it is to most game engines. Understanding how the engine measures time, and how those measurements affect a project, is therefore essential.
OpenBOR uses a modern decoupled timing model by default. This means gameplay simulation and display rendering operate as independent loops, with the simulation advancing at regular 5-millisecond intervals.
The gameplay loop advances movement, animation, collision, combat, entity behavior, and other world systems according to the logical clock. The rendering loop draws the latest available world state according to the selected display rate. Since neither loop depends on the other to advance, changes in display performance, refresh rate, or VSync behavior do not ordinarily change the speed or timing of gameplay.
Logical Time vs. FPS
Logical time controls how frequently OpenBOR updates the simulated game world. Frames per second, or FPS, control how frequently the display loop renders that world. These are separate rates serving different purposes.
OpenBOR’s logical clock runs at 200 ticks per second by default. Each logical tick therefore represents 5 milliseconds. The display loop, running at 60 FPS, renders once every 16.67 milliseconds. Since the two loops are decoupled, they do not need to align one-to-one. Under these default rates, OpenBOR ordinarily processes three or four logical ticks between displayed frames.
Gameplay durations remain based on logical time regardless of how often the world is drawn. Running the display loop at 60, 200, 500, VSync, or an unrestricted rate does not reinterpret an animation delay, effect duration, movement timer, or other logical-time value.
OpenBOR also provides an optional frame-aligned timing mode for projects that use traditional frame-count conventions. Creators may configure the logical clock to a rate evenly divisible by the desired frame rate and express animation delays as direct logical ticks. For example, a 180 Hz logical clock provides exactly three logical ticks for every interval of a 60 Hz timeline:
delay 3 direct
This represents one 60 Hz frame interval. Values of 6 direct, 9 direct, and 12 direct represent two, three, and four frame intervals respectively. Frame-based timing can therefore be expressed exactly without coupling gameplay simulation to display rendering.
See Logical clock precision for more information about animation timing and direct delays.
OpenBOR exposes the following primary measurements of time:
Game Time
Game Time is the traditional beat ’em up countdown used to maintain the pace of a level. It is a gameplay rule rather than the engine’s logical clock.
When Game Time expires, players ordinarily lose a life. Creators may disable, remove, replace, or otherwise modify this behavior.
See Game Time.
Elapsed Time
Elapsed Time is the current level’s logical clock. It begins at `0` when active level gameplay starts and increments once for every processed logical tick.
At the default 200 Hz logical rate, each increment represents 5 milliseconds. Animation timing, effect durations, entity simulation, and most other gameplay processes use Elapsed Time or values derived from it.
See Elapsed Time.
Ticks
Ticks measure the number of milliseconds elapsed since OpenBOR started. Unlike Elapsed Time, Ticks are not limited to an active level and do not reset when a level begins.
Ticks are useful for loading operations, menus, recorded input, diagnostics, and script timers that must remain available outside active gameplay.
See Ticks.
System Time
System Time provides optional access to the host device’s real-world clock, including Unix timestamps, calendar dates, local time, and Coordinated Universal Time.
Native gameplay does not depend on System Time. Creators may use it through script for features such as dated save files, logging, calendar events, or other intentionally real-world behavior.
See System Time.
Game Time
Game time is a default countdown timer that appears at top center of the screen during an active level. It starts at 99 and decrements once every two real world seconds. At 0, all active players take damage equal to their current HP, and the game is over if no lives or credits remain. Time resets automatically when players clear a designated level wait and at the start of each new level.

Set Time
settime {int time}
#default
settime 99
Level header command that sets value applied to timer on level start and native resets.
- Accepts any integer value from
0to99. Out of bounds values are ignored. 0= infinite time.
HUD Display
For configuring how timer displays on screen, see Heads Up Display.
Other
data/sounds/timeover.wav
If available, plays once when the game timer expires.
anim lose
If available, entity plays this animation in place of normal death when game timer expires.
- Accessible to script as
openborconstant("ANI_LOSE").
anim falllose
If available and entity does not have a lose animation, entity plays this animation in place of normal fall death when game timer expires.
- Accessible to script as
openborconstant("ANI_FALLLOSE").
Attack types
Attack types applied to players when game time expires.
openborconstant("ATK_TIMEOVER")- Applied to players without a lose animation.openborconstant("ATK_LOSE")- Applied to players with a lose animation.
Script API
Game Time
Current game time is read and write accessible.
Get
int time = openborvariant("game_time");
Get integer representing game time remaining.
Set
int new_time = 50;
setopenborvariat("game_time", new_time);
Set game time remaining.
Timetick Event
If available, data/scripts/timetick.c fires on each game time decrement. Populates the following local variables.
time- Current game time value.
Elapsed Time
In terms of game mechanics, Elapsed Time is OpenBOR’s most important and frequently used time measurement because it represents the engine’s logical clock. Its implementation, however, is relatively simple.
At the beginning of each level, a global elapsed-time counter is initialized to 0. OpenBOR’s logical clock runs at 200 ticks per second (200 Hz) by default, and the counter increments by 1 for every processed logical tick.
At 200 Hz, each logical tick represents 5 milliseconds, or one-half of a centisecond. Therefore:
- 2 elapsed-time ticks equal 1 centisecond.
- 200 elapsed-time ticks equal 1 second.
OpenBOR’s logical clock operates independently of its outer update and rendering rate. Depending on the selected FPS setting and display synchronization, the outer update cycle may execute faster or slower than the logical clock. Available FPS settings include VSync, fixed limits of 60, 200, or 500 FPS, and an unrestricted option.
Some operations execute once per outer update cycle. These include the global update.c and updated.c script events. Such scripts may therefore execute at a different frequency than the logical clock.
Other operations, including entity simulation and entity update scripts, execute according to logical ticks.
Elapsed Time is therefore reliable as a gameplay timer regardless of the selected outer frame rate. Manually incremented script timers may produce inconsistent results when incremented from an event that executes once per outer update cycle. For timing operations outside active gameplay, where Elapsed Time is unavailable, consider using Ticks instead.
Duration Testing
Most timed systems operate by setting an expiration value equal to the current Elapsed Time plus a predetermined duration. The engine then compares that expiration value against the elapsed-time counter. Once Elapsed Time reaches or passes the expiration value, the timed item may complete, expire, continue to another state, or perform whatever other action its system requires.
/* Set expiration. */
int expiration = elapsed_time + duration;
/*
* "Reach or exceed" logic downstream.
*/
if(elapsed_time >= expiration) {
// Take action.
}
Note: In case it matters, the Elapsed Time counter is stored as a 64-bit unsigned integer. At 200 increments per second, a single level could run continuously for roughly 2.9 billion years before the counter rolls over.
Script API
Get Elapsed Time
int elapsed_time = openborvariant("elapsed_time");
Set Elapsed Time
Be careful about setting elapsed time during game-play, as again, virtually every active game timing property depends on it.
int new_time = 100
setopenborvariant("elapsed_time", new_time);
Ticks
Ticks is a read only measure of milliseconds starting from game boot. OpenBOR natively uses ticks for the player input recording feature and for timing during load screens. Ticks therefore do not generally affect active game-play, but are useful for user content, scripting, and keeping time outside of active game-play elements when Elapsed Time is not natively incremented.
Script API
Get Ticks
int ticks = openborvariant("ticks");
System Time
System time is entirely optional and provided for creator defined script use. It is not accessed by native engine logic in any way. System time is available as a Unix timestamp in either seconds or milliseconds where supported by hardware. Conversion functions allow outputting a custom formatted string or time parts as integer values for use in logic expressions.
Script API
Get Current Unix Timestamp
Returns the current system time as the number of seconds elapsed since 1970-01-01 00:00:00 UTC.
int timestamp = datetime_gettimestamp();
Get Current Unix Timestamp in Milliseconds
Returns the current system time as the number of milliseconds elapsed since 1970-01-01 00:00:00 UTC.
Note: Millisecond Unix timestamps require a 64-bit integer value.
int timestamp = datetime_gettimestampms();
Format Current Local Date and Time
Formats the current system time as a string using the host system's local time standard. The format string uses standard datetime conversion codes.
Note: Names such as weekdays and months may vary according to the host system's configured language and locale.
void date_time = datetime_format("%Y-%m-%d %H:%M:%S");
The following examples demonstrate several common formats:
/*
* ISO-style date and 24-hour time:
* 2026-07-12 14:35:08
*/
void date_time = datetime_format("%Y-%m-%d %H:%M:%S");
/*
* Month, day, and year:
* 07/12/2026
*/
void date = datetime_format("%m/%d/%Y");
/*
* Full weekday and month names:
* Sunday, July 12, 2026
*/
void written_date = datetime_format("%A, %B %d, %Y");
/*
* Abbreviated weekday and month names:
* Sun, Jul 12, 2026
*/
void short_date = datetime_format("%a, %b %d, %Y");
/*
* 12-hour time with AM or PM:
* 02:35:08 PM
*/
void time_12_hour = datetime_format("%I:%M:%S %p");
/*
* 24-hour time:
* 14:35:08
*/
void time_24_hour = datetime_format("%H:%M:%S");
/*
* Year, month, day, hour, and minute:
* 20260712_1435
*
* Useful for save names, logs, and generated filenames.
*/
void file_timestamp = datetime_format("%Y%m%d_%H%M");
/*
* Day of the year:
* Day 193 of 2026
*/
void year_progress = datetime_format("Day %j of %Y");
/*
* Weekday name:
* Sunday
*/
void weekday = datetime_format("%A");
Format a Unix Timestamp
Formats a supplied Unix timestamp expressed in milliseconds.
int timestamp = 1783890000000;
void date_time = datetime_format("%Y-%m-%d %H:%M:%S", timestamp);
Format Date and Time as UTC
All format and date part functions can accept the following constants:
opeborcosntat("DATETIME_STANDARD_LOCAL")- Use local system time (default).opeborcosntat("DATETIME_STANDARD_UTC")- Use Coordinated Universal Time.
Formats a supplied Unix timestamp using Coordinated Universal Time instead of the host system's local time.
int timestamp = datetime_gettimestampms();
void date_time = datetime_format("%Y-%m-%d %H:%M:%S", timestamp, opeborcosntat("DATETIME_STANDARD_UTC"));
Get Current Year
Returns the current four-digit year using the host system's local time standard.
int year = datetime_getyear();
Get Current Month
Returns the current calendar month as an integer.
int month = datetime_getmonth();
Get Current Day
Returns the current day of the month as an integer.
int day = datetime_getday();
Get Current Weekday
Return the current day of the week as an integer corresponding to one of the following constants:
openborconstant("DATETIME_WEEKDAY_SUNDAY")
openborconstant("DATETIME_WEEKDAY_MONDAY")
openborconstant("DATETIME_WEEKDAY_TUESDAY")
openborconstant("DATETIME_WEEKDAY_WEDNESDAY")
openborconstant("DATETIME_WEEKDAY_THURSDAY")
openborconstant("DATETIME_WEEKDAY_FRIDAY")
openborconstant("DATETIME_WEEKDAY_SATURDAY")
int weekday = datetime_getweekday();
/* Is it Monday? */
if(weekday == openborconstant("DATETIME_WEEKDAY_MONDAY")){
log("Just another Manic Monday.");
}
Get Current Hour
Returns the current hour using the host system's local time standard.
int hour = datetime_gethour();
Get Current Minute
Returns the current minute of the hour.
int minute = datetime_getminute();
Get Current Second
Returns the current second of the minute.
int second = datetime_getsecond();
Get Date and Time Parts from a Unix Timestamp
Each component function optionally accepts a Unix timestamp expressed in milliseconds. This allows creators to extract individual date and time values from a stored timestamp.
int timestamp = 1783890000000;
int year = datetime_getyear(timestamp);
int month = datetime_getmonth(timestamp);
int day = datetime_getday(timestamp);
int hour = datetime_gethour(timestamp);
int minute = datetime_getminute(timestamp);
int second = datetime_getsecond(timestamp);
Get Date and Time Parts as UTC
Pass opeborcosntat("DATETIME_STANDARD_UTC") as the second argument to interpret the supplied timestamp as Coordinated Universal Time.
int timestamp = datetime_gettimestampms();
int year = datetime_getyear(timestamp, opeborcosntat("DATETIME_STANDARD_UTC"));
int month = datetime_getmonth(timestamp, opeborcosntat("DATETIME_STANDARD_UTC"));
int day = datetime_getday(timestamp, opeborcosntat("DATETIME_STANDARD_UTC"));
int hour = datetime_gethour(timestamp, opeborcosntat("DATETIME_STANDARD_UTC"));
Use Current Time with an Explicit Time Standard
Pass `NULL()` for the optional timestamp argument when selecting a time standard without supplying a custom timestamp.
int utc_hour = datetime_gethour(NULL(), opeborcosntat("DATETIME_STANDARD_UTC"));