Upgrade bpm calculator, try implement slider animation
This commit is contained in:
parent
55b62e62bb
commit
60d7b4e346
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -106,5 +106,6 @@ void Editor::enter()
|
|||
void Editor::leave()
|
||||
{
|
||||
_group.reset();
|
||||
_bpm_calculator.reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue