Upgrade bpm calculator, try implement slider animation

This commit is contained in:
NaiJi ✨ 2021-09-08 22:05:56 +03:00
parent 55b62e62bb
commit 60d7b4e346
23 changed files with 247 additions and 39 deletions

View File

@ -25,6 +25,7 @@ private:
std::array<std::shared_ptr<GUIState>, GUIState::Tag::AMOUNT> _states; std::array<std::shared_ptr<GUIState>, GUIState::Tag::AMOUNT> _states;
std::vector<std::shared_ptr<GUIState>> _state_stack; std::vector<std::shared_ptr<GUIState>> _state_stack;
sf::RenderWindow _game_window; sf::RenderWindow _game_window;
std::shared_ptr<Game> _game; std::shared_ptr<Game> _game;

View File

@ -8,16 +8,33 @@ class BPMCalculator
{ {
public: public:
explicit BPMCalculator(const std::shared_ptr<Music>& music); explicit BPMCalculator(const std::shared_ptr<Music>& music);
void setMusic(const std::shared_ptr<Music>& music); void setMusic(const std::shared_ptr<Music>& music);
std::shared_ptr<Music> music() const; std::shared_ptr<Music> music() const;
void start(); void start();
void stop();
void click(); void click();
int getCurrentApproximation() const; float fetchCurrentBPMApproximation();
microsec getStartingOffset() const;
void setStartingOffset(microsec offset);
void moveStartingOffsetBy(microsec shift);
microsec fetchTimeUntilNextBeat();
microsec fetchBeatInterval();
private: private:
bool _need_recalculate;
bool _calculating;
std::shared_ptr<Music> _music; std::shared_ptr<Music> _music;
std::vector<microsec> _deltas; std::vector<microsec> _deltas;
microsec _previous_click_offset; microsec _previous_click_offset;
microsec _first_click_offset;
int _approximated_bpm;
inline float calculateBPM(microsec all_microseconds, std::size_t beats_amount) const;
inline void reset();
}; };

View File

@ -26,12 +26,18 @@ Application::Application() :
_game_window.setMouseCursorGrabbed(false); _game_window.setMouseCursorGrabbed(false);
_game_window.setVerticalSyncEnabled(true); _game_window.setVerticalSyncEnabled(true);
MainMenu::Callbacks callbacks = {[&](){ pushState(GUIState::Tag::EDITOR); }}; MainMenu::Callbacks callbacks =
{
[&](){ pushState(GUIState::Tag::GAME); },
[&](){ pushState(GUIState::Tag::EDITOR); }
};
Editor::Callbacks editor_callbacks = {[&](){ popState(); }}; Editor::Callbacks editor_callbacks = {[&](){ popState(); }};
const auto main_menu = std::make_shared<MainMenu>(_game_window, std::move(callbacks), _font_holder); const auto main_menu = std::make_shared<MainMenu>(_game_window, std::move(callbacks), _font_holder);
const auto game_state = std::make_shared<GameState>(_game_window, _game, GameState::Callbacks()); const auto game_state = std::make_shared<GameState>(_game_window, _game, GameState::Callbacks());
const auto editor = std::make_shared<Editor>(_game_window, std::move(editor_callbacks), std::make_unique<MusicSFML>(), _font_holder); const auto editor = std::make_shared<Editor>(_game_window, std::move(editor_callbacks), std::make_unique<MusicSFML>(), _font_holder);
_states[GUIState::Tag::MAIN_MENU] = main_menu; _states[GUIState::Tag::MAIN_MENU] = main_menu;
_states[GUIState::Tag::GAME] = game_state; _states[GUIState::Tag::GAME] = game_state;
_states[GUIState::Tag::EDITOR] = editor; _states[GUIState::Tag::EDITOR] = editor;

View File

@ -106,5 +106,6 @@ void Editor::enter()
void Editor::leave() void Editor::leave()
{ {
_group.reset(); _group.reset();
_bpm_calculator.reset();
} }

View File

@ -9,18 +9,23 @@ MainMenu::MainMenu(sf::RenderWindow& game_window, Callbacks&& callbacks, const F
const float window_width = game_window.getSize().x; const float window_width = game_window.getSize().x;
const float window_height = game_window.getSize().y; const float window_height = game_window.getSize().y;
std::shared_ptr<PushButton> button_start = std::make_shared<PushButton>("Start", font_holder.get(Fonts::Id::GUI), 48); auto button_start = std::make_shared<PushButton>("Start", font_holder.get(Fonts::Id::GUI), 48);
button_start->setRect(sf::FloatRect(window_width / 3., window_height / 7. * 2, window_width / 3., window_height / 7.)); button_start->setRect(sf::FloatRect(window_width / 3., window_height / 7., window_width / 3., window_height / 7.));
button_start->setCallback(callbacks.onAppendGameState); button_start->setCallback(callbacks.onAppendGameState);
std::shared_ptr<PushButton> button_exit = std::make_shared<PushButton>("Exit", font_holder.get(Fonts::Id::GUI), 48); auto button_editor = std::make_shared<PushButton>("Editor", font_holder.get(Fonts::Id::GUI), 48);
button_exit->setRect(sf::FloatRect(window_width / 3., window_height / 7. * 4, window_width / 3., window_height / 7.)); button_editor->setRect(sf::FloatRect(window_width / 3., window_height / 7. * 3, window_width / 3., window_height / 7.));
button_editor->setCallback(callbacks.onAppendEditorState);
auto button_exit = std::make_shared<PushButton>("Exit", font_holder.get(Fonts::Id::GUI), 48);
button_exit->setRect(sf::FloatRect(window_width / 3., window_height / 7. * 5, window_width / 3., window_height / 7.));
button_exit->setCallback([&]() button_exit->setCallback([&]()
{ {
_game_window.close(); _game_window.close();
}); });
_buttons->addChild(button_start); _buttons->addChild(button_start);
_buttons->addChild(button_editor);
_buttons->addChild(button_exit); _buttons->addChild(button_exit);
} }

View File

@ -13,6 +13,7 @@ public:
struct Callbacks struct Callbacks
{ {
std::function<void(void)> onAppendGameState; std::function<void(void)> onAppendGameState;
std::function<void(void)> onAppendEditorState;
}; };
explicit MainMenu(sf::RenderWindow& game_window, Callbacks&& callbacks, const FontHolder &font_holder); explicit MainMenu(sf::RenderWindow& game_window, Callbacks&& callbacks, const FontHolder &font_holder);

View File

@ -34,10 +34,18 @@ void BPMCalculatorWidget::update(const sf::Time& dt)
{ {
Window::update(dt); Window::update(dt);
const auto approximation = _bpm_calculator->getCurrentApproximation(); const auto approximation = _bpm_calculator->fetchCurrentBPMApproximation();
if (approximation != 0) if (approximation != 0)
{ {
_bpm_value.setString(std::to_string(approximation)); _bpm_value.setString(std::to_string(approximation));
const microsec until_beat = _bpm_calculator->fetchTimeUntilNextBeat();
const microsec beat_interval = _bpm_calculator->fetchBeatInterval();
const auto time_relation = beat_interval / until_beat;
const auto slider_path_left = Window::rect().width / time_relation;
_slider->setTickPosition(slider_path_left);
} }
} }

View File

@ -49,3 +49,17 @@ bool BPMSlider::isUnderMouse(int mouse_x, int mouse_y) const
return mouse_x == mouse_y; // just to compile return mouse_x == mouse_y; // just to compile
} }
sf::FloatRect BPMSlider::rect() const
{
return _slider_background.getGlobalBounds();
}
sf::Vector2f BPMSlider::position() const
{
return _slider_background.getPosition();
}
void BPMSlider::setTickPosition(float x_position)
{
_slider_tick.setPosition(_slider_background.getPosition().x + x_position, _slider_tick.getPosition().y);
}

View File

@ -13,11 +13,17 @@ public:
virtual void input(const sf::Event& event) override; virtual void input(const sf::Event& event) override;
virtual void update(const sf::Time& dt) override; virtual void update(const sf::Time& dt) override;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
virtual void setRect(const sf::FloatRect& rect) override;
virtual void setPosition(const sf::Vector2f& position) override;
virtual void move(const sf::Vector2f& delta) override; virtual void move(const sf::Vector2f& delta) override;
virtual bool isUnderMouse(int mouse_x, int mouse_y) const override; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override;
virtual void setRect(const sf::FloatRect& rect) override;
virtual sf::FloatRect rect() const override;
virtual void setPosition(const sf::Vector2f& position) override;
virtual sf::Vector2f position() const override;
void setTickPosition(float x_position);
private: private:
sf::RectangleShape _slider_background; sf::RectangleShape _slider_background;
sf::RectangleShape _slider_tick; sf::RectangleShape _slider_tick;

View File

@ -54,3 +54,13 @@ void Button::setText(const std::string& text)
{ {
_button_text.setString(text); _button_text.setString(text);
} }
sf::FloatRect Button::rect() const
{
return _button_content.getGlobalBounds();
}
sf::Vector2f Button::position() const
{
return _button_content.getPosition();
}

View File

@ -13,11 +13,14 @@ public:
virtual void input(const sf::Event& event) override = 0; virtual void input(const sf::Event& event) override = 0;
virtual void update(const sf::Time& dt) override final; virtual void update(const sf::Time& dt) override final;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override final; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override final;
virtual void setPosition(const sf::Vector2f& position) override final;
virtual void move(const sf::Vector2f& delta) override final; virtual void move(const sf::Vector2f& delta) override final;
virtual bool isUnderMouse(int mouse_x, int mouse_y) const override final; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override final;
virtual void setRect(const sf::FloatRect& rect) override; virtual void setRect(const sf::FloatRect& rect) override;
virtual sf::FloatRect rect() const override;
virtual void setPosition(const sf::Vector2f& position) override;
virtual sf::Vector2f position() const override;
virtual void setText(const std::string& text); virtual void setText(const std::string& text);

View File

@ -38,3 +38,18 @@ bool Group::isUnderMouse(int mouse_x, int mouse_y) const
{ {
return _rect.contains(mouse_x, mouse_y); return _rect.contains(mouse_x, mouse_y);
} }
sf::FloatRect Group::rect() const
{
return _rect;
}
sf::Vector2f Group::position() const
{
return sf::Vector2f
{
_rect.top,
_rect.left
};
}

View File

@ -8,11 +8,15 @@ public:
virtual void input(const sf::Event& event) override; virtual void input(const sf::Event& event) override;
virtual void update(const sf::Time& dt) override; virtual void update(const sf::Time& dt) override;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
virtual void setRect(const sf::FloatRect& rect) override;
virtual void setPosition(const sf::Vector2f& position) override;
virtual void move(const sf::Vector2f& delta) override; virtual void move(const sf::Vector2f& delta) override;
virtual bool isUnderMouse(int mouse_x, int mouse_y) const override; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override;
virtual void setRect(const sf::FloatRect& rect) override;
virtual sf::FloatRect rect() const override;
virtual void setPosition(const sf::Vector2f& position) override;
virtual sf::Vector2f position() const override;
private: private:
sf::FloatRect _rect; sf::FloatRect _rect;
}; };

View File

@ -113,3 +113,13 @@ void MenuBar::setVisibility(bool is_visible)
for (auto& submenu : _submenus) for (auto& submenu : _submenus)
submenu->setVisibility(false); submenu->setVisibility(false);
} }
sf::FloatRect MenuBar::rect() const
{
return _bar_rect.getGlobalBounds();
}
sf::Vector2f MenuBar::position() const
{
return _bar_rect.getPosition();
}

View File

@ -14,12 +14,16 @@ public:
virtual void input(const sf::Event& event) override; virtual void input(const sf::Event& event) override;
virtual void update(const sf::Time& dt) override; virtual void update(const sf::Time& dt) override;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
virtual void setRect(const sf::FloatRect& rect) override;
virtual void setPosition(const sf::Vector2f& position) override;
virtual void move(const sf::Vector2f& delta) override; virtual void move(const sf::Vector2f& delta) override;
virtual bool isUnderMouse(int mouse_x, int mouse_y) const override; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override;
virtual void setVisibility(bool is_visible = true) override; virtual void setVisibility(bool is_visible = true) override;
virtual void setRect(const sf::FloatRect& rect) override;
virtual sf::FloatRect rect() const override;
virtual void setPosition(const sf::Vector2f& position) override;
virtual sf::Vector2f position() const override;
void addRootSubMenu(std::string name, const std::shared_ptr<MenuDrop>& submenu); void addRootSubMenu(std::string name, const std::shared_ptr<MenuDrop>& submenu);
void addDependentSubmenu(const std::shared_ptr<MenuDrop>& submenu); void addDependentSubmenu(const std::shared_ptr<MenuDrop>& submenu);

View File

@ -147,3 +147,12 @@ bool MenuDrop::isLocked() const
return _is_locked; return _is_locked;
} }
sf::FloatRect MenuDrop::rect() const
{
return _content_rect.getGlobalBounds();
}
sf::Vector2f MenuDrop::position() const
{
return _content_rect.getPosition();
}

View File

@ -13,12 +13,16 @@ public:
virtual void input(const sf::Event& event) override; virtual void input(const sf::Event& event) override;
virtual void update(const sf::Time& dt) override; virtual void update(const sf::Time& dt) override;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
virtual void setRect(const sf::FloatRect& rect) override;
virtual void setPosition(const sf::Vector2f& position) override;
virtual void move(const sf::Vector2f& delta) override; virtual void move(const sf::Vector2f& delta) override;
virtual bool isUnderMouse(int mouse_x, int mouse_y) const override; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override;
virtual void setVisibility(bool is_visible = true) override; virtual void setVisibility(bool is_visible = true) override;
virtual void setRect(const sf::FloatRect& rect) override;
virtual sf::FloatRect rect() const override;
virtual void setPosition(const sf::Vector2f& position) override;
virtual sf::Vector2f position() const override;
void addPushButton(const std::shared_ptr<PushButton>& button); void addPushButton(const std::shared_ptr<PushButton>& button);
void addCascadeButton(const std::shared_ptr<CascadeMenuButton>& button); void addCascadeButton(const std::shared_ptr<CascadeMenuButton>& button);
void addSeparator(); void addSeparator();

View File

@ -37,3 +37,12 @@ bool MenuSeparator::isUnderMouse(int mouse_x, int mouse_y) const
return _is_visible && _rect.contains(mouse_x, mouse_y); return _is_visible && _rect.contains(mouse_x, mouse_y);
} }
sf::FloatRect MenuSeparator::rect() const
{
return {};
}
sf::Vector2f MenuSeparator::position() const
{
return {};
}

View File

@ -10,11 +10,15 @@ public:
virtual void input(const sf::Event& event) override; virtual void input(const sf::Event& event) override;
virtual void update(const sf::Time& dt) override; virtual void update(const sf::Time& dt) override;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
virtual void setRect(const sf::FloatRect& rect) override;
virtual void setPosition(const sf::Vector2f& position) override;
virtual void move(const sf::Vector2f& delta) override; virtual void move(const sf::Vector2f& delta) override;
virtual bool isUnderMouse(int mouse_x, int mouse_y) const override; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override;
virtual void setRect(const sf::FloatRect& rect) override;
virtual sf::FloatRect rect() const override;
virtual void setPosition(const sf::Vector2f& position) override;
virtual sf::Vector2f position() const override;
private: private:
sf::VertexArray _line; sf::VertexArray _line;
sf::FloatRect _rect; sf::FloatRect _rect;

View File

@ -14,11 +14,15 @@ public:
virtual void input(const sf::Event& event) = 0; virtual void input(const sf::Event& event) = 0;
virtual void update(const sf::Time& dt) = 0; virtual void update(const sf::Time& dt) = 0;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const = 0; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const = 0;
virtual void setRect(const sf::FloatRect& rect) = 0;
virtual void setPosition(const sf::Vector2f& position) = 0;
virtual void move(const sf::Vector2f& delta) = 0; virtual void move(const sf::Vector2f& delta) = 0;
virtual bool isUnderMouse(int mouse_x, int mouse_y) const = 0; virtual bool isUnderMouse(int mouse_x, int mouse_y) const = 0;
virtual void setRect(const sf::FloatRect& rect) = 0;
virtual sf::FloatRect rect() const = 0;
virtual void setPosition(const sf::Vector2f& position) = 0;
virtual sf::Vector2f position() const = 0;
virtual void setVisibility(bool is_visible = true); virtual void setVisibility(bool is_visible = true);
bool isVisible() const; bool isVisible() const;

View File

@ -108,3 +108,12 @@ void Window::addBarButton(const std::string &text, std::function<void(void)> cal
addChild(b); addChild(b);
} }
sf::FloatRect Window::rect() const
{
return _window_content.getGlobalBounds();
}
sf::Vector2f Window::position() const
{
return _window_content.getPosition();
}

View File

@ -13,11 +13,15 @@ public:
virtual void input(const sf::Event& event) override; virtual void input(const sf::Event& event) override;
virtual void update(const sf::Time& dt) override; virtual void update(const sf::Time& dt) override;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
virtual void setRect(const sf::FloatRect& rect) override;
virtual void setPosition(const sf::Vector2f& position) override;
virtual void move(const sf::Vector2f& delta) override; virtual void move(const sf::Vector2f& delta) override;
virtual bool isUnderMouse(int mouse_x, int mouse_y) const override final; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override final;
virtual void setRect(const sf::FloatRect& rect) override;
virtual sf::FloatRect rect() const override;
virtual void setPosition(const sf::Vector2f& position) override;
virtual sf::Vector2f position() const override;
void addBarButton(const std::string& text, std::function<void(void)> callback); void addBarButton(const std::string& text, std::function<void(void)> callback);
protected: protected:

View File

@ -3,18 +3,27 @@
#include <numeric> #include <numeric>
#include <iostream> #include <iostream>
constexpr microsec MICROSECONDS_IN_MINUTE = 60000000; constexpr float MICROSECONDS_IN_MINUTE_f = 60000000.;
BPMCalculator::BPMCalculator(const std::shared_ptr<Music>& music) : BPMCalculator::BPMCalculator(const std::shared_ptr<Music>& music) :
_music(music), _music(music)
_previous_click_offset(0) {
{} reset();
}
void BPMCalculator::reset()
{
_calculating = false;
_deltas.clear();
_previous_click_offset = 0;
_approximated_bpm = 0;
_need_recalculate = true;
}
void BPMCalculator::setMusic(const std::shared_ptr<Music>& music) void BPMCalculator::setMusic(const std::shared_ptr<Music>& music)
{ {
_deltas.clear();
_previous_click_offset = 0;
_music = music; _music = music;
reset();
} }
std::shared_ptr<Music> BPMCalculator::music() const std::shared_ptr<Music> BPMCalculator::music() const
@ -24,15 +33,23 @@ std::shared_ptr<Music> BPMCalculator::music() const
void BPMCalculator::start() void BPMCalculator::start()
{ {
_deltas.clear(); reset();
_previous_click_offset = 0;
_calculating = true;
}
void BPMCalculator::stop()
{
_calculating = false;
} }
void BPMCalculator::click() void BPMCalculator::click()
{ {
const microsec click_offset = _music->fetchOffset(); if (!_calculating)
return;
std::cout << click_offset << "\n\n\n\n"; const microsec click_offset = _music->fetchOffset();
_need_recalculate = true;
if (_previous_click_offset == 0) if (_previous_click_offset == 0)
{ {
@ -46,14 +63,57 @@ void BPMCalculator::click()
_previous_click_offset = click_offset; _previous_click_offset = click_offset;
} }
int BPMCalculator::getCurrentApproximation() const float BPMCalculator::fetchCurrentBPMApproximation()
{ {
if (!_need_recalculate)
return _approximated_bpm;
_need_recalculate = false;
const microsec sum = std::accumulate(_deltas.begin(), _deltas.end(), 0); const microsec sum = std::accumulate(_deltas.begin(), _deltas.end(), 0);
bool hasEnoughDeltas = _deltas.size() >= 8;
std::cout << "S: " << sum << " _deltas.size(): " << _deltas.size(); _approximated_bpm = (!hasEnoughDeltas)
std::cout << "\n " << (static_cast<float>(sum) / static_cast<float>(_deltas.size())) << '\n'; ? 0.
: calculateBPM(sum, _deltas.size());
return (sum == 0 || _deltas.size() < 8) return _approximated_bpm;
? 0. }
: static_cast<int>(static_cast<float>(MICROSECONDS_IN_MINUTE) / (static_cast<float>(sum) / static_cast<float>(_deltas.size())));
float BPMCalculator::calculateBPM(microsec all_microseconds, std::size_t beats_amount) const
{
if (beats_amount == 0)
return 0;
float relation = static_cast<float>(all_microseconds)
/ static_cast<float>(beats_amount);
return static_cast<float>(1. / relation);
}
microsec BPMCalculator::getStartingOffset() const
{
return _first_click_offset;
}
void BPMCalculator::setStartingOffset(microsec offset)
{
_first_click_offset = offset;
}
void BPMCalculator::moveStartingOffsetBy(microsec shift)
{
_first_click_offset += shift;
}
microsec BPMCalculator::fetchTimeUntilNextBeat()
{
const microsec actual_offset = _music->fetchOffset() - _first_click_offset;
return fetchBeatInterval() % actual_offset;
}
microsec BPMCalculator::fetchBeatInterval()
{
return static_cast<microsec>(1. / fetchCurrentBPMApproximation());
} }