30. Time and Motion
Learn how to handle time and motion in Siv3D.
30.1 Measuring Elapsed Time
Scene::DeltaTime()
returns the elapsed time from the previous frame (in seconds) as a double
type
This value can be used to create motion that is independent of frame rate
For details, see Tutorial 14
Generally, if the elapsed time from the previous frame is too large, animation and physics simulation steps in the game become large, potentially compromising stability
Therefore, Scene::DeltaTime()
is limited to not exceed the value of Scene::GetMaxDeltaTime()
(default is 0.1
)
30.2 Accumulating Elapsed Time
Scene::Time()
returns the elapsed time since the program started (in seconds) as a double
type
It is updated when System::Update()
is called, so calls to Scene::Time()
within the same frame return the same value
# include <Siv3D.hpp>
void Main ()
{
Scene :: SetBackground ( ColorF { 0.6 , 0.8 , 0.7 });
const Font font { FontMethod :: MSDF , 48 , Typeface :: Bold };
while ( System :: Update ())
{
font ( U "Time: {:.2f}" _fmt ( Scene :: Time ())). draw ( 40 , Vec2 { 40 , 40 }, ColorF { 0.2 });
}
}
The value returned by Scene::Time()
is the accumulation of Scene::DeltaTime()
As mentioned above, Scene::DeltaTime()
may be shorter than the actual frame elapsed time, so Scene::Time()
may be shorter than real time passage
When time synchronized with reality is needed, use the following methods:
Use Stopwatch
to measure time
Use Timer
to measure time
Use Time::GetMillisec()
to get real time points
30.3 Time-Based Motion
You can express motion by changing position, size, angle, etc. over time
# include <Siv3D.hpp>
void Main ()
{
Scene :: SetBackground ( ColorF { 0.6 , 0.8 , 0.7 });
const Font font { FontMethod :: MSDF , 48 , Typeface :: Bold };
const Texture emoji { U "🍎" _emoji };
while ( System :: Update ())
{
const double t = Scene :: Time ();
RectF { ( t * 50 ), 40 , 40 , 200 }. draw ( ColorF { 0.2 });
Circle { 200 , 400 , ( t * 20 ) }. draw ( Palette :: Seagreen );
emoji . rotated ( t * 90 _deg ). drawAt ( 600 , 400 );
}
}
30.4 Doing Something at Regular Intervals
Decide on an event period in advance, and trigger the event when accumulated time (seconds) exceeds that period (seconds)
Count up every 0.5 seconds # include <Siv3D.hpp>
void Main ()
{
Scene :: SetBackground ( ColorF { 0.6 , 0.8 , 0.7 });
const double interval = 0.5 ;
double accumulatedTime = 0.0 ;
int32 count = 0 ;
while ( System :: Update ())
{
accumulatedTime += Scene :: DeltaTime ();
if ( interval <= accumulatedTime )
{
Print << ++ count ;
accumulatedTime -= interval ;
}
}
}
When the event period is short (shorter than one frame's time), multiple events need to be triggered within one frame
To handle such situations, use while (eventPeriod <= accumulatedTime)
instead of if
Count up every 0.01 seconds # include <Siv3D.hpp>
void Main ()
{
Scene :: SetBackground ( ColorF { 0.6 , 0.8 , 0.7 });
const double interval = 0.01 ;
double accumulatedTime = 0.0 ;
int32 count = 0 ;
while ( System :: Update ())
{
accumulatedTime += Scene :: DeltaTime ();
while ( interval <= accumulatedTime )
{
Print << ++ count ;
accumulatedTime -= interval ;
}
}
}
30.5 Stopwatch
Stopwatch
is a class that conveniently measures elapsed time and resets
Specifying StartImmediately::Yes
in the Stopwatch
constructor starts measurement immediately upon creation
The main member functions of Stopwatch
are as follows:
Code
Description
.start()
Start or resume measurement
.pause()
Pause measurement
.resume()
Resume paused measurement
.reset()
Stop measurement and reset elapsed time to 0
.restart()
Reset measurement and start measuring from 0 again
.isRunning()
Returns bool
indicating whether measurement is in progress
.isPaused()
Returns bool
indicating whether measurement is paused
.isStarted()
Returns bool
indicating whether measurement has started
.min()
Returns elapsed time in minutes as int32
.s()
Returns elapsed time in seconds as int32
.s64()
Returns elapsed time in seconds as int64
.sF()
Returns elapsed time in seconds as double
.ms()
Returns elapsed time in milliseconds as int32
.ms64()
Returns elapsed time in milliseconds as int64
.msF()
Returns elapsed time in milliseconds as double
.us()
Returns elapsed time in microseconds as int32
.us64()
Returns elapsed time in microseconds as int64
.usF()
Returns elapsed time in microseconds as double
.format(...)
Returns elapsed time as a string in the specified format
Elapsed time is not affected by Scene::GetMaxDeltaTime()
and is always measured in real time
There's no need to get elapsed time by individual units
When elapsed time is 65.4 seconds, s()
returns 65
, sF()
returns 65.4
, and ms()
returns 65400
When called multiple times within the same frame, elapsed time may vary depending on timing
# include <Siv3D.hpp>
void Main ()
{
Scene :: SetBackground ( ColorF { 0.6 , 0.8 , 0.7 });
const Font font { FontMethod :: MSDF , 48 , Typeface :: Bold };
// Start measurement immediately upon creation
Stopwatch stopwatch { StartImmediately :: Yes };
while ( System :: Update ())
{
if ( MouseL . down ())
{
if ( stopwatch . isPaused ())
{
// Resume
stopwatch . resume ();
}
else
{
// Pause
stopwatch . pause ();
}
}
if ( MouseR . down ())
{
// Restart
stopwatch . restart ();
}
RectF { 0 , 200 , ( stopwatch . sF () * 100 ), 200 }. draw ();
font ( stopwatch . format ( U "mm:ss.xx" )). draw ( 40 , Vec2 { 40 , 40 }, ColorF { 0.2 });
}
}
30.6 Time Types
The following types are available to represent time:
Types with F
hold values as floating point numbers
Duration
is an alias for SecondsF
Type
Time Represented
Days
or DaysF
Days
Hours
or HoursF
Hours
Minutes
or MinutesF
Minutes
Seconds
or SecondsF
Seconds
Milliseconds
or MillisecondsF
Milliseconds
Microseconds
or MicrosecondsF
Microseconds
Nanoseconds
or NanosecondsF
Nanoseconds
Duration
Alias for SecondsF
You can easily create time types by adding time literal suffixes to integer or floating point literals
For example, 10s
is the same as Seconds{ 10 }
, and 0.5s
is the same as SecondsF{ 0.5 }
Suffix
Time
_d
Days
h
Hours
min
Minutes
s
Seconds
ms
Milliseconds
us
Microseconds
ns
Nanoseconds
Time types support arithmetic and comparison operations
Time types can be converted between each other
Converting from floating point time types → integer time types requires DurationCast<Type>()
# include <Siv3D.hpp>
void Main ()
{
Scene :: SetBackground ( ColorF { 0.6 , 0.8 , 0.7 });
const MinutesF m = 3 min ;
const SecondsF s = 5.5 s ;
Print << ( m + s );
const Seconds s2 = DurationCast < Seconds > ( s );
Print << s2 ;
const Duration d = 123.456 s ;
Print << d ;
while ( System :: Update ())
{
}
}
30.7 Timer
Timer
is a class that measures remaining time with countdown from a specified time
Specifying StartImmediately::Yes
in the Timer
constructor starts measurement immediately upon creation
The main member functions of Timer
are as follows:
Code
Description
.start()
Start or resume the timer
.pause()
Pause the timer
.resume()
Resume the paused timer
.reset()
Stop the timer and reset remaining time
.restart()
Reset the timer and start again
.isRunning()
Returns bool
indicating whether measurement is in progress
.isPaused()
Returns bool
indicating whether measurement is paused
.isStarted()
Returns bool
indicating whether measurement has started
.reachedZero()
Returns bool
indicating whether remaining time has reached 0
.min()
Returns remaining time in minutes as int32
.s()
Returns remaining time in seconds as int32
.s64()
Returns remaining time in seconds as int64
.sF()
Returns remaining time in seconds as double
.ms()
Returns remaining time in milliseconds as int32
.ms64()
Returns remaining time in milliseconds as int64
.msF()
Returns remaining time in milliseconds as double
.us()
Returns remaining time in microseconds as int32
.us64()
Returns remaining time in microseconds as int64
.usF()
Returns remaining time in microseconds as double
.progress1_0()
Returns timer progress (starts at 1.0, ends at 0.0) as double
.progress0_1()
Returns timer progress (starts at 0.0, ends at 1.0) as double
Remaining time is not affected by Scene::GetMaxDeltaTime()
and is always measured in real time
There's no need to get remaining time by individual units
When remaining time is 65.4 seconds, s()
returns 65
, sF()
returns 65.4
, and ms()
returns 65400
# include <Siv3D.hpp>
void Main ()
{
Scene :: SetBackground ( ColorF { 0.6 , 0.8 , 0.7 });
const Font font { FontMethod :: MSDF , 48 , Typeface :: Bold };
// Start measurement immediately upon creation
Timer timer { 10 s , StartImmediately :: Yes };
while ( System :: Update ())
{
if ( MouseL . down ())
{
if ( timer . isPaused ())
{
// Resume
timer . resume ();
}
else
{
// Pause
timer . pause ();
}
}
if ( MouseR . down ())
{
// Restart
timer . restart ();
}
RectF { 0 , 200 , ( timer . progress1_0 () * 800 ), 200 }. draw ();
if ( timer . reachedZero ())
{
font ( U "Time's up!" ). draw ( 40 , Vec2 { 40 , 40 }, Palette :: Red );
}
else
{
font ( timer . format ( U "mm:ss.xx" )). draw ( 40 , Vec2 { 40 , 40 }, ColorF { 0.2 });
}
}
}
30.8 Time Comparison
You can compare Stopwatch
or Timer
objects with time type values
Instead of if (3 <= stopwatch.s())
, you can use if (3s <= stopwatch)
to check if the stopwatch has elapsed 3 seconds or more
Instead of if (timer.sF() < 10.0)
, you can use if (timer < 10s)
to check if the timer has less than 10 seconds remaining
# include <Siv3D.hpp>
void Main ()
{
Scene :: SetBackground ( ColorF { 0.6 , 0.8 , 0.7 });
const Font font { FontMethod :: MSDF , 48 , Typeface :: Bold };
// Start measurement immediately upon creation
Stopwatch stopwatch { StartImmediately :: Yes };
while ( System :: Update ())
{
// If 2 seconds or more have elapsed
if ( 2 s <= stopwatch )
{
Circle { 200 , 300 , 50 }. draw ();
}
// If 4 seconds or more have elapsed
if ( 4 s <= stopwatch )
{
Circle { 400 , 300 , 50 }. draw ();
}
// If 6 seconds or more have elapsed
if ( 6 s <= stopwatch )
{
Circle { 600 , 300 , 50 }. draw ();
}
font ( stopwatch . format ( U "mm:ss.xx" )). draw ( 40 , Vec2 { 40 , 40 }, ColorF { 0.2 });
}
}
30.9 Periodic Motion
When you need values that periodically change between 0 ↔ 1 or -1 ↔ 1, it's convenient to use periodic functions provided in the Periodic::
namespace
These functions return values in the range 0~1 or -1~1 with specific periods and patterns based on time passage
Periodic::function(period, elapsed time)
The period is specified using time literals like 2s
or 0.5s
Elapsed time (seconds) is passed as double
type. By default, Scene::Time()
is used
Periodic functions 0~1 Periodic functions -1~1
Periodic Function
Motion
Periodic::Square0_1
Periodic::Triangle0_1
Periodic::Sine0_1
Periodic::Sawtooth0_1
Periodic::Jump0_1
Periodic::Square0_1()
Returns 0.0 or 1.0 alternately at the specified period
Returns 1.0 in the first half of the period and 0.0 in the remaining half
Periodic::Triangle0_1()
Repeats the change from 0.0 increasing at constant speed to 1.0, then decreasing at constant speed back to 0.0 at the specified period
Periodic::Sine0_1()
Returns numeric changes that draw a sine curve in the range 0.0~1.0 at the specified period
Periodic::Sawtooth0_1()
Repeats the change from 0.0 → 1.0 at the specified period
Periodic::Jump0_1()
Repeats numeric changes like velocity when jumping from the ground at the specified period
Sample Code
# include <Siv3D.hpp>
void Main ()
{
Scene :: SetBackground ( ColorF { 0.6 , 0.8 , 0.7 });
while ( System :: Update ())
{
const double p0 = Periodic :: Square0_1 ( 2 s );
const double p1 = Periodic :: Triangle0_1 ( 2 s );
const double p2 = Periodic :: Sine0_1 ( 2 s );
const double p3 = Periodic :: Sawtooth0_1 ( 2 s );
const double p4 = Periodic :: Jump0_1 ( 2 s );
Line { 100 , 0 , 100 , 600 }. draw ( 2 , ColorF { 0.2 });
Line { 700 , 0 , 700 , 600 }. draw ( 2 , ColorF { 0.2 });
Circle { ( 100 + p0 * 600 ), 100 , 20 }. draw ( ColorF { 0.2 });
Circle { ( 100 + p1 * 600 ), 200 , 20 }. draw ( ColorF { 0.2 });
Circle { ( 100 + p2 * 600 ), 300 , 20 }. draw ( ColorF { 0.2 });
Circle { ( 100 + p3 * 600 ), 400 , 20 }. draw ( ColorF { 0.2 });
Circle { ( 100 + p4 * 600 ), 500 , 20 }. draw ( ColorF { 0.2 });
}
}
Periodic Function
Motion
Periodic::Square1_1
Periodic::Triangle1_1
Periodic::Sine1_1
Periodic::Sawtooth1_1
Periodic::Jump1_1
Periodic::Square1_1()
Returns -1.0 or 1.0 alternately at the specified period
Returns 1.0 in the first half of the period and -1.0 in the remaining half
Periodic::Triangle1_1()
Repeats the change from -1.0 increasing at constant speed to 1.0, then decreasing at constant speed back to -1.0 at the specified period
Periodic::Sine1_1()
Returns numeric changes that draw a sine curve in the range -1.0~1.0 at the specified period
Periodic::Sawtooth1_1()
Repeats the change from -1.0 → 1.0 at the specified period
Periodic::Jump1_1()
Repeats numeric changes like velocity when jumping from the ground at the specified period
Sample Code
# include <Siv3D.hpp>
void Main ()
{
Scene :: SetBackground ( ColorF { 0.6 , 0.8 , 0.7 });
while ( System :: Update ())
{
const double p0 = Periodic :: Square1_1 ( 2 s );
const double p1 = Periodic :: Triangle1_1 ( 2 s );
const double p2 = Periodic :: Sine1_1 ( 2 s );
const double p3 = Periodic :: Sawtooth1_1 ( 2 s );
const double p4 = Periodic :: Jump1_1 ( 2 s );
Line { 100 , 0 , 100 , 600 }. draw ( 2 , ColorF { 0.2 });
Line { 700 , 0 , 700 , 600 }. draw ( 2 , ColorF { 0.2 });
Circle { ( 400 + p0 * 300 ), 100 , 20 }. draw ( ColorF { 0.2 });
Circle { ( 400 + p1 * 300 ), 200 , 20 }. draw ( ColorF { 0.2 });
Circle { ( 400 + p2 * 300 ), 300 , 20 }. draw ( ColorF { 0.2 });
Circle { ( 400 + p3 * 300 ), 400 , 20 }. draw ( ColorF { 0.2 });
Circle { ( 400 + p4 * 300 ), 500 , 20 }. draw ( ColorF { 0.2 });
}
}
30.10 Transition
When you need to process "gradually approach 1.0 while a condition is met, gradually return to 0.0 when the condition is not met," the Transition
class is convenient
The Transition
constructor sets the minimum time required to reach from 0.0 to 1.0 and the minimum time to decrease from maximum to minimum value
Call .update(state)
every frame, passing true
for increase and false
for decrease, and the value will increase/decrease at the set speed
You can get the current value with .value()
# include <Siv3D.hpp>
void Main ()
{
Scene :: SetBackground ( ColorF { 0.6 , 0.8 , 0.7 });
const Font font { FontMethod :: MSDF , 48 , Typeface :: Bold };
Transition transition { 1.0 s , 0.25 s };
while ( System :: Update ())
{
// Increase if left mouse button is pressed, otherwise decrease
transition . update ( MouseL . pressed ());
RectF { 0 , 200 , ( transition . value () * 800 ), 200 }. draw ();
font ( U "{:.2f}" _fmt ( transition . value ())). draw ( 40 , Vec2 { 40 , 40 }, ColorF { 0.2 });
}
}
30.11 Linear Interpolation
When you have states A and B and want to interpolate between them with interpolation coefficient t
, use A.lerp(B, t)
The interpolation coefficient t
is usually in the range 0.0 ~ 1.0
The following classes have the member function .lerp()
:
Element
Class
Color
ColorF
, HSV
Vector
Point
, Vec2
, Vec3
, Vec4
Shape
Line
, Circle
, Rect
, RectF
, Triangle
, Quad
, Ellipse
, RoundRect
# include <Siv3D.hpp>
void Main ()
{
Scene :: SetBackground ( ColorF { 0.6 , 0.8 , 0.7 });
const ColorF color0 { 0.1 , 0.5 , 1.0 };
const ColorF color1 { 0.1 , 1.0 , 0.5 };
const Circle circle0 { 100 , 200 , 20 };
const Circle circle1 { 700 , 200 , 40 };
const RectF rect0 { Arg :: center ( 100 , 300 ), 80 };
const RectF rect1 { Arg :: center ( 700 , 300 ), 40 };
const Triangle triangle0 { 100 , 400 , 100 , 0 _deg };
const Triangle triangle1 { 700 , 400 , 100 , 120 _deg };
const Line line0 { 50 , 450 , 150 , 550 };
const Line line1 { 750 , 450 , 650 , 550 };
while ( System :: Update ())
{
const double t = Periodic :: Triangle0_1 ( 3 s );
RectF { 200 , 50 , 400 , 80 }. draw ( color0 . lerp ( color1 , t ));
circle0 . lerp ( circle1 , t ). draw ( ColorF { 0.2 });
rect0 . lerp ( rect1 , t ). draw ( ColorF { 0.2 });
triangle0 . lerp ( triangle1 , t ). draw ( ColorF { 0.2 });
line0 . lerp ( line1 , t ). draw ( 2 , ColorF { 0.2 });
}
}
30.12 Easing
Simply increasing values linearly (at constant speed) from 0.0 to 1.0 results in monotonous motion
Adding speed variations, such as gradually accelerating at the beginning and slowing down near the goal, can achieve sophisticated visual effects
Easing functions that can transform 0.0 ↔ 1.0 movement into characteristic curves can improve motion impressions
About 30 types of easing functions are available, and you can check the list at Easing Functions Cheat Sheet
# include <Siv3D.hpp>
void Main ()
{
Scene :: SetBackground ( ColorF { 0.6 , 0.8 , 0.7 });
// Start position
Vec2 from { 100 , 100 };
// Goal position
Vec2 to { 700 , 500 };
Stopwatch stopwatch { StartImmediately :: Yes };
while ( System :: Update ())
{
// Movement ratio 0.0~1.0
const double t = Min ( stopwatch . sF (), 1.0 );
// Apply easing function
const double e = EaseInOutExpo ( t );
// Position progressed by ratio e from start to goal position
const Vec2 pos = from . lerp ( to , e );
if ( MouseL . down ())
{
// Set start position to current position
from = pos ;
// Set goal position to mouse cursor position
to = Cursor :: Pos ();
stopwatch . restart ();
}
pos . asCircle ( 40 ). draw ( ColorF { 0.2 });
to . asCircle ( 50 ). drawFrame ( 5 , ColorF { 0.2 });
}
}
30.13 SmoothDamp
Linear interpolation and easing are suitable for cases where start and end values (target values) are fixed
However, when the target value changes during movement, speed and direction change abruptly, giving an unnatural impression
To continue smooth movement/change considering current velocity even when target values change, use the Math::SmoothDamp
function
The Math::SmoothDamp
function is a very convenient and powerful interpolation function that calculates the next position based on time from current position, target position, and current velocity
The following types support the Math::SmoothDamp
function:
Element
Type/Class
Numeric types
float
, double
Vector
Vec2
, Vec3
, Vec4
Color
ColorF
More classes are planned to be supported in Siv3D v0.8
Function Overview
The Math::SmoothDamp
function for Vec2
is as follows:
Vec2 Math::SmoothDamp ( const Vec2 & from , const Vec2 & to , Vec2 & velocity , double smoothTime , const Optional < double >& maxSpeed = unspecified , double deltaTime = Scene :: DeltaTime ()); `
from
: Current position
to
: Target position
velocity
: Current velocity (pass the variable storing velocity by reference)
smoothTime
: Smoothing time (expected time required when moving toward target at maximum speed). Delay time when chasing a moving target; smaller values reach the target faster
maxSpeed
: Maximum speed. Specify unspecified
for unlimited
deltaTime
: Elapsed time from previous frame (default is Scene::DeltaTime()
)
Return value: Next position
# include <Siv3D.hpp>
void Main ()
{
Scene :: SetBackground ( ColorF { 0.6 , 0.8 , 0.7 });
// Current position
Vec2 currentPos { 100 , 100 };
// Movement velocity
Vec2 velocity { 0 , 0 };
while ( System :: Update ())
{
// Target position
const Vec2 targetPos = Cursor :: Pos ();
currentPos = Math :: SmoothDamp ( currentPos , targetPos , velocity , 0.5 );
currentPos . asCircle ( 40 ). draw ( ColorF { 0.2 });
targetPos . asCircle ( 50 ). drawFrame ( 4 , ColorF { 0.2 });
}
}
30.14 Getting Application Launch Time
To get the elapsed time since application startup in real time, use the following functions:
Return value is uint64
type
Code
Description
Time::GetSec()
Returns elapsed time since application startup in seconds
Time::GetMillisec()
Returns elapsed time since application startup in milliseconds
Time::GetMicrosec()
Returns elapsed time since application startup in microseconds
Time::GetNanosec()
Returns elapsed time since application startup in nanoseconds
# include <Siv3D.hpp>
void Main ()
{
while ( System :: Update ())
{
ClearPrint ();
Print << Time :: GetSec ();
Print << Time :: GetMillisec ();
Print << Time :: GetMicrosec ();
Print << Time :: GetNanosec ();
}
}
30.15 Getting UNIX Time
To get the elapsed time since January 1, 1970, 00:00:00 (UNIX epoch) (UNIX time), use the following functions:
Return value is uint64
type
Code
Description
Time::GetSecSinceEpoch()
Returns current UNIX time in seconds
Time::GetMillisecSinceEpoch()
Returns current UNIX time in milliseconds
Time::GetMicrosecSinceEpoch()
Returns current UNIX time in microseconds
# include <Siv3D.hpp>
void Main ()
{
while ( System :: Update ())
{
ClearPrint ();
Print << Time :: GetSecSinceEpoch ();
Print << Time :: GetMillisecSinceEpoch ();
Print << Time :: GetMicrosecSinceEpoch ();
}
}
30.16 Date and Time Class
A class DateTime
is provided for handling dates and times
struct DateTime
{
int32 year ;
int32 month ;
int32 day ;
int32 hour ;
int32 minute ;
int32 second ;
int32 milliseconds ;
};
Addition and subtraction can be performed using time type values
DateTime::Now()
returns the current date and time as DateTime
type
# include <Siv3D.hpp>
void Main ()
{
// Get current date and time
const DateTime t = DateTime :: Now ();
Print << t ;
Print << t . year ;
Print << t . month ;
Print << t . day ;
Print << t . hour ;
Print << t . minute ;
Print << t . second ;
Print << t . milliseconds ;
// 30 minutes ago
Print << ( t - 30 min );
// Next week
Print << ( t + 7 _d );
// Time until 2030
const Duration s = ( DateTime { 2030 , 1 , 1 } - t );
Print << s ;
Print << DaysF { s };
Print << DurationCast < Days > ( s );
while ( System :: Update ())
{
}
}
30.17 Getting Time Zone Offset
Time::UTCOffsetMinutes()
returns the time zone offset in minutes from Coordinated Universal Time (UTC) for the computer being used
# include <Siv3D.hpp>
void Main ()
{
Print << Time :: UTCOffsetMinutes ();
while ( System :: Update ())
{
}
}