From ab7184ef861f12eb877c698fba227a8cb5dcb38c Mon Sep 17 00:00:00 2001 From: Muzychenko Andrey <33288308+k4zmu2a@users.noreply.github.com> Date: Sat, 13 Nov 2021 10:55:37 +0300 Subject: [PATCH] Added float target frame time, set UPS to 120. Improved frame stabilization. Ref PR #85. --- SpaceCadetPinball/pb.cpp | 49 +++++++++++++++++++---------------- SpaceCadetPinball/pb.h | 7 ++--- SpaceCadetPinball/winmain.cpp | 18 ++++++------- 3 files changed, 40 insertions(+), 34 deletions(-) diff --git a/SpaceCadetPinball/pb.cpp b/SpaceCadetPinball/pb.cpp index 1c74b16..19fde64 100644 --- a/SpaceCadetPinball/pb.cpp +++ b/SpaceCadetPinball/pb.cpp @@ -26,8 +26,8 @@ TPinballTable* pb::MainTable = nullptr; datFileStruct* pb::record_table = nullptr; -int pb::time_ticks = 0, pb::demo_mode = 0, pb::game_mode = 2, pb::mode_countdown_, pb::state, pb::frameCounter = 0; -float pb::time_now, pb::time_next, pb::ball_speed_limit; +int pb::time_ticks = 0, pb::demo_mode = 0, pb::game_mode = 2, pb::state, pb::frameCounter = 0; +float pb::time_now, pb::time_next, pb::ball_speed_limit, pb::mode_countdown_, pb::time_ticks_remainder = 0; high_score_struct pb::highscore_table[5]; bool pb::FullTiltMode = false, pb::cheat_mode = false; @@ -225,29 +225,34 @@ void pb::ballset(int x, int y) ball->Speed = maths::normalize_2d(&ball->Acceleration); } -void pb::frame(int time) +void pb::frame(float dtMilliSec) { - static int frameTime = 0; + static float frameTime = 0; - if (time > 100) - time = 100; - if (time <= 0) + if (dtMilliSec > 100) + dtMilliSec = 100; + if (dtMilliSec <= 0) return; - float timeMul = time * 0.001f; - if (!mode_countdown(time)) + auto dtSec = dtMilliSec * 0.001f; + if (!mode_countdown(dtMilliSec)) { - time_next = time_now + timeMul; - timed_frame(time_now, timeMul, true); + time_next = time_now + dtSec; + timed_frame(time_now, dtSec, true); time_now = time_next; - time_ticks += time; + + auto dtMilliSecComp = dtMilliSec + time_ticks_remainder; + auto dtWhole = static_cast(dtMilliSecComp); + time_ticks_remainder = dtMilliSecComp - static_cast(dtWhole); + time_ticks += dtWhole; + if (nudge::nudged_left || nudge::nudged_right || nudge::nudged_up) { - nudge::nudge_count = timeMul * 4.0f + nudge::nudge_count; + nudge::nudge_count = dtSec * 4.0f + nudge::nudge_count; } else { - auto nudgeDec = nudge::nudge_count - timeMul; + auto nudgeDec = nudge::nudge_count - dtSec; if (nudgeDec <= 0.0f) nudgeDec = 0.0; nudge::nudge_count = nudgeDec; @@ -265,9 +270,9 @@ void pb::frame(int time) // Retained render prevents frame skip. The next best thing - complete refresh at fixed rate. render::update(false); - // Frame time at 60 FPS = 16.(6) ms = (16 + 17 + 17) / 3 - auto targetTime = frameCounter % 3 == 0 ? 16 : 17; - frameTime += time; + // Frame time at 60 FPS = 16.(6) ms + auto targetTime = 1000 / 60.0f; + frameTime += dtMilliSec; if (frameTime >= targetTime) { frameTime = min(frameTime - targetTime, 100); @@ -526,7 +531,7 @@ void pb::keydown(int key) } } -int pb::mode_countdown(int time) +int pb::mode_countdown(float dtMilliSec) { if (!game_mode || game_mode <= 0) return 1; @@ -534,14 +539,14 @@ int pb::mode_countdown(int time) { if (game_mode == 3) { - mode_countdown_ -= time; - if (mode_countdown_ < 0 || time < 0) + mode_countdown_ -= dtMilliSec; + if (mode_countdown_ < 0.0f || dtMilliSec < 0.0f) mode_change(4); } else if (game_mode == 4) { - mode_countdown_ -= time; - if (mode_countdown_ < 0 || time < 0) + mode_countdown_ -= dtMilliSec; + if (mode_countdown_ < 0.0f || dtMilliSec < 0.0f) mode_change(1); } return 1; diff --git a/SpaceCadetPinball/pb.h b/SpaceCadetPinball/pb.h index e8625bc..bb66e6f 100644 --- a/SpaceCadetPinball/pb.h +++ b/SpaceCadetPinball/pb.h @@ -26,14 +26,14 @@ public: static void toggle_demo(); static void replay_level(int demoMode); static void ballset(int x, int y); - static void frame(int time); + static void frame(float dtMilliSec); static void timed_frame(float timeNow, float timeDelta, bool drawBalls); static void window_size(int* width, int* height); static void pause_continue(); static void loose_focus(); static void keyup(int key); static void keydown(int key); - static int mode_countdown(int time); + static int mode_countdown(float dtMilliSec); static void launch_ball(); static void end_game(); static void high_scores(); @@ -41,6 +41,7 @@ public: static bool chk_highscore(); static float collide(float timeNow, float timeDelta, TBall* ball); private: - static int demo_mode, mode_countdown_; + static int demo_mode; static int state; + static float mode_countdown_, time_ticks_remainder; }; diff --git a/SpaceCadetPinball/winmain.cpp b/SpaceCadetPinball/winmain.cpp index 1b0c209..688f5f5 100644 --- a/SpaceCadetPinball/winmain.cpp +++ b/SpaceCadetPinball/winmain.cpp @@ -12,7 +12,7 @@ #include "splash.h" #include "render.h" -const int TargetFrameTime = 8; +const float TargetUPS = 120, TargetFrameTime = 1000 / TargetUPS; HINSTANCE winmain::hinst = nullptr; HWND winmain::hwnd_frame = nullptr; @@ -225,7 +225,7 @@ int winmain::WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi pb::replay_level(0); DWORD someTimeCounter = 300u, prevTime = 0u, frameStart = timeGetTime(); - int sleepRemainder = 0, frameDuration = TargetFrameTime; + float sleepRemainder = 0, frameDuration = TargetFrameTime; while (true) { if (!someTimeCounter) @@ -299,11 +299,11 @@ int winmain::WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi gdrv::fill_bitmap(&gfr_display, 1, height, width - 1, 0, 0); // Background auto targetVal = dt < target ? dt : target; - auto targetHeight = min(static_cast(std::round(targetVal * scale)), height); + auto targetHeight = min(static_cast(std::floor(targetVal * scale)), height); gdrv::fill_bitmap(&gfr_display, 1, targetHeight, width - 1, height - targetHeight, -1); // Target auto diffVal = dt < target ? target - dt : dt - target; - auto diffHeight = min(static_cast(std::round(diffVal * scale)), height); + auto diffHeight = min(static_cast(std::floor(diffVal * scale)), height); gdrv::fill_bitmap(&gfr_display, 1, diffHeight, width - 1, height - targetHeight - diffHeight, 1); // Target diff gdrv::blit(&gfr_display, 0, 0, render::vscreen.Width - width, 0, width, height); @@ -311,21 +311,21 @@ int winmain::WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi auto updateEnd = timeGetTime(); auto sleepDuration = TargetFrameTime - (int)(updateEnd - frameStart) - sleepRemainder; + auto intSleepDuration = static_cast(sleepDuration); DWORD frameEnd; - if (sleepDuration > 0) + if (intSleepDuration > 0) { - Sleep(sleepDuration); + Sleep(intSleepDuration); frameEnd = timeGetTime(); - sleepRemainder = (frameEnd - updateEnd) - sleepDuration; } else { frameEnd = updateEnd; - sleepRemainder = 0; } - frameDuration = min(frameEnd - frameStart, TargetFrameTime * 2); + sleepRemainder = max(min((int)(frameEnd - updateEnd) - sleepDuration, TargetFrameTime), -TargetFrameTime); + frameDuration = min((frameEnd - frameStart), TargetFrameTime * 2); frameStart = frameEnd; --someTimeCounter;