From cf1119c742114bf51d07f83aa2a78963e95963a6 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Thu, 5 Aug 2021 21:59:48 +0300 Subject: [PATCH] Encapsulate music and timer interpolation, test with pause --- include/game/inputtype.h | 2 +- include/game/precisionevaluator.h | 2 +- include/game/timeline.h | 5 +- include/{game => tools}/mathutils.h | 0 include/tools/music.h | 21 ++++++++ src/application.cpp | 4 +- src/classicgame/classicanimationscenario.h | 2 +- src/classicgame/classicgame.cpp | 30 +++++++++-- src/classicgame/classicgame.h | 7 ++- src/classicgame/classicmapcreator.h | 2 +- src/classicgame/classicsprite.h | 2 +- src/classicgame/classictimeline.cpp | 40 +++------------ src/classicgame/classictimeline.h | 12 +---- src/tools/musicsfml.cpp | 58 ++++++++++++++++++++++ src/tools/musicsfml.h | 30 +++++++++++ 15 files changed, 159 insertions(+), 58 deletions(-) rename include/{game => tools}/mathutils.h (100%) create mode 100644 include/tools/music.h create mode 100644 src/tools/musicsfml.cpp create mode 100644 src/tools/musicsfml.h diff --git a/include/game/inputtype.h b/include/game/inputtype.h index fb36035..2274218 100644 --- a/include/game/inputtype.h +++ b/include/game/inputtype.h @@ -2,7 +2,7 @@ #define INPUTTYPE_H #include -#include "game/mathutils.h" +#include "tools/mathutils.h" struct PlayerInput { diff --git a/include/game/precisionevaluator.h b/include/game/precisionevaluator.h index 2ecd1aa..69ca971 100644 --- a/include/game/precisionevaluator.h +++ b/include/game/precisionevaluator.h @@ -1,7 +1,7 @@ #ifndef PRECISIONEVALUATOR_H #define PRECISIONEVALUATOR_H -#include "game/mathutils.h" +#include "tools/mathutils.h" #include #include #include diff --git a/include/game/timeline.h b/include/game/timeline.h index 6bdcf17..b939ea2 100644 --- a/include/game/timeline.h +++ b/include/game/timeline.h @@ -1,7 +1,7 @@ #ifndef TIMELINE_H #define TIMELINE_H -#include "game/mathutils.h" +#include "tools/mathutils.h" #include #include @@ -13,10 +13,9 @@ class Timeline public: virtual ~Timeline() = default; - virtual void update() = 0; + virtual void update(const microsec& offset) = 0; virtual void clear() = 0; - virtual microsec currentMusicOffset() const = 0; virtual void drawVisibleNotes() const = 0; }; diff --git a/include/game/mathutils.h b/include/tools/mathutils.h similarity index 100% rename from include/game/mathutils.h rename to include/tools/mathutils.h diff --git a/include/tools/music.h b/include/tools/music.h new file mode 100644 index 0000000..be9d368 --- /dev/null +++ b/include/tools/music.h @@ -0,0 +1,21 @@ +#pragma once + +#include "tools/mathutils.h" +#include + +class Music +{ +public: + virtual ~Music() = default; + + virtual bool openFromFile(const std::string& filepath) = 0; + + virtual void play() = 0; + virtual void pause() = 0; + virtual void stop() = 0; + + virtual void setVolume(int volume) = 0; + + virtual void setOffset(const microsec& offset) = 0; + virtual microsec fetchOffset() = 0; +}; diff --git a/src/application.cpp b/src/application.cpp index 7efc616..a6a067b 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -7,13 +7,15 @@ #include "gui/mainmenu.h" #include "gui/gamestate.h" +#include "tools/musicsfml.h" + #include const sf::Time TIME_PER_FRAME = sf::seconds(1.f / 90.f); Application::Application() : _game_window({1280, 720}, "Test", sf::Style::Default), - _game(std::make_unique(std::make_unique(_game_window))) + _game(std::make_unique(std::make_unique(_game_window), std::make_unique())) { _game_window.setFramerateLimit(60); _game_window.setKeyRepeatEnabled(false); diff --git a/src/classicgame/classicanimationscenario.h b/src/classicgame/classicanimationscenario.h index 65b1154..c4dc02f 100644 --- a/src/classicgame/classicanimationscenario.h +++ b/src/classicgame/classicanimationscenario.h @@ -1,6 +1,6 @@ #pragma once -#include "game/mathutils.h" +#include "tools/mathutils.h" #include class ClassicSprite; diff --git a/src/classicgame/classicgame.cpp b/src/classicgame/classicgame.cpp index 98edd9e..930a5c3 100644 --- a/src/classicgame/classicgame.cpp +++ b/src/classicgame/classicgame.cpp @@ -2,11 +2,14 @@ #include "classictimeline.h" #include "classicnote.h" #include "classicmapcreator.h" +#include "tools/music.h" #include -ClassicGame::ClassicGame(std::unique_ptr&& manager) : +ClassicGame::ClassicGame(std::unique_ptr&& manager, std::unique_ptr&& music) : _timeline(std::make_unique()), - _graphics_manager(std::move(manager)) + _graphics_manager(std::move(manager)), + _music(std::move(music)), + _is_paused(false) { _slap_buffer.loadFromFile("very-final-slap.wav"); _slap.setBuffer(_slap_buffer); @@ -58,12 +61,15 @@ void ClassicGame::run() { ClassicMapCreator creator(_graphics_manager); auto beatmap = creator.createBeatmap("aa"); + _music->openFromFile("METEOR.flac"); + _music->setVolume(10); + _music->play(); _timeline->run(std::move(beatmap.notes), beatmap.visibility_offset); } void ClassicGame::input(PlayerInput&& inputdata) { - inputdata.timestamp = _timeline->currentMusicOffset(); + inputdata.timestamp = _music->fetchOffset(); switch (inputdata.event.type) { @@ -73,6 +79,22 @@ void ClassicGame::input(PlayerInput&& inputdata) case sf::Event::KeyPressed: { + if (inputdata.event.key.code == sf::Keyboard::Space) + { + if (!_is_paused) + { + _is_paused = true; + _music->pause(); + return; + } + else + { + _is_paused = false; + _music->play(); + return; + } + } + auto note_it = _timeline->getActiveNote(); if (!_timeline->isExpired(note_it)) @@ -111,7 +133,7 @@ void ClassicGame::input(PlayerInput&& inputdata) void ClassicGame::update() { - _timeline->update(); + _timeline->update(_music->fetchOffset()); _timeline->fetchVisibleNotes(); } diff --git a/src/classicgame/classicgame.h b/src/classicgame/classicgame.h index ab9b4f1..12801d1 100644 --- a/src/classicgame/classicgame.h +++ b/src/classicgame/classicgame.h @@ -7,6 +7,7 @@ #include #include +class Music; class ClassicNote; class ClassicTimeline; class ClassicGraphicsManager; @@ -14,7 +15,7 @@ class ClassicGraphicsManager; class ClassicGame final : public Game { public: - explicit ClassicGame(std::unique_ptr&& manager); + explicit ClassicGame(std::unique_ptr&& manager, std::unique_ptr&& music); virtual ~ClassicGame() override; virtual void run() override; @@ -34,6 +35,10 @@ private: std::unique_ptr _graphics_manager; sf::SoundBuffer _slap_buffer; sf::Sound _slap; + + std::unique_ptr _music; + + bool _is_paused; }; #endif // CLASSICGAME_H diff --git a/src/classicgame/classicmapcreator.h b/src/classicgame/classicmapcreator.h index 137a039..5576d91 100644 --- a/src/classicgame/classicmapcreator.h +++ b/src/classicgame/classicmapcreator.h @@ -3,7 +3,7 @@ #include -#include "game/mathutils.h" +#include "tools/mathutils.h" #include "classicgraphicsmanager.h" struct Beatmap diff --git a/src/classicgame/classicsprite.h b/src/classicgame/classicsprite.h index d22e5b3..5397af7 100644 --- a/src/classicgame/classicsprite.h +++ b/src/classicgame/classicsprite.h @@ -1,6 +1,6 @@ #pragma once -#include "game/mathutils.h" +#include "tools/mathutils.h" #include "game/sprite.h" #include "SFML/Graphics/RectangleShape.hpp" #include "SFML/Graphics/Text.hpp" diff --git a/src/classicgame/classictimeline.cpp b/src/classicgame/classictimeline.cpp index 9284eaf..11c3cbc 100644 --- a/src/classicgame/classictimeline.cpp +++ b/src/classicgame/classictimeline.cpp @@ -3,19 +3,8 @@ #include ClassicTimeline::ClassicTimeline() : - _sfml_music_offset(0), - _previous_frame_offset(0), - _absolute_offset(0) -{ - // BPM of METEOR is 170. - // Length is 1:14 - // I calculated that the time between beats is about 1412162 microseconds - - std::string song_filename = "METEOR.flac"; - - _music.openFromFile(song_filename); - _music.setVolume(10); -} + _current_offset(0) +{} void ClassicTimeline::run(std::vector&& notes, const microsec& visibility) { @@ -28,9 +17,6 @@ void ClassicTimeline::run(std::vector&& notes, const microsec& vis expire(_active_note); fetchVisibleNotes(); - _music.play(); - - _previous_frame_offset = _offset_interpolator.restart().asMicroseconds(); } ClassicTimeline::~ClassicTimeline() @@ -46,22 +32,13 @@ void ClassicTimeline::clear() _timeline.clear(); } -void ClassicTimeline::update() +void ClassicTimeline::update(const microsec& offset) { - const auto interpolator_timestamp = _offset_interpolator.getElapsedTime().asMicroseconds(); - const auto sfml_new_offset = currentMusicOffset(); - - _absolute_offset += (interpolator_timestamp - _previous_frame_offset); - _previous_frame_offset = interpolator_timestamp; - if (sfml_new_offset != _sfml_music_offset) - { - _absolute_offset = ((_absolute_offset + sfml_new_offset) / 2); - _sfml_music_offset = sfml_new_offset; - } + _current_offset = offset; checkCurrentActiveNote(); checkForNextActiveNote(); - updateVisibleSprites(_absolute_offset); + updateVisibleSprites(_current_offset); } void ClassicTimeline::checkCurrentActiveNote() @@ -115,11 +92,6 @@ void ClassicTimeline::expire(Iterator& iterator) iterator = _timeline.end(); } -microsec ClassicTimeline::currentMusicOffset() const -{ - return _music.getPlayingOffset().asMicroseconds(); -} - bool ClassicTimeline::isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const { return ((*iterator)->offset() - _visibility_offset) <= music_offset; @@ -127,7 +99,7 @@ bool ClassicTimeline::isVisiblyClose(const Iterator& iterator, const microsec& m void ClassicTimeline::fetchVisibleNotes() { - findLastVisibleNote(_absolute_offset); + findLastVisibleNote(_current_offset); findFirstVisibleNote(); } diff --git a/src/classicgame/classictimeline.h b/src/classicgame/classictimeline.h index e9e5844..ff81800 100644 --- a/src/classicgame/classictimeline.h +++ b/src/classicgame/classictimeline.h @@ -2,8 +2,6 @@ #include "game/timeline.h" -#include -#include #include #include @@ -14,10 +12,9 @@ class ClassicTimeline : public Timeline public: explicit ClassicTimeline(); virtual ~ClassicTimeline(); - virtual void update() override; + virtual void update(const microsec& offset) override; virtual void clear() override; - virtual microsec currentMusicOffset() const override; virtual void drawVisibleNotes() const override; void run(std::vector&& notes, const microsec& visibility); @@ -37,12 +34,7 @@ private: std::vector _input_intervals; std::vector _timeline; microsec _visibility_offset; - - sf::Music _music; - sf::Clock _offset_interpolator; - microsec _sfml_music_offset; - microsec _previous_frame_offset; - microsec _absolute_offset; + microsec _current_offset; void updateVisibleSprites(const microsec& music_offset); void checkCurrentActiveNote(); diff --git a/src/tools/musicsfml.cpp b/src/tools/musicsfml.cpp new file mode 100644 index 0000000..2c00bc9 --- /dev/null +++ b/src/tools/musicsfml.cpp @@ -0,0 +1,58 @@ +#include "musicsfml.h" + +MusicSFML::MusicSFML() : + _sfml_music_offset(0), + _previous_frame_offset(0), + _absolute_offset(0) +{} + +bool MusicSFML::openFromFile(const std::string& filepath) +{ + return _music.openFromFile(filepath); +} + +void MusicSFML::play() +{ + _music.play(); + _sfml_music_offset = _offset_interpolator.restart().asMicroseconds(); +} + +void MusicSFML::pause() +{ + _music.pause(); +} + +void MusicSFML::stop() +{ + _music.stop(); +} + +void MusicSFML::setVolume(int volume) +{ + _music.setVolume(volume); +} + +void MusicSFML::setOffset(const microsec& offset) +{ + _previous_frame_offset += (offset - _absolute_offset); + _music.setPlayingOffset(sf::microseconds(offset)); +} + +microsec MusicSFML::fetchOffset() +{ + if (_music.getStatus() != sf::Music::Status::Playing) + return _absolute_offset; + + const auto interpolator_timestamp = _offset_interpolator.getElapsedTime().asMicroseconds(); + const auto sfml_new_offset = _music.getPlayingOffset().asMicroseconds(); + + _absolute_offset += (interpolator_timestamp - _previous_frame_offset); + _previous_frame_offset = interpolator_timestamp; + if (sfml_new_offset != _sfml_music_offset) + { + _absolute_offset = ((_absolute_offset + sfml_new_offset) / 2); + _sfml_music_offset = sfml_new_offset; + } + + return _absolute_offset; +} diff --git a/src/tools/musicsfml.h b/src/tools/musicsfml.h new file mode 100644 index 0000000..467a113 --- /dev/null +++ b/src/tools/musicsfml.h @@ -0,0 +1,30 @@ +#pragma once + +#include "tools/music.h" + +#include + +class MusicSFML : public Music +{ +public: + explicit MusicSFML(); + + virtual bool openFromFile(const std::string& filepath) override; + + virtual void play() override; + virtual void pause() override; + virtual void stop() override; + + virtual void setVolume(int volume) override; + + virtual void setOffset(const microsec& offset) override; + virtual microsec fetchOffset() override; + +private: + sf::Music _music; + sf::Clock _offset_interpolator; + + microsec _sfml_music_offset; + microsec _previous_frame_offset; + microsec _absolute_offset; +};