WOWCube Docs logo
WOWCube Docs
Mission Control
Section Shortcuts
APIExamplesSourceWOWConnectChangelog
Filters
SDK and language defaults persist in cookies.
SDK version
Navigation Tree
Collapsed by default, focused on the active path.
Made byMcKay Seamons
GitHub
  1. Home
  2. Docs
  3. Examples
  4. Time and Timers
Mission NodeSDK 6.2PawnbasicsProject Included

Time and Timers

Time Time is an essential thing. It is utilized in every computer game, it is used in game logic, animations, visual effects and user input handling. But how...

Examples / SDK 6.2 / Pawn / basics

Time

Time is an essential thing. It is utilized in every computer game, it is used in game logic, animations, visual effects and user input handling.

But how exactly time is measured on WOWCube device?

As we already know, the WOWCube device is built out of eight modules with its own CPU and memory. Each runs CubiOS operating system that hadles hardware components of the module. One of the components is a clock, a device that produces ticks at some very high, constant frequency. Once module is started, it begins to do these ticks and it never stops. So knowing the number of ticks passed between two moments we can actually see how long it took or how much time passed.

Time ticks are very important for all CubiOS internal function too. Graphics on screens, memory, sensors and power - all this obeys the rhythm set by the internal clock. All ongoing processes that run insinde WOWCube use time. If internal clock stops, device turns unresponsive.

So in order to create a cubeapp that implements any kind of dynamics, we would want to use the time as well, right?

But how do we know that tick has elapsed? How do we know how fast this happened?

For that, ON_Tick() callback is used. It is called by CubiOS every time new tick passes, as frequent as device can do.

Using this callback, we can do multiple useful things with time. Most typical tasks are to calculate time elapsed from the last iteration of game logic (i.e last frame) and implement a timer that ticks with given periodicity.

Let's take a look at the example.

Measuring elapsed time

Firstly, we would want to measure time elapsed between two calls of ON_Tick() callback.

In order to calculate the time between, we need to know two things: when the previous tick happened and when the current tick did. For that, variables are declared:

Time and Timers
PAWN
1new previousTime = 0;
2new currentTime = 0;
3new deltaTime = 0;
4
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

And now all we have to do is to see the difference of currentTime and previousTime like this:

Time and Timers
PAWN
1deltaTime = currentTime - previousTime;
2
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

In order to fill the variables with actual time values, getTime() function is used. This function returns current time of the module in milliseconds.

A millisecond (from milli- and second; symbol: ms) is a thousandth (0.001 or 1/1000) of a second.

Thus, subtracting previous time from current time we will get amount of milliseconds passed between two moments in time.

And sice we do this math every time ON_Tick() is called, the deltaTime will be getting a time between two ticks!

Pay attention!

The calling frequency of ON_Tick() callback is not guaranteed to be constant, may vary and depends on the actual CPU load of the module. The load, in turn, depends on a complexity of a code ON_Tick() callback implements. More complex the code is, more CPU load is put.

Implementing a timer

Timer is a specialized type of clock used for measuring specific time intervals.

A principle of implementation of a timer is not much different. Timer checks elapsed time between the ticks and it fires if that time is greater or equal to requested delay value.

Using timers it is very easy to implement recurrent in-game actions that happen each 1, or 5, or 10 seconds.

Let's see the code. First, a timer structure is declared:

Time and Timers
PAWN
1new timer[.delay, .time];
2
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

Timer has two components - delay and time. First one is a desired delay, second one is for storing current time value.

We also declare a variable for keeping a number of seconds passed. This variable will be incremented every time the timer ticks.

Time and Timers
PAWN
1new seconds = 0;
2
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

Now, the timer must be initialized. This is done inside ON_Init() callback that is called when cubeapp is started:

Time and Timers
PAWN
1ON_Init(d, size, const pkt[])
2{
3 previousTime = getTime();
4
5 timer.delay = 1000;
6 timer.time = previousTime;
7}
8
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

Timer delay is initialized with 1000 milliseconds or 1 second.

The last bit is in ON_Tick(): each second the value of seconds is incremented.

Time and Timers
PAWN
1ON_Tick()
2{
3 currentTime = getTime();
4 ...
5 if(currentTime - timer.time >= timer.delay)
6 {
7 timer.time = currentTime;
8 seconds++;
9
10 if(seconds>99) seconds = 0;
11 }
12 ...
13}
14
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

Measuring FPS

Frame rate (expressed in frames per second or FPS) is the frequency (rate) at which consecutive images (frames) are displayed on module screens.

FPS is one of the most important metrics of any computer game with graphics. It is essential to keep it at acceptable rates while the game plays. Sudden drops of FPS value may get user dissatisfied with the game.

The way of calculation of FPS is rather trivial: as long as we know deltaTime, the FPS value would be calculated as

Time and Timers
PAWN
11000.0/deltaTime
2
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

This, however, is where things are getting slightly more complicated on WOWCube platform...

Fixed point arithmetic

At the moment, the only way to deal with fractional values on WOWCube platform is a fixed point arithmetic. Unlike it in other programming languages, WOWCube SDK DOES NOT SUPPORT neither float, nor double variable types.

Why is that? Why not to use common types?

In short, this is done for two reasons:

  • make the code faster
  • make it consume less memory

The WOWCube is a powerful device with eight grunty CPUs, but it is still a very compact, portable piece of hardware that needs top level of overall optimization to deliver the best user experience. Hence the use of fixed point instead of full-fleged floating point rationals.

There is also another, less obvious reason too: fixed point arithmetics defines the precision in an exact and intuitive manner unlike the common floating point arithmetics. Fixed point values have limited precision regardless of how they are implemented. In simple terms it means that while tiny errors in floating point values can build up to larger, noticable errors after repeating operations, with fixed point arithmetics such will never happen. So fixed point, and especially decimal arithmetic is more appropriate for cubeapp applications where all numbers lie in a limited range.

The fixed point format used in WOWCube SDK uses three decimal digits and stores the values in two's complement. This gives a range of -2147483 to +2147482 with 3 digits behing the decimal point.

Fixed point arithmetic also goes by the name of scaled integer arithmetic. Basically, a fixed point number is the numeration of a fraction where the denominator is implied. In WOWCube SDK, the denominator is 1000. Therefore, the integer value of 12345 stands for 12345/1000 or 12.345

Measuring FPS (part II)

So, in order to calculate FPS, we must deal with fractional values. Hence, fixed point values must be used.

The fps variable is declared this way:

Time and Timers
PAWN
1new Fixed:fps = 0;
2
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

Then, its value is calculated in ON_Tick() callback

Time and Timers
PAWN
1ON_Tick()
2{
3 currentTime = getTime();
4 deltaTime = currentTime - previousTime;
5 previousTime = currentTime;
6
7 if(deltaTime>0)
8 fps = fdiv(fixed(1000),fixed(deltaTime));
9 else
10 fps = 0;
11
12 ...
13}
14
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

Here's what's happening in the code:

  • Integer value of 1000 is converted into fixed point value
  • Integer value of deltaTime is converted into fixed point value too
  • First value is divided by second value with fdiv() function
  • The fdiv() function returns fixed point value of fps

Rendering results

It's a final part of the excercise, and it is quite simple.

In order to render time values, three simple steps are taken inside ON_Render() callback:

  • Clear the screen

  • Render the text with values

  • Commit changes

    Time and Timers
    PAWN
    1 for(new screenNumber = 0; screenNumber<3; screenNumber++)
    2 {
    3 GFX_setRenderTarget(screenNumber);
    4 GFX_clear(Colors.black);
    5
    6 GFX_drawText([ 120,80 ], TEXT_SIZE, 0, 0, TEXT_ALIGN_CENTER, Colors.white, "dt: %d ms", deltaTime);
    7 GFX_drawText([ 120,120 ], TEXT_SIZE, 0, 0, TEXT_ALIGN_CENTER, Colors.green, "fps: %q", fps);
    8 GFX_drawText([ 120,160 ], TEXT_SIZE, 0, 0, TEXT_ALIGN_CENTER, Colors.blue, "timer: %d s", seconds);
    9
    10 GFX_render();
    11 }
    12
    Wrapped for easier reading. Turn wrap off to inspect exact line lengths.

The only thing that's left uncovered is the strformat function call.

Check out Working with Strings example to learn more...

Context Rail

Project files

main.pwn
project/src/main.pwn
wowcubeapp-build.json
project/wowcubeapp-build.json
Jump Grid

On This Page

TimeMeasuring elapsed timeImplementing a timerMeasuring FPSFixed point arithmeticMeasuring FPS (part II)Rendering results
Context Rail

Related nodes

info.json
Examples / SDK 6.2 / Pawn / basics / Time and Timers
main.pwn
Examples / SDK 6.2 / Pawn / basics / Time and Timers / project / src
wowcubeapp-build.json
Examples / SDK 6.2 / Pawn / basics / Time and Timers / project
Getting Started
Examples / SDK 6.2 / Pawn / basics
Previous Node
wowcubeapp-build.json
Examples / SDK 6.2 / Pawn / basics / Getting Started / project
Next Node
info.json
Examples / SDK 6.2 / Pawn / basics / Time and Timers