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::vector<std::shared_ptr<GUIState>> _state_stack;
sf::RenderWindow _game_window;
std::shared_ptr<Game> _game;

View File

@ -8,16 +8,33 @@ class BPMCalculator
{
public:
explicit BPMCalculator(const std::shared_ptr<Music>& music);
void setMusic(const std::shared_ptr<Music>& music);
std::shared_ptr<Music> music() const;
void start();
void stop();
void click();
int getCurrentApproximation() const;
float fetchCurrentBPMApproximation();
microsec getStartingOffset() const;
void setStartingOffset(microsec offset);
void moveStartingOffsetBy(microsec shift);
microsec fetchTimeUntilNextBeat();
microsec fetchBeatInterval();
private:
bool _need_recalculate;
bool _calculating;
std::shared_ptr<Music> _music;
std::vector<microsec> _deltas;
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.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(); }};
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 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::GAME] = game_state;
_states[GUIState::Tag::EDITOR] = editor;

View File

@ -106,5 +106,6 @@ void Editor::enter()
void Editor::leave()
{
_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_height = game_window.getSize().y;
std::shared_ptr<PushButton> 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.));
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., window_width / 3., window_height / 7.));
button_start->setCallback(callbacks.onAppendGameState);
std::shared_ptr<PushButton> 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. * 4, window_width / 3., window_height / 7.));
auto button_editor = std::make_shared<PushButton>("Editor", font_holder.get(Fonts::Id::GUI), 48);
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([&]()
{
_game_window.close();
});
_buttons->addChild(button_start);
_buttons->addChild(button_editor);
_buttons->addChild(button_exit);
}

View File

@ -13,6 +13,7 @@ public:
struct Callbacks
{
std::function<void(void)> onAppendGameState;
std::function<void(void)> onAppendEditorState;
};
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);
const auto approximation = _bpm_calculator->getCurrentApproximation();
const auto approximation = _bpm_calculator->fetchCurrentBPMApproximation();
if (approximation != 0)
{
_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
}
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 update(const sf::Time& dt) 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 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:
sf::RectangleShape _slider_background;
sf::RectangleShape _slider_tick;

View File

@ -54,3 +54,13 @@ void Button::setText(const std::string& 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 update(const sf::Time& dt) 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 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;
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);
}
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 update(const sf::Time& dt) 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 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:
sf::FloatRect _rect;
};

View File

@ -113,3 +113,13 @@ void MenuBar::setVisibility(bool is_visible)
for (auto& submenu : _submenus)
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 update(const sf::Time& dt) 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 bool isUnderMouse(int mouse_x, int mouse_y) const 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 addDependentSubmenu(const std::shared_ptr<MenuDrop>& submenu);

View File

@ -147,3 +147,12 @@ bool MenuDrop::isLocked() const
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 update(const sf::Time& dt) 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 bool isUnderMouse(int mouse_x, int mouse_y) const 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 addCascadeButton(const std::shared_ptr<CascadeMenuButton>& button);
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);
}
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 update(const sf::Time& dt) 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 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:
sf::VertexArray _line;
sf::FloatRect _rect;

View File

@ -14,11 +14,15 @@ public:
virtual void input(const sf::Event& event) = 0;
virtual void update(const sf::Time& dt) = 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 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);
bool isVisible() const;

View File

@ -108,3 +108,12 @@ void Window::addBarButton(const std::string &text, std::function<void(void)> cal
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 update(const sf::Time& dt) 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 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);
protected:

View File

@ -3,18 +3,27 @@
#include <numeric>
#include <iostream>
constexpr microsec MICROSECONDS_IN_MINUTE = 60000000;
constexpr float MICROSECONDS_IN_MINUTE_f = 60000000.;
BPMCalculator::BPMCalculator(const std::shared_ptr<Music>& music) :
_music(music),
_previous_click_offset(0)
{}
_music(music)
{
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)
{
_deltas.clear();
_previous_click_offset = 0;
_music = music;
reset();
}
std::shared_ptr<Music> BPMCalculator::music() const
@ -24,15 +33,23 @@ std::shared_ptr<Music> BPMCalculator::music() const
void BPMCalculator::start()
{
_deltas.clear();
_previous_click_offset = 0;
reset();
_calculating = true;
}
void BPMCalculator::stop()
{
_calculating = false;
}
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)
{
@ -46,14 +63,57 @@ void BPMCalculator::click()
_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);
bool hasEnoughDeltas = _deltas.size() >= 8;
std::cout << "S: " << sum << " _deltas.size(): " << _deltas.size();
std::cout << "\n " << (static_cast<float>(sum) / static_cast<float>(_deltas.size())) << '\n';
return (sum == 0 || _deltas.size() < 8)
_approximated_bpm = (!hasEnoughDeltas)
? 0.
: static_cast<int>(static_cast<float>(MICROSECONDS_IN_MINUTE) / (static_cast<float>(sum) / static_cast<float>(_deltas.size())));
: calculateBPM(sum, _deltas.size());
return _approximated_bpm;
}
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());
}