From a016a156e76f7394c5632da325ab0b453cfb3b37 Mon Sep 17 00:00:00 2001 From: Paweł Redman Date: Mon, 26 Mar 2018 14:19:50 +0200 Subject: Independent game timing. --- src/common.hpp | 24 +++++++++++++++---- src/game/game.cpp | 65 ++++++++++++++++++++++++++++++++++---------------- src/game/interface.cpp | 16 +++++++++---- src/main.cpp | 29 ++++++++++++---------- src/render.cpp | 4 ++-- 5 files changed, 94 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/common.hpp b/src/common.hpp index 846b166..df9c8d1 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -35,6 +35,11 @@ extern bool debug_draw_paths; extern bool debug_draw_tile_coords; extern bool debug_AI; +// time in nanoseconds +// nclock() never returns 0, so it can be used as a null value +typedef uint64_t ntime_t; +ntime_t nclock(void); + namespace procgen { class prng_t { uint32_t state = 0; @@ -240,15 +245,23 @@ namespace game { public: world::world_t world; interface::state_t *interface; - double now = 0.0, dt; - bool paused = false; procgen::prng_t dice_prng; std::unordered_set awake_entities; std::unordered_set selected_units; + ntime_t time; // game time + double now, dt; // FIXME: refactor the code, use ntime_t everywhere + + // frame timing data (game runs at a different frequency than the renderer) + bool paused; + ntime_t t0; + size_t frames = 0, frames_since_t0, frames_behind = 0; + void start(void); void stop(void); - void tick(double now_, double dt_); + void tick(ntime_t time); + void pause(void); + void resume(void); // These are called by the interface. void select(rectf_t rect); @@ -355,14 +368,15 @@ namespace render { void drender_text(rectf_t rect, std::string str); void drender_entity(world::entity_t *ent); public: - double now, dt; + ntime_t time; + double dt; struct { size_t sectors, tiles, entities; } stats; state_t(sf::RenderWindow *window_); - void begin_frame(double time_, double dt_); + void begin_frame(ntime_t time_, double dt); void end_frame(void); void render(game::state_t *game); diff --git a/src/game/game.cpp b/src/game/game.cpp index f76b478..44ea95d 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -92,6 +92,8 @@ void state_t::start(void) soldier = new unit_soldier_t(this); soldier->place(&world, v2f_t(2.5, 0.5)); select(rectf_t(v2f_t(0.5, 0.5), v2f_t(2.5, 0.5))); + + resume(); } void state_t::stop(void) @@ -179,34 +181,57 @@ void state_t::spawn_soldier(v2f_t x) soldier->destroy(); } -void state_t::tick(double now_, double dt_) +void state_t::pause(void) +{ + paused = true; +} + +void state_t::resume(void) +{ + t0 = nclock(); + frames_since_t0 = 0; + + paused = false; +} + + +#define TIME_DELTA ((ntime_t)1000000000 / 60) + +void state_t::tick(ntime_t time_) { - union { - double d; - uint32_t i; - } u; + size_t target, frames_this_tick = 0; if (paused) return; - if (dt > 0.05) - dt = 0.05; - else - dt = dt_; + target = (time_ - t0) / TIME_DELTA; + + while (frames_since_t0 < target) { + // FIXME: Is this non-deterministic enough? + dice_prng.seed(dice_prng.next() ^ time); - now += dt; + // setting up old variables (refactor them out eventually) + now = time * 1.0e-9; + dt = TIME_DELTA * 1.0e-9; - // FIXME: Is this non-deterministic enough? - u.d = now; - dice_prng.seed(dice_prng.next() ^ u.i); - u.d = dt; - dice_prng.seed(dice_prng.next() ^ u.i); + // on_think can insert/erase elements of awake_entities so iterate + // over a copy of it. + auto copy = awake_entities; + for (entity_t *ent : copy) + ent->on_think(); - // on_think can insert/erase elements of awake_entities so iterate - // over a copy of it. - auto copy = awake_entities; - for (entity_t *ent : copy) - ent->on_think(); + frames++; + frames_since_t0++; + frames_this_tick++; + time += TIME_DELTA; + + if (frames_this_tick == 3) { + t0 = time_; + frames_since_t0 = 0; + frames_behind++; + break; + } + } } die_t::die_t(size_t sides_) diff --git a/src/game/interface.cpp b/src/game/interface.cpp index 7697a22..262c3ae 100644 --- a/src/game/interface.cpp +++ b/src/game/interface.cpp @@ -160,11 +160,13 @@ void state_t::tick(double dt) case sf::Event::KeyPressed: switch (event.key.code) { case sf::Keyboard::Key::Space: - game->paused ^= 1; - if (game->paused) + if (!game->paused) { + game->pause(); print(text::get(text::PAUSED)); - else + } else { + game->resume(); print(text::get(text::UNPAUSED)); + } break; case sf::Keyboard::Key::F: @@ -239,7 +241,7 @@ void state_t::render_to(render::state_t *render) x[1] += em; } - x = v2f_t(0.0f, h - em * 4.5); + x = v2f_t(0.0f, h - em * 5.5); ss << "World S/T:"; ss << game->world.stats.sectors << "/"; ss << game->world.stats.tiles; @@ -271,6 +273,12 @@ void state_t::render_to(render::state_t *render) ss << std::fixed << std::setprecision(1); ss << "FPS: " << fps; render->render_text(x, em, ss.str(), render::ALIGN_LEFT_TOP, sf::Color::White); + + x[1] += em; + ss.str(std::string()); + ss << std::fixed << std::setprecision(3); + ss << "Game t/F/B: " << game->time * 1.0e-9 << "/" << game->frames << "/" << game->frames_behind; + render->render_text(x, em, ss.str(), render::ALIGN_LEFT_TOP, sf::Color::White); } } // namespace interface diff --git a/src/main.cpp b/src/main.cpp index 71e47be..3f60f25 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,20 +24,23 @@ bool debug_draw_tile_coords = false; bool debug_AI = false; render::state_t *debug_render; -uint64_t nano_clock(void) +static ntime_t nclock_ref; + +// always > 0 +ntime_t nclock(void) { - return std::chrono::high_resolution_clock::now().time_since_epoch() / std::chrono::nanoseconds(1); + return std::chrono::high_resolution_clock::now().time_since_epoch() / + std::chrono::nanoseconds(1) - nclock_ref; } int main() { - uint64_t t0 = nano_clock(); + nclock_ref = nclock() - 1; + sf::RenderWindow window(sf::VideoMode(800, 600), "Minitrem"); render::state_t render(&window); game::state_t game; interface::state_t interface(&window, &game); - size_t frame = 0; - double before = NAN; game.interface = &interface; debug_render = &render; @@ -47,14 +50,17 @@ int main() game.start(); while (1) { - double now = (nano_clock() - t0) * 1.0e-9, dt; + static ntime_t before = 0; + ntime_t now = nclock(); + double dt; - if (frame) - dt = now - before; + if (before) + dt = (now - before) * 1.0e-9; else - dt = 0.01; + dt = 0.1; + before = now; - game.tick(now, dt); + game.tick(now); interface.tick(dt); if (!window.isOpen()) break; @@ -64,9 +70,6 @@ int main() render.render(&game); interface.render_to(&render); render.end_frame(); - - before = now; - frame++; } game.stop(); diff --git a/src/render.cpp b/src/render.cpp index 8ad0e25..7a798ab 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -55,9 +55,9 @@ void register_tile(uint8_t type, const char *top, const char *side, float height } -void state_t::begin_frame(double now_, double dt_) +void state_t::begin_frame(ntime_t time_, double dt_) { - now = now_; + time = time_; dt = dt_; window->clear(); -- cgit