Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions include/scratchcpp/iengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
20 changes: 18 additions & 2 deletions src/engine/internal/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ void Engine::initClone(Sprite *clone)

void Engine::run()
{
auto frameDuration = std::chrono::milliseconds(33);
updateFrameDuration();
start();

while (true) {
Expand All @@ -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<std::chrono::milliseconds>(currentTime - lastFrameTime);
auto sleepTime = frameDuration - elapsedTime;
auto sleepTime = m_frameDuration - elapsedTime;
bool timeOut = sleepTime <= std::chrono::milliseconds::zero();

if (!timeOut && !m_skipFrame)
Expand All @@ -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];
Expand Down Expand Up @@ -757,3 +768,8 @@ BlockSectionContainer *Engine::blockSectionContainer(IBlockSection *section) con

return nullptr;
}

void Engine::updateFrameDuration()
{
m_frameDuration = std::chrono::milliseconds(static_cast<long>(1000 / m_fps));
}
7 changes: 7 additions & 0 deletions src/engine/internal/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -91,6 +94,8 @@ class Engine : public IEngine
std::shared_ptr<Entity> getEntity(const std::string &id);
std::shared_ptr<IBlockSection> blockSection(const std::string &opcode) const;

void updateFrameDuration();

std::unordered_map<std::shared_ptr<IBlockSection>, std::unique_ptr<BlockSectionContainer>> m_sections;
std::vector<std::shared_ptr<Target>> m_targets;
std::vector<std::shared_ptr<Broadcast>> m_broadcasts;
Expand All @@ -107,6 +112,8 @@ class Engine : public IEngine

std::unique_ptr<ITimer> 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;
Expand Down
9 changes: 9 additions & 0 deletions test/engine/engine_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions test/mocks/enginemock.h
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down