From b573dd9ae3dcfe9a6c383c221e460704018aeecc Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 18 Sep 2023 09:45:47 +0200 Subject: [PATCH 1/2] Add a way to change FPS --- include/scratchcpp/iengine.h | 6 ++++++ src/engine/internal/engine.cpp | 20 ++++++++++++++++++-- src/engine/internal/engine.h | 7 +++++++ test/engine/engine_test.cpp | 9 +++++++++ test/mocks/enginemock.h | 3 +++ 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/include/scratchcpp/iengine.h b/include/scratchcpp/iengine.h index b622f665..ac4b7ce8 100644 --- a/include/scratchcpp/iengine.h +++ b/include/scratchcpp/iengine.h @@ -85,6 +85,12 @@ class LIBSCRATCHCPP_EXPORT IEngine */ virtual void run() = 0; + /*! Returns the framerate of the project. */ + virtual double fps() const = 0; + + /*! Sets the framerate of the project. */ + virtual void setFps(double fps) = 0; + /*! Returns true if there are any running script of the broadcast with the given index. */ virtual bool broadcastRunning(unsigned int index, VirtualMachine *sourceScript) = 0; diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp index 135d928d..de5f9809 100644 --- a/src/engine/internal/engine.cpp +++ b/src/engine/internal/engine.cpp @@ -317,7 +317,7 @@ void Engine::initClone(Sprite *clone) void Engine::run() { - auto frameDuration = std::chrono::milliseconds(33); + updateFrameDuration(); start(); while (true) { @@ -332,7 +332,7 @@ void Engine::run() // Sleep until the time for the next frame auto currentTime = std::chrono::steady_clock::now(); auto elapsedTime = std::chrono::duration_cast(currentTime - lastFrameTime); - auto sleepTime = frameDuration - elapsedTime; + auto sleepTime = m_frameDuration - elapsedTime; bool timeOut = sleepTime <= std::chrono::milliseconds::zero(); if (!timeOut && !m_skipFrame) @@ -346,6 +346,17 @@ void Engine::run() } } +double Engine::fps() const +{ + return m_fps; +} + +void Engine::setFps(double fps) +{ + m_fps = fps; + updateFrameDuration(); +} + bool Engine::broadcastRunning(unsigned int index, VirtualMachine *sourceScript) { const auto &scripts = m_runningBroadcastMap[index]; @@ -757,3 +768,8 @@ BlockSectionContainer *Engine::blockSectionContainer(IBlockSection *section) con return nullptr; } + +void Engine::updateFrameDuration() +{ + m_frameDuration = std::chrono::milliseconds(static_cast(1000 / m_fps)); +} diff --git a/src/engine/internal/engine.h b/src/engine/internal/engine.h index 04f519ba..94795894 100644 --- a/src/engine/internal/engine.h +++ b/src/engine/internal/engine.h @@ -36,6 +36,9 @@ class Engine : public IEngine void initClone(libscratchcpp::Sprite *clone) override; void run() override; + double fps() const override; + void setFps(double fps) override; + bool broadcastRunning(unsigned int index, VirtualMachine *sourceScript) override; void breakFrame() override; @@ -91,6 +94,8 @@ class Engine : public IEngine std::shared_ptr getEntity(const std::string &id); std::shared_ptr blockSection(const std::string &opcode) const; + void updateFrameDuration(); + std::unordered_map, std::unique_ptr> m_sections; std::vector> m_targets; std::vector> m_broadcasts; @@ -107,6 +112,8 @@ class Engine : public IEngine std::unique_ptr m_defaultTimer; ITimer *m_timer = nullptr; + double m_fps = 30; // default FPS + std::chrono::milliseconds m_frameDuration; // will be computed in run() bool m_breakFrame = false; bool m_skipFrame = false; diff --git a/test/engine/engine_test.cpp b/test/engine/engine_test.cpp index 12620f4a..e862ec7a 100644 --- a/test/engine/engine_test.cpp +++ b/test/engine/engine_test.cpp @@ -34,6 +34,15 @@ TEST(EngineTest, Clear) ASSERT_TRUE(engine.registeredSections().empty()); } +TEST(EngineTest, Fps) +{ + Engine engine; + ASSERT_EQ(engine.fps(), 30); + + engine.setFps(60.25); + ASSERT_EQ(engine.fps(), 60.25); +} + TEST(EngineTest, BreakFrame) { Engine engine; diff --git a/test/mocks/enginemock.h b/test/mocks/enginemock.h index 0beda8ec..2d874d79 100644 --- a/test/mocks/enginemock.h +++ b/test/mocks/enginemock.h @@ -24,6 +24,9 @@ class EngineMock : public IEngine MOCK_METHOD(void, initClone, (Sprite *), (override)); MOCK_METHOD(void, run, (), (override)); + MOCK_METHOD(double, fps, (), (const, override)); + MOCK_METHOD(void, setFps, (double fps), (override)); + MOCK_METHOD(bool, broadcastRunning, (unsigned int, VirtualMachine *), (override)); MOCK_METHOD(void, breakFrame, (), (override)); From 3fc9ff84f3fdaf84190c731af2e708743a4af697 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 18 Sep 2023 09:48:02 +0200 Subject: [PATCH 2/2] README: Mark custom FPS as finished --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 368812fc..420d40ae 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ int main(int argc, char **argv) { - [ ] Project metadata - [ ] Turbo mode - [ ] Multithreading (experimental) -- [ ] Custom FPS in the default event loop (`libscratchcpp::Engine::run()`) +- [x] Custom FPS in the default event loop (`libscratchcpp::Engine::run()`) - [ ] Scratch 2.0 to 3.0 converter (help needed) - [ ] Scratch 1.4 and below to 3.0 converter (help needed) - [ ] API for comments