- pass timestamp of frame start time to all update functions

- use timestamp instead of SDL_GetTicks to keep time in Timer class
- use seconds instead of milliseconds in Timer class
This commit is contained in:
ohsqueezy 2023-06-07 20:22:20 -04:00
parent 1ca956b5ac
commit 355ab4d8c4
20 changed files with 181 additions and 158 deletions

View File

@ -2,7 +2,7 @@
Animation::Animation() Animation::Animation()
{ {
timer.toggle(sb::Timer::OFF); timer.off();
} }
void Animation::play(float delay, bool play_once) void Animation::play(float delay, bool play_once)
@ -10,12 +10,12 @@ void Animation::play(float delay, bool play_once)
this->delay = delay; this->delay = delay;
play_state = true; play_state = true;
paused = false; paused = false;
previous_step_time = timer.ms_elapsed(); previous_step_time = timer.elapsed();
overflow = 0; overflow = 0;
ending = play_once; ending = play_once;
if (delay <= 0) if (delay <= 0)
{ {
timer.toggle(true); timer.on();
} }
} }
@ -31,19 +31,19 @@ void Animation::play_once(float delay)
void Animation::pause() void Animation::pause()
{ {
timer.toggle(false); timer.off();
paused = true; paused = true;
} }
void Animation::unpause() void Animation::unpause()
{ {
timer.toggle(true); timer.on();
paused = false; paused = false;
} }
void Animation::reset() void Animation::reset()
{ {
timer.toggle(false); timer.off();
play_state = false; play_state = false;
timer.reset(); timer.reset();
} }
@ -53,25 +53,25 @@ bool Animation::playing(bool include_delay) const
return play_state && !paused && (include_delay || delay <= 0); return play_state && !paused && (include_delay || delay <= 0);
} }
void Animation::update() void Animation::update(float timestamp)
{ {
timer.update(); timer.update(timestamp);
if (playing() && containing_object->is_active()) if (playing() && containing_object->is_active())
{ {
if (delay > 0) if (delay > 0)
{ {
delay -= timer.ms_last_frame(); delay -= timer.frame();
if (delay <= 0) if (delay <= 0)
{ {
timer.toggle(true); timer.on();
} }
} }
if (delay <= 0) if (delay <= 0)
{ {
if (timer.ms_elapsed() - previous_step_time + overflow > frame_length) if (timer.elapsed() - previous_step_time + overflow > frame_length)
{ {
overflow = timer.ms_elapsed() - previous_step_time + overflow - frame_length; overflow = timer.elapsed() - previous_step_time + overflow - frame_length;
previous_step_time = timer.ms_elapsed(); previous_step_time = timer.elapsed();
step(); step();
if (ending) if (ending)
{ {

View File

@ -25,8 +25,7 @@ class Animation
private: private:
bool play_state = false, ending = false, paused = false; bool play_state = false, ending = false, paused = false;
int previous_step_time = 0; float delay = 0.0f, overflow = 0.0f, frame_length = 0.0f, previous_step_time = 0.0f;
float delay = 0, overflow = 0, frame_length = 0;
callback step = nullptr; callback step = nullptr;
Node* containing_object = nullptr; Node* containing_object = nullptr;
sb::Timer timer = sb::Timer(); sb::Timer timer = sb::Timer();
@ -35,19 +34,22 @@ public:
Animation(); Animation();
/* Constructor that allows passing a pointer to an object and a pointer to one of its /*!
* Constructor that allows passing a pointer to an object and a pointer to one of its
* member functions that will be used as the animation function. Frame length can be supplied * member functions that will be used as the animation function. Frame length can be supplied
* in milliseconds, representing how often the animation function will run. If omitted, the * in seconds, representing how often the animation function will run. If omitted, the
* animation function will run every time the Animation object is updated (generally, once per * animation function will run every time the Animation object is updated.
* frame of the application). */ */
template<typename T> template<typename T>
Animation(void(T::*member_function)(), T* object, float frame_length = 0) : frame_length(frame_length) Animation(void(T::*member_function)(), T* object, float frame_length = 0) : frame_length(frame_length)
{ {
bind(member_function, object); bind(member_function, object);
timer.toggle(sb::Timer::OFF); timer.off();
} }
/* Set the animation function by supplying an object and one of its member functions */ /*!
* Set the animation function by supplying an object and one of its member functions.
*/
template<typename T> template<typename T>
void bind(void(T::*f)(), T* o) void bind(void(T::*f)(), T* o)
{ {
@ -62,7 +64,7 @@ public:
void unpause(); void unpause();
void reset(); void reset();
bool playing(bool = true) const; bool playing(bool = true) const;
void update(); void update(float timestamp);
}; };

View File

@ -104,9 +104,9 @@ bool SoundEffect::playing(bool include_delay) const
return false; return false;
} }
void SoundEffect::update() void SoundEffect::update(float timestamp)
{ {
play_animation.update(); play_animation.update(timestamp);
} }
SoundEffect::~SoundEffect() SoundEffect::~SoundEffect()
@ -126,10 +126,10 @@ void Audio::load_bgm()
load_directory(configuration()["audio"]["default-bgm-root"], bgm, this); load_directory(configuration()["audio"]["default-bgm-root"], bgm, this);
} }
void Audio::update() void Audio::update(float timestamp)
{ {
for (auto& [name, sound_effect] : sfx) for (auto& [name, sound_effect] : sfx)
{ {
sound_effect.update(); sound_effect.update(timestamp);
} }
} }

View File

@ -56,7 +56,7 @@ struct SoundEffect : Node
void set_loop_count(int); void set_loop_count(int);
void set_loop_forever(); void set_loop_forever();
bool playing(bool = false) const; bool playing(bool = false) const;
void update(); void update(float timestamp);
~SoundEffect(); ~SoundEffect();
}; };
@ -70,7 +70,7 @@ struct Audio : Node
Audio(Node*); Audio(Node*);
void load_sfx(); void load_sfx();
void load_bgm(); void load_bgm();
void update(); void update(float timestamp);
template <typename T> template <typename T>
void load_directory(const fs::path& root, std::map<std::string, T>& storage, Node* parent = nullptr) void load_directory(const fs::path& root, std::map<std::string, T>& storage, Node* parent = nullptr)

View File

@ -17,8 +17,6 @@
#include "glm/common.hpp" #include "glm/common.hpp"
#include "glm/gtx/integer.hpp" #include "glm/gtx/integer.hpp"
#include "utility.hpp"
namespace sb namespace sb
{ {

View File

@ -34,7 +34,7 @@ void Configuration::set_defaults()
{"suppress-any-key-on-mods", true}, {"suppress-any-key-on-mods", true},
{"system-any-key-ignore-commands", {"fullscreen", "screenshot", "record", "quit"}}, {"system-any-key-ignore-commands", {"fullscreen", "screenshot", "record", "quit"}},
{"any-key-ignore-commands", nlohmann::json::array()}, {"any-key-ignore-commands", nlohmann::json::array()},
{"default-unsuppress-delay", 700}, {"default-unsuppress-delay", 0.7},
{"ignore-repeat-keypress", true} {"ignore-repeat-keypress", true}
}; };
config["display"] = { config["display"] = {
@ -69,10 +69,10 @@ void Configuration::set_defaults()
{"screenshot-extension", ".png"}, {"screenshot-extension", ".png"},
{"screenshot-zfill", 5}, {"screenshot-zfill", 5},
{"screenshot-directory", "."}, {"screenshot-directory", "."},
{"gif-frame-length", 100}, {"gif-frame-length", 0.1},
{"video-directory", "."}, {"video-directory", "."},
{"write-mp4", false}, {"write-mp4", false},
{"max-stash-length", 5000}, {"max-stash-length", 5.0},
{"max-in-game-stashes", 3}, {"max-in-game-stashes", 3},
{"max-video-stashes", 40}, {"max-video-stashes", 40},
{"max-video-memory", 1000}, {"max-video-memory", 1000},
@ -92,7 +92,7 @@ void Configuration::set_defaults()
}; };
config["configuration"] = { config["configuration"] = {
{"auto-refresh", false}, {"auto-refresh", false},
{"auto-refresh-interval", 1000} {"auto-refresh-interval", 1.0}
}; };
} }
@ -156,6 +156,7 @@ void Configuration::merge(const fs::path& path)
sb::Log::log(message, sb::Log::WARN); sb::Log::log(message, sb::Log::WARN);
} }
#ifndef __ANDROID__ #ifndef __ANDROID__
config_file_modification_time = fs::last_write_time(path);
} }
else else
{ {
@ -208,9 +209,9 @@ void Configuration::refresh()
} }
} }
void Configuration::update() void Configuration::update(float timestamp)
{ {
auto_refresher.update(); auto_refresher.update(timestamp);
} }
std::ostream& std::operator<<(std::ostream& out, const Configuration& configuration) std::ostream& std::operator<<(std::ostream& out, const Configuration& configuration)

View File

@ -102,10 +102,10 @@ public:
/*! /*!
* Enable auto refresh. Auto refresh watches the file at the given path for changes and loads them automatically every interval given * Enable auto refresh. Auto refresh watches the file at the given path for changes and loads them automatically every interval given
* in milliseconds. * in seconds.
* *
* @param file_to_refresh path to a configuration JSON * @param file_to_refresh path to a configuration JSON
* @param interval amount of milliseconds between each refresh * @param interval amount of seconds between each refresh
*/ */
void enable_auto_refresh(const fs::path& file_to_refresh, float interval); void enable_auto_refresh(const fs::path& file_to_refresh, float interval);
@ -120,9 +120,11 @@ public:
void refresh(); void refresh();
/*! /*!
* Update the auto refresher. * Update the auto refresher with the given timestamp, which should be the timestamp passed to Game::update.
*
* @param timestamp seconds elapsed since the program started
*/ */
void update(); void update(float timestamp);
}; };

View File

@ -565,26 +565,27 @@ void Game::run()
#endif #endif
} }
void Game::frame(float ticks) void Game::frame(float timestamp)
{ {
if (ticks - last_frame_timestamp + frame_time_overflow >= frame_length) if (timestamp - last_frame_timestamp + frame_time_overflow >= frame_length)
{ {
last_frame_length = ticks - last_frame_timestamp; last_frame_length = timestamp - last_frame_timestamp;
if (frame_length_history.size() == 5000) if (frame_length_history.size() == 5000)
{ {
frame_length_history.pop_back(); frame_length_history.pop_back();
} }
frame_length_history.insert(frame_length_history.begin(), last_frame_length); frame_length_history.insert(frame_length_history.begin(), last_frame_length);
frame_time_overflow = last_frame_length + frame_time_overflow - frame_length; frame_time_overflow = last_frame_length + frame_time_overflow - frame_length;
last_frame_timestamp = ticks; last_frame_timestamp = timestamp;
if (last_frame_length < 1000) if (last_frame_length < 1000)
{ {
recorder.update(); float timestamp_seconds = timestamp / 1000.0f;
recorder.update(timestamp_seconds);
_delegate.dispatch(); _delegate.dispatch();
audio.update(); audio.update(timestamp_seconds);
input.unsuppress_animation.update(); input.unsuppress_animation.update(timestamp_seconds);
update(); update(timestamp_seconds);
_configuration.update(); _configuration.update(timestamp_seconds);
if (!is_gl_context) if (!is_gl_context)
{ {
SDL_SetRenderTarget(renderer, nullptr); SDL_SetRenderTarget(renderer, nullptr);
@ -597,10 +598,10 @@ void Game::frame(float ticks)
// frame_time_overflow = 0; // frame_time_overflow = 0;
// } // }
frame_count_this_second++; frame_count_this_second++;
if (ticks - last_frame_count_timestamp >= 1000) if (timestamp - last_frame_count_timestamp >= 1000)
{ {
current_frames_per_second = frame_count_this_second; current_frames_per_second = frame_count_this_second;
last_frame_count_timestamp = ticks; last_frame_count_timestamp = timestamp;
frame_count_this_second = 0; frame_count_this_second = 0;
std::ostringstream message; std::ostringstream message;
message << "Counted " << current_frames_per_second << " frames last second"; message << "Counted " << current_frames_per_second << " frames last second";

View File

@ -148,7 +148,7 @@ public:
void run(); void run();
void frame(float); void frame(float);
void flag_to_end(); void flag_to_end();
virtual void update() {}; virtual void update(float timestamp) {};
void set_framerate(int); void set_framerate(int);
float get_frame_length() const; float get_frame_length() const;
void handle_quit_event(SDL_Event&); void handle_quit_event(SDL_Event&);
@ -165,9 +165,9 @@ public:
* @return weighted value * @return weighted value
*/ */
template<typename T> template<typename T>
T weight(T amount) T weight(T amount) const
{ {
return (last_frame_length / (1000.0 / 60)) * amount; return (last_frame_length / 1000.0f) * amount;
} }
}; };

View File

@ -125,10 +125,10 @@ void Node::suppress_input()
get_root()->get_input().suppress(); get_root()->get_input().suppress();
} }
void Node::suppress_input_temporarily(int length) void Node::suppress_input_temporarily(float length)
{ {
suppress_input(); suppress_input();
if (length == 0) if (length == 0.0f)
{ {
length = configuration()["input"]["default-unsuppress-delay"]; length = configuration()["input"]["default-unsuppress-delay"];
} }

View File

@ -78,7 +78,7 @@ public:
const Game* get_root() const; const Game* get_root() const;
Box window_box(bool = false); Box window_box(bool = false);
void suppress_input(); void suppress_input();
void suppress_input_temporarily(int = 0); void suppress_input_temporarily(float length = 0.0f);
void unsuppress_input(); void unsuppress_input();
const std::string get_branch_as_string() const; const std::string get_branch_as_string() const;
virtual std::string class_name() const { return "Node"; }; virtual std::string class_name() const { return "Node"; };

View File

@ -17,7 +17,7 @@ Recorder::Recorder(Node* parent) : Node(parent)
* been configured by the user. */ * been configured by the user. */
float Recorder::frame_length() float Recorder::frame_length()
{ {
return configuration()["recording"].value("video-frame-length", get_root()->get_frame_length()); return configuration()["recording"].value("video-frame-length", get_root()->get_frame_length() / 1000.0f);
} }
/* Handle commands for screenshot, record video and save video */ /* Handle commands for screenshot, record video and save video */
@ -259,7 +259,7 @@ void Recorder::write_stash_frames(Stash* stash)
if (ii == stash->frame_offset) if (ii == stash->frame_offset)
{ {
GifBegin(&gif_writer, gif_path.string().c_str(), frame->w, GifBegin(&gif_writer, gif_path.string().c_str(), frame->w,
frame->h, gif_frame_length / 10); frame->h, gif_frame_length * 100);
} }
else else
{ {
@ -269,7 +269,7 @@ void Recorder::write_stash_frames(Stash* stash)
SDL_Surface* converted = SDL_ConvertSurfaceFormat( SDL_Surface* converted = SDL_ConvertSurfaceFormat(
frame, SDL_PIXELFORMAT_ABGR8888, 0); frame, SDL_PIXELFORMAT_ABGR8888, 0);
GifWriteFrame(&gif_writer, (const uint8_t*) converted->pixels, GifWriteFrame(&gif_writer, (const uint8_t*) converted->pixels,
frame->w, frame->h, gif_frame_length / 10); frame->w, frame->h, gif_frame_length * 100);
} }
elapsed += frame_length(); elapsed += frame_length();
delete[] stash->pixel_buffers.front(); delete[] stash->pixel_buffers.front();
@ -343,7 +343,7 @@ void Recorder::write_mp4()
std::string pixel_format = configuration()["recording"]["mp4-pixel-format"].get<std::string>(); std::string pixel_format = configuration()["recording"]["mp4-pixel-format"].get<std::string>();
fs::path images_match = current_video_directory / "%05d.png"; fs::path images_match = current_video_directory / "%05d.png";
mp4_command << "ffmpeg -f s16le -ac 2 -ar 22050 -i " << current_audio_path.string() << mp4_command << "ffmpeg -f s16le -ac 2 -ar 22050 -i " << current_audio_path.string() <<
" -f image2 -framerate " << (1000 / frame_length()) << " -f image2 -framerate " << (1.0f / frame_length()) <<
" -i " << images_match.string() << " -s " << size.x << "x" << size.y << " -i " << images_match.string() << " -s " << size.x << "x" << size.y <<
" -c:v libx264 -crf 17 -pix_fmt " << pixel_format << " " << " -c:v libx264 -crf 17 -pix_fmt " << pixel_format << " " <<
current_video_directory.string() << ".mp4"; current_video_directory.string() << ".mp4";
@ -357,14 +357,14 @@ void Recorder::write_audio(Uint8* stream, int len)
audio_file.write(reinterpret_cast<char*>(stream), len); audio_file.write(reinterpret_cast<char*>(stream), len);
} }
void Recorder::update() void Recorder::update(float timestamp)
{ {
if (is_recording and get_memory_size() > configuration()["recording"]["max-video-memory"]) if (is_recording and get_memory_size() > configuration()["recording"]["max-video-memory"])
{ {
end_recording(); end_recording();
} }
animation.set_frame_length(frame_length()); animation.set_frame_length(frame_length());
animation.update(); animation.update(timestamp);
} }
void Recorder::process_audio(void* context, Uint8* stream, int len) void Recorder::process_audio(void* context, Uint8* stream, int len)

View File

@ -75,7 +75,7 @@ public:
void finish_writing_video(); void finish_writing_video();
void write_mp4(); void write_mp4();
void write_audio(Uint8*, int); void write_audio(Uint8*, int);
void update(); void update(float timestamp);
virtual std::string class_name() const { return "Recorder"; } virtual std::string class_name() const { return "Recorder"; }
}; };

View File

@ -51,13 +51,6 @@ public:
std::vector<Segment> subsegments(int) const; std::vector<Segment> subsegments(int) const;
inline bool operator<(const Segment& segment) const { return operator<(segment.length()); } inline bool operator<(const Segment& segment) const { return operator<(segment.length()); }
/*
std::cout << (a < b) << (a > b) << (b < c) << (b <= c) << (c > b) << (c >= b) << (d < 1) << (d < 2) <<
(d >= a) << (d > c) << std::endl;
should print 0101010101
*/
template<typename N> template<typename N>
inline bool operator<(const N& other) const { return length() < other; } inline bool operator<(const N& other) const { return length() < other; }
@ -76,3 +69,11 @@ namespace std
{ {
std::ostream& operator<<(std::ostream&, const Segment&); std::ostream& operator<<(std::ostream&, const Segment&);
} }
/* Add Segment class to the sb namespace. This should be the default location, but Segment is left in the global namespace
* for backward compatibility.
*/
namespace sb
{
using ::Segment;
}

View File

@ -17,8 +17,6 @@
#include "glm/common.hpp" #include "glm/common.hpp"
#include "glm/gtx/integer.hpp" #include "glm/gtx/integer.hpp"
#include "utility.hpp"
namespace sb namespace sb
{ {

View File

@ -830,7 +830,7 @@ void Sprite::set_draw_children_on_frame(bool on_frame)
draw_children_on_frame = on_frame; draw_children_on_frame = on_frame;
} }
void Sprite::update(const std::vector<Box>& subsections) void Sprite::update(float timestamp, const std::vector<Box>& subsections)
{ {
if (is_active()) if (is_active())
{ {
@ -838,10 +838,10 @@ void Sprite::update(const std::vector<Box>& subsections)
{ {
move_weighted(step); move_weighted(step);
} }
frame_animation.update(); frame_animation.update(timestamp);
blink_animation.update(); blink_animation.update(timestamp);
wipe_animation.update(); wipe_animation.update(timestamp);
toggle_hidden_animation.update(); toggle_hidden_animation.update(timestamp);
SDL_Texture* texture = get_current_frame(); SDL_Texture* texture = get_current_frame();
if (is_loaded() && !is_hidden() && get_current_frameset().get_frame_count()) if (is_loaded() && !is_hidden() && get_current_frameset().get_frame_count())
{ {
@ -892,7 +892,7 @@ void Sprite::update(const std::vector<Box>& subsections)
child_relative_position = child.sprite.get_nw(); child_relative_position = child.sprite.get_nw();
child.sprite.move(get_nw()); child.sprite.move(get_nw());
} }
child.sprite.update(); child.sprite.update(timestamp);
if (!draw_children_on_frame) if (!draw_children_on_frame)
{ {
child.sprite.set_nw(child_relative_position); child.sprite.set_nw(child_relative_position);
@ -901,9 +901,9 @@ void Sprite::update(const std::vector<Box>& subsections)
} }
} }
void Sprite::update() void Sprite::update(float timestamp)
{ {
update({}); update(timestamp, {});
} }
void Sprite::render_subsection(SDL_Renderer* renderer, SDL_Texture* texture, const Box& subsection, const Box& box) void Sprite::render_subsection(SDL_Renderer* renderer, SDL_Texture* texture, const Box& subsection, const Box& box)

View File

@ -140,8 +140,8 @@ public:
bool has_child(std::string) const; bool has_child(std::string) const;
void remove_child(std::string); void remove_child(std::string);
void set_draw_children_on_frame(bool); void set_draw_children_on_frame(bool);
virtual void update(const std::vector<Box>&); virtual void update(float timestamp, const std::vector<Box>&);
virtual void update(); virtual void update(float timestamp);
void render_subsection(SDL_Renderer*, SDL_Texture*, const Box&, const Box&); void render_subsection(SDL_Renderer*, SDL_Texture*, const Box&, const Box&);
void set_to_leave_memory_allocated(); void set_to_leave_memory_allocated();
void set_to_deallocate_memory(); void set_to_deallocate_memory();

View File

@ -84,10 +84,8 @@ namespace sb
private: private:
using Reaction = std::function<return_type(bool, arguments...)>; using Reaction = std::function<return_type(bool, arguments...)>;
inline static const bool STATE_OFF = false;
inline static const bool STATE_ON = true;
bool state = STATE_OFF; bool state = false;
Reaction reaction; Reaction reaction;
public: public:
@ -102,6 +100,12 @@ namespace sb
Switch(bool state, Reaction reaction = Reaction()) : state(state), reaction(reaction) {} Switch(bool state, Reaction reaction = Reaction()) : state(state), reaction(reaction) {}
/*!
* Toggle state, triggering the reaction function.
*
* @param args arbitrary list of arguments that are expected by the reaction function
* @return return the return value of the reaction function
*/
return_type flip(arguments... args) return_type flip(arguments... args)
{ {
state = !state; state = !state;
@ -121,13 +125,14 @@ namespace sb
/*! /*!
* @return when called as a boolean, return state * @return when called as a boolean, return state
*/ */
operator bool() operator bool() const
{ {
return on(); return state;
} }
/*! /*!
* Assign the switch object's state using the assignment operator with a boolean value. * Assign the switch object's state using the assignment operator with a boolean value, without triggering the reaction function.
* To trigger the reaction function, use `Switch::flip` instead.
* *
* @param state state the switch will be set to * @param state state the switch will be set to
*/ */
@ -137,14 +142,6 @@ namespace sb
return *this; return *this;
} }
/*!
* @return true if state is Switch::STATE_ON, false otherwise
*/
bool on()
{
return state;
}
operator std::string() const operator std::string() const
{ {
return state ? "true" : "false"; return state ? "true" : "false";

View File

@ -1,77 +1,55 @@
#include "Timer.hpp" #include "Timer.hpp"
/* Initialize a Timer object for keeping a general time count in milliseconds or seconds which can be paused sb::Timer::operator bool() const
* arbitrarily. The time initializes to zero. By default, the timer begins timing at initialization (this
* may change in the future). The ticks and previous_ticks parameters are initially synchronized with the SDL
* ticks function, which returns the number of milliseconds since the program began, and the SDL ticks function
* is used to keep the time updated. */
sb::Timer::Timer()
{
ticks = SDL_GetTicks();
ticks_previous = ticks;
}
/* Check whether Timer is keeping time or not */
bool sb::Timer::state()
{ {
return timing; return timing;
} }
/* Toggle timing on/off */
void sb::Timer::toggle() void sb::Timer::toggle()
{ {
toggle(!state()); toggle(!*this);
} }
/* Set state explicitly to sb::Timer::ON (true) or sb::Timer::OFF (false) */
void sb::Timer::toggle(bool state) void sb::Timer::toggle(bool state)
{ {
timing = state; timing = state;
} }
/* Reset time elapsed to zero */ void sb::Timer::on()
{
toggle(ON);
}
void sb::Timer::off()
{
toggle(OFF);
}
void sb::Timer::reset() void sb::Timer::reset()
{ {
ticks_elapsed = 0; _elapsed = 0;
ticks = SDL_GetTicks();
ticks_previous = ticks;
frame_duration = 0;
} }
/* Return milliseconds elapsed on timer */ float sb::Timer::elapsed() const
float sb::Timer::ms_elapsed()
{ {
return ticks_elapsed; return _elapsed;
} }
/* Return seconds elapsed on timer */ float sb::Timer::frame() const
float sb::Timer::seconds_elapsed()
{
return ms_elapsed() / 1000.0f;
}
/* Return the length of the previous frame in milliseconds (the time between the last two calls to
* the timer update function) */
float sb::Timer::ms_last_frame()
{ {
return frame_duration; return frame_duration;
} }
/* Return the length of the previous frame in seconds (the time between the last two calls to the void sb::Timer::update(float timestamp)
* timer update function) */
float sb::Timer::seconds_last_frame()
{ {
return ms_last_frame() / 1000.0f; if (previous_is_recorded)
}
/* Add time to the timer by measuring the time between this call and the previous call to update */
void sb::Timer::update()
{
ticks = SDL_GetTicks();
frame_duration = ticks - ticks_previous;
if (state())
{ {
ticks_elapsed += frame_duration; frame_duration = timestamp - timestamp_previous;
if (*this)
{
_elapsed += frame();
}
} }
ticks_previous = ticks; timestamp_previous = timestamp;
previous_is_recorded = true;
} }

View File

@ -10,17 +10,20 @@
#pragma once #pragma once
#include "SDL.h"
namespace sb namespace sb
{ {
/*!
* Timer in seconds which can be paused arbitrarily.
*
* It must be updated every frame with the timestamp passed to Game::update, regardless of whether it is actively timing or not.
*/
class Timer class Timer
{ {
private: private:
bool timing = Timer::ON; bool timing = false, previous_is_recorded = false;
int ticks = 0, ticks_previous = 0, frame_duration = 0, ticks_elapsed = 0; float timestamp = 0.0f, timestamp_previous = 0.0f, frame_duration = 0.0f, _elapsed = 0.0f;
public: public:
@ -30,16 +33,58 @@ namespace sb
ON ON
}; };
Timer(); /*!
bool state(); * @return boolean indicating whether timer object is keeping time or not
*/
operator bool() const;
/*!
* Toggle timing on/off
*/
void toggle(); void toggle();
void toggle(bool);
/*!
* @param state set timing state explictly by passing a boolean
*/
void toggle(bool state);
/*!
* Start timing.
*/
void on();
/*!
* Stop timing.
*/
void off();
/*!
* Reset time elapsed to zero.
*/
void reset(); void reset();
float ms_elapsed();
float seconds_elapsed(); /*!
float ms_last_frame(); * @return seconds elapsed on timer
float seconds_last_frame(); */
void update(); float elapsed() const;
/*!
* @return length of the previous frame in seconds
*/
float frame() const;
/*!
* Update the timer's elapsed time, using the amount of seconds elapsed in the program at the start of the frame to keep track.
*
* The timer should be updated every frame to keep track of the timestamp, regardless of whether it is currently timing or not.
*
* The timestamp should be forwarded from the value passed to Game::update, so that it is consistently the timestamp at the
* beginning of the frame.
*
* @param timestamp seconds elapsed since the start of the program
*/
void update(float timestamp);
/*! /*!
* Applies delta timing to a value: returns the value as weighted by the amount of time passed since the * Applies delta timing to a value: returns the value as weighted by the amount of time passed since the
@ -50,9 +95,9 @@ namespace sb
* @return weighted value * @return weighted value
*/ */
template<typename Type> template<typename Type>
Type weigh(Type amount) Type delta(Type amount) const
{ {
return seconds_last_frame() * amount; return frame() * amount;
} }
}; };