From 192e371d2f500a048638e123dde2493bcd527484 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Fri, 11 Jun 2021 19:58:44 +0300 Subject: [PATCH] All simple curve flying animation --- include/application.h | 2 +- include/note.h | 3 +- include/precisionevaluator.h | 10 +++--- include/timeline.h | 2 +- src/application.cpp | 8 +++-- src/classicgame/classicgame.cpp | 47 +++++++++++++++++++++---- src/classicgame/classicgame.h | 6 +++- src/classicgame/classicnote.cpp | 53 ++++++++++++++++++++++++----- src/classicgame/classicnote.h | 25 +++++++++++--- src/classicgame/classicsprite.cpp | 12 +++++-- src/classicgame/classicsprite.h | 4 ++- src/classicgame/classictimeline.cpp | 33 +++++++++++++----- src/classicgame/classictimeline.h | 4 +-- 13 files changed, 164 insertions(+), 45 deletions(-) diff --git a/include/application.h b/include/application.h index 7fda7e8..35b64d1 100644 --- a/include/application.h +++ b/include/application.h @@ -30,7 +30,7 @@ private: std::unique_ptr _game; - void startGameLoop(); + void exec(); }; #endif // APPLICATION_H diff --git a/include/note.h b/include/note.h index df70b3f..ffd9165 100644 --- a/include/note.h +++ b/include/note.h @@ -13,7 +13,8 @@ public: _perfect_offset(perfect_offset) {} virtual ~Note() = default; - virtual bool isActive(microsec music_offset) const = 0; + virtual bool isActive(const microsec& music_offset) const = 0; + virtual void update(const microsec& music_offset) = 0; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const = 0; virtual microsec offset() const diff --git a/include/precisionevaluator.h b/include/precisionevaluator.h index b24b53b..97a4d7e 100644 --- a/include/precisionevaluator.h +++ b/include/precisionevaluator.h @@ -10,7 +10,7 @@ using microsec = sf::Int64; -template::value>> +template::value>> class PrecisionEvaluator { public: @@ -34,7 +34,7 @@ public: && music_play_offset < _end_handling_offset; } - inline GRADE calculatePrecision(microsec odds) const noexcept + inline Grade calculatePrecision(microsec odds) const noexcept { microsec shift_from_perfect = std::abs(odds - offset()); @@ -45,7 +45,7 @@ public: break; } - return static_cast(raw_grade); + return static_cast(raw_grade); } private: @@ -53,9 +53,9 @@ private: microsec _start_handling_offset; microsec _end_handling_offset; - /* Amount of values in enum instanced as GRADES + /* Amount of values in enum instanced as GradeS * represents capacity of _intervals. - * So, for each V value in GRADES enum, _intervals[V] + * So, for each V value in GradeS enum, _intervals[V] * should return time shift from V - 1. * V0 is PERFECT SCORE and the last V represents the worst * grades which is death of note by expiration */ diff --git a/include/timeline.h b/include/timeline.h index f303393..b51aad9 100644 --- a/include/timeline.h +++ b/include/timeline.h @@ -13,7 +13,7 @@ public: virtual ~Timeline() = default; virtual void update() = 0; - virtual void init() = 0; + virtual void run() = 0; virtual void clear() = 0; virtual microsec currentMusicOffset() const = 0; diff --git a/src/application.cpp b/src/application.cpp index c9003bf..825e76f 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -24,11 +24,12 @@ Application::~Application() void Application::run() { _game_window.display(); + _game->run(); - startGameLoop(); + exec(); } -void Application::startGameLoop() +void Application::exec() { sf::Clock timer; sf::Time time_since_last_update = sf::Time::Zero; @@ -54,6 +55,9 @@ void Application::input() sf::Event event; while (_game_window.pollEvent(event)) { + if (event.type == sf::Event::Closed) + _game_window.close(); + _game->input(event); } } diff --git a/src/classicgame/classicgame.cpp b/src/classicgame/classicgame.cpp index 69a678f..9575f80 100644 --- a/src/classicgame/classicgame.cpp +++ b/src/classicgame/classicgame.cpp @@ -8,9 +8,7 @@ ClassicGame::ClassicGame() : _timeline(std::make_unique()), _view_manager(std::make_unique()) { - _timeline->fetchVisibleNotes(_view_manager); - _timeline->init(); - + _font.loadFromFile("VeraMono.ttf"); _keys_to_buttons = { {sf::Keyboard::Up, Button::UP}, // Load from settings @@ -55,7 +53,8 @@ ClassicGame::~ClassicGame() void ClassicGame::run() { - + _timeline->fetchVisibleNotes(_view_manager); + _timeline->run(); } void ClassicGame::input(const sf::Event& event) @@ -66,7 +65,8 @@ void ClassicGame::input(const sf::Event& event) switch (event.type) { default: - break; + return; + break; case sf::Event::KeyPressed: { if (_keys_to_buttons.find(event.key.code) != _keys_to_buttons.end()) @@ -83,8 +83,26 @@ void ClassicGame::input(const sf::Event& event) auto note = _timeline->getActiveNote(); - if (!_timeline->isExpired(note)) - (*note)->input(ClassicInputType(timestamp, new_action)); + if (!_timeline->isExpired(note) || (*note)->state() != ClassicNote::State::DEAD) + { + auto grade = (*note)->input(ClassicInputType(timestamp, new_action)); + + sf::Text new_grade; + new_grade.setFillColor(sf::Color::White); + new_grade.setPosition((*note)->getCoordinates().x, (*note)->getCoordinates().y - 40); + switch (grade) + { + case ClassicNote::Grade::PERFECT: + new_grade.setString("PERFECT"); break; + case ClassicNote::Grade::GOOD: + new_grade.setString("GOOD"); break; + case ClassicNote::Grade::BAD: + new_grade.setString("BAD"); break; + } + + new_grade.setFont(_font); + _grades.emplace_back(new_grade); + } } Action ClassicGame::getActionKeyPressed(Button button) const @@ -101,9 +119,24 @@ void ClassicGame::update() { _timeline->update(); _timeline->fetchVisibleNotes(_view_manager); + + for (auto& grade : _grades) + { + if (grade.getFillColor().a > 20) + { + grade.setFillColor(sf::Color(255, 255, 255, grade.getFillColor().a - 20)); + } + } + + _grades.remove_if([](const auto& grade) { return grade.getFillColor().a <= 20; }); } void ClassicGame::draw(sf::RenderWindow& window) const { _timeline->drawVisibleNotes(window); + + for (auto& grade : _grades) + { + window.draw(grade); + } } diff --git a/src/classicgame/classicgame.h b/src/classicgame/classicgame.h index 125d8f3..3dc00cf 100644 --- a/src/classicgame/classicgame.h +++ b/src/classicgame/classicgame.h @@ -2,7 +2,8 @@ #define CLASSICGAME_H #include - +#include +#include #include "game.h" #include "classicactions.h" @@ -31,6 +32,9 @@ private: std::unique_ptr _timeline; std::unique_ptr _view_manager; + + sf::Font _font; + std::list _grades; }; #endif // CLASSICGAME_H diff --git a/src/classicgame/classicnote.cpp b/src/classicgame/classicnote.cpp index 84c4734..e327feb 100644 --- a/src/classicgame/classicnote.cpp +++ b/src/classicgame/classicnote.cpp @@ -8,22 +8,43 @@ ClassicNote::ClassicNote(const std::vector& intervals, microsec perfec Note(perfect_offset), _coordinates(coord), _evaluator(intervals, _perfect_offset), - _action(action) + _action(action), + _appearance_time(0) {} -bool ClassicNote::isActive(microsec music_offset) const +bool ClassicNote::isActive(const microsec& music_offset) const { return _evaluator.isActive(music_offset); } +static int getPt( int n1 , int n2 , float perc ) +{ + int diff = n2 - n1; + + return n1 + ( diff * perc ); +} + +void ClassicNote::update(const microsec& music_offset) +{ + auto update_time = music_offset - _appearance_time; + auto i = update_time / _trail_path_percent / 100; + + int xa = getPt( 720./2. , 1280./2. , i ); + int ya = getPt( 0 , 720./2. , i ); + int xb = getPt( 1280./2. , _coordinates.x , i ); + int yb = getPt( 720./2. , _coordinates.y , i ); + + _sprite->setTrailCoordinates(getPt( xa , xb , i ), getPt( ya , yb , i )); +} + void ClassicNote::draw(sf::RenderTarget& target, sf::RenderStates states) const { target.draw(*_sprite, states); } -ClassicNote::GRADE ClassicNote::input(ClassicInputType&& input_data) -{ - auto grade = ClassicNote::GRADE::BAD; +auto ClassicNote::input(ClassicInputType&& input_data) -> Grade +{ + auto grade = ClassicNote::Grade::BAD; if (input_data == _action) { @@ -31,7 +52,7 @@ ClassicNote::GRADE ClassicNote::input(ClassicInputType&& input_data) } std::cout << "User input: " << static_cast(grade) << "\n"; - + _state = State::DEAD; return grade; } @@ -40,19 +61,35 @@ Action ClassicNote::action() const return _action; } +auto ClassicNote::state() const -> State +{ + return _state; +} + +void ClassicNote::setState(State next_state) +{ + _state = next_state; +} + std::shared_ptr ClassicNote::sprite() const noexcept { return _sprite; } +void ClassicNote::saveAppearanceTime(const microsec &offset) +{ + _appearance_time = offset; + _trail_path_percent = ((_perfect_offset - _appearance_time) * 0.01); +} + void ClassicNote::setSprite(const std::shared_ptr& sprite) noexcept { _sprite = sprite; if (_sprite) - _sprite->setCoordinates(_coordinates.x, _coordinates.y); + _sprite->setCoordinates(_coordinates.x, _coordinates.y, 720/2, 50); } -inline const Coordinates& ClassicNote::getCoordinates() const noexcept +const Coordinates& ClassicNote::getCoordinates() const noexcept { return _coordinates; } diff --git a/src/classicgame/classicnote.h b/src/classicgame/classicnote.h index 3320197..d6caae3 100644 --- a/src/classicgame/classicnote.h +++ b/src/classicgame/classicnote.h @@ -23,31 +23,46 @@ class ClassicNote : public Note { public: - enum class GRADE + enum class Grade { PERFECT, GOOD, BAD }; + enum class State + { + FLYING, + DYING, + DEAD + }; + explicit ClassicNote(const std::vector& intervals, microsec perfect_offset, Action action, const Coordinates& coord); virtual ~ClassicNote() = default; - virtual bool isActive(microsec music_offset) const override; + virtual bool isActive(const microsec& music_offset) const override; + virtual void update(const microsec &music_offset) override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; - GRADE input(ClassicInputType&& input_data); + Grade input(ClassicInputType&& input_data); Action action() const; + State state() const; + + void setState(State next_state); std::shared_ptr sprite() const noexcept; + void saveAppearanceTime(const microsec& offset); void setSprite(const std::shared_ptr& sprite) noexcept; - inline const Coordinates& getCoordinates() const noexcept; + const Coordinates& getCoordinates() const noexcept; private: const Coordinates _coordinates; - const PrecisionEvaluator _evaluator; + const PrecisionEvaluator _evaluator; const Action _action; + State _state = State::FLYING; std::shared_ptr _sprite; + microsec _appearance_time; + float _trail_path_percent; //100% for sprite falling trajectory }; diff --git a/src/classicgame/classicsprite.cpp b/src/classicgame/classicsprite.cpp index a7e8cde..80a43ea 100644 --- a/src/classicgame/classicsprite.cpp +++ b/src/classicgame/classicsprite.cpp @@ -2,15 +2,23 @@ #include ClassicSprite::ClassicSprite(const sf::RectangleShape& shape) : - _shape(shape) + _shape(shape), + _trail(shape) {} void ClassicSprite::draw(sf::RenderTarget& target, sf::RenderStates states) const { target.draw(_shape, states); + target.draw(_trail, states); } -void ClassicSprite::setCoordinates(float x, float y) noexcept +void ClassicSprite::setCoordinates(float x, float y, float trail_x, float trail_y) noexcept { _shape.setPosition(x, y); + _trail.setPosition(trail_x, trail_y); +} + +void ClassicSprite::setTrailCoordinates(float trail_x, float trail_y) noexcept +{ + _trail.setPosition(trail_x, trail_y); } diff --git a/src/classicgame/classicsprite.h b/src/classicgame/classicsprite.h index fac4054..9de9f09 100644 --- a/src/classicgame/classicsprite.h +++ b/src/classicgame/classicsprite.h @@ -9,8 +9,10 @@ public: ClassicSprite(const sf::RectangleShape& shape); virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; - void setCoordinates(float x, float y) noexcept; + void setCoordinates(float x, float y, float trail_x, float trail_y) noexcept; + void setTrailCoordinates(float trail_x, float trail_y) noexcept; private: sf::RectangleShape _shape; + sf::RectangleShape _trail; }; diff --git a/src/classicgame/classictimeline.cpp b/src/classicgame/classictimeline.cpp index 7020b91..5ecb39a 100644 --- a/src/classicgame/classictimeline.cpp +++ b/src/classicgame/classictimeline.cpp @@ -11,20 +11,20 @@ ClassicTimeline::ClassicTimeline() // Length is 1:14 // I calculated that the time between beats is about 1412162 microseconds - std::string song_filename = "/home/naiji/METEOR.flac"; + std::string song_filename = "METEOR.flac"; _music.openFromFile(song_filename); _music.setVolume(10); _timeline.reserve(1000); - microsec starting_beat_offset = 372162; + microsec starting_beat_offset = 352162; int amount_of_beats = 209; microsec interval = 1412162; microsec note_input_offset = 412162; microsec bpm_iterator = starting_beat_offset; microsec bpm_end = starting_beat_offset + (interval * amount_of_beats); - _visibility_offset = note_input_offset * 6; + _visibility_offset = note_input_offset * 8; _timeline.emplace_back(new ClassicNote({note_input_offset}, bpm_iterator, Action::PRESS_DOWN, {90, 90})); bpm_iterator += interval; @@ -35,10 +35,13 @@ ClassicTimeline::ClassicTimeline() _timeline.emplace_back(new ClassicNote({note_input_offset}, bpm_iterator, Action::PRESS_LEFT, {290, 90})); bpm_iterator += interval; + float x = 90.; + while (bpm_iterator < bpm_end) { - _timeline.emplace_back(new ClassicNote({note_input_offset}, bpm_iterator, Action::PRESS_UP, {390, 390})); + _timeline.emplace_back(new ClassicNote({note_input_offset}, bpm_iterator, Action::PRESS_UP, {x, 390.})); bpm_iterator += interval; + x += 70; } expire(_last_visible_note); @@ -46,7 +49,7 @@ ClassicTimeline::ClassicTimeline() _top_note = _timeline.begin(); } -void ClassicTimeline::init() +void ClassicTimeline::run() { _music.play(); } @@ -113,7 +116,7 @@ microsec ClassicTimeline::currentMusicOffset() const return _music.getPlayingOffset().asMicroseconds(); } -void ClassicTimeline::discardExpiredNotes(const std::unique_ptr &view_manager, const microsec &music_offset) +void ClassicTimeline::discardExpiredNotes(const std::unique_ptr &view_manager) { if (_top_note == _timeline.begin()) return; @@ -137,15 +140,25 @@ bool ClassicTimeline::isVisiblyClose(const Iterator &iterator, const microsec &m void ClassicTimeline::fetchVisibleNotes(const std::unique_ptr& view_manager) { - microsec music_offset = currentMusicOffset(); - discardExpiredNotes(view_manager, music_offset); + const microsec music_offset = currentMusicOffset(); + discardExpiredNotes(view_manager); Iterator note_iterator = _top_note; while (isVisiblyClose(note_iterator, music_offset)) { ClassicNote* note = *note_iterator; if (!note->sprite()) + { + note->saveAppearanceTime(music_offset); view_manager->initNoteSprite(note); + } + + if (note->state() == ClassicNote::State::DEAD) + { + view_manager->resetNoteSprite(note); + } + else + note->update(music_offset); ++note_iterator; } @@ -157,13 +170,15 @@ void ClassicTimeline::drawVisibleNotes(sf::RenderWindow &window) const { bool no_visible_notes = isExpired(_last_visible_note) || _top_note > _last_visible_note; + if (no_visible_notes) return; Iterator note_to_draw = _top_note; while (note_to_draw != (_last_visible_note)) { - window.draw(*(*note_to_draw)); + if ((*note_to_draw)->sprite()) + window.draw(*(*note_to_draw)); ++note_to_draw; } } diff --git a/src/classicgame/classictimeline.h b/src/classicgame/classictimeline.h index c6f05c2..68cd7b0 100644 --- a/src/classicgame/classictimeline.h +++ b/src/classicgame/classictimeline.h @@ -14,7 +14,7 @@ public: explicit ClassicTimeline(); virtual ~ClassicTimeline(); virtual void update() override; - virtual void init() override; + virtual void run() override; virtual void clear() override; virtual microsec currentMusicOffset() const override; @@ -41,7 +41,7 @@ private: void checkCurrentActiveNote(const microsec &music_offset); void checkForNextActiveNote(const microsec &music_offset); - void discardExpiredNotes(const std::unique_ptr& view_manager, const microsec &music_offset); + void discardExpiredNotes(const std::unique_ptr& view_manager); bool isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const; /* Difference between top and active note is that