Iterative refactoring of what's done to this moment

This commit is contained in:
NaiJi ✨ 2021-06-16 18:16:18 +03:00
parent ad1b31c95c
commit 106697f6af
13 changed files with 188 additions and 122 deletions

View File

@ -4,4 +4,5 @@ class Sprite
{ {
public: public:
virtual ~Sprite() = default; virtual ~Sprite() = default;
virtual void reset() = 0;
}; };

View File

@ -1,12 +0,0 @@
#ifndef TIMELINEVIEWMANAGER_H
#define TIMELINEVIEWMANAGER_H
class Note;
class TimelineViewManager
{
public:
virtual ~TimelineViewManager() = default;
};
#endif // TIMELINEVIEWMANAGER_H

View File

@ -44,10 +44,16 @@ void Application::input()
sf::Event event; sf::Event event;
while (_game_window.pollEvent(event)) while (_game_window.pollEvent(event))
{ {
if (event.type == sf::Event::Closed) switch(event.type)
{
case sf::Event::Closed:
_game_window.close(); _game_window.close();
break;
default:
_game->input(event); _game->input(event);
break;
}
} }
} }

View File

@ -1,12 +1,11 @@
#include "classicgame.h" #include "classicgame.h"
#include "classicinputtype.h" #include "classicinputtype.h"
#include "classictimeline.h" #include "classictimeline.h"
#include "classicviewmanager.h" #include "spritecontainer.h"
#include "classicnote.h" #include "classicnote.h"
ClassicGame::ClassicGame() : ClassicGame::ClassicGame() :
_timeline(std::make_unique<ClassicTimeline>()), _timeline(std::make_unique<ClassicTimeline>())
_view_manager(std::make_unique<ClassicViewManager>())
{ {
_keys_to_buttons = _keys_to_buttons =
{ {
@ -52,7 +51,7 @@ ClassicGame::~ClassicGame()
void ClassicGame::run() void ClassicGame::run()
{ {
_timeline->fetchVisibleNotes(_view_manager); _timeline->fetchVisibleNotes(_sprite_container);
_timeline->run(); _timeline->run();
} }
@ -82,7 +81,7 @@ void ClassicGame::input(const sf::Event& event)
auto note = _timeline->getActiveNote(); auto note = _timeline->getActiveNote();
if (!_timeline->isExpired(note) && (*note)->state() == ClassicNote::State::FLYING) if (!_timeline->isExpired(note))
{ {
(*note)->input(ClassicInputType(timestamp, new_action)); (*note)->input(ClassicInputType(timestamp, new_action));
} }
@ -101,7 +100,7 @@ Action ClassicGame::getActionKeyReleased(Button button) const
void ClassicGame::update() void ClassicGame::update()
{ {
_timeline->update(); _timeline->update();
_timeline->fetchVisibleNotes(_view_manager); _timeline->fetchVisibleNotes(_sprite_container);
} }
void ClassicGame::draw(sf::RenderWindow& window) const void ClassicGame::draw(sf::RenderWindow& window) const

View File

@ -5,9 +5,9 @@
#include <list> #include <list>
#include "game.h" #include "game.h"
#include "classicactions.h" #include "classicactions.h"
#include "spritecontainer.h"
class ClassicTimeline; class ClassicTimeline;
class ClassicViewManager;
class ClassicGame final : public Game class ClassicGame final : public Game
{ {
@ -30,7 +30,7 @@ private:
Action getActionKeyReleased(Button button) const; Action getActionKeyReleased(Button button) const;
std::unique_ptr<ClassicTimeline> _timeline; std::unique_ptr<ClassicTimeline> _timeline;
std::unique_ptr<ClassicViewManager> _view_manager; SpriteContainer _sprite_container;
}; };
#endif // CLASSICGAME_H #endif // CLASSICGAME_H

View File

@ -9,6 +9,7 @@ ClassicNote::ClassicNote(const std::vector<microsec>& intervals, microsec perfec
_coordinates(coord), _coordinates(coord),
_evaluator(intervals, _perfect_offset), _evaluator(intervals, _perfect_offset),
_action(action), _action(action),
_state(State::NONE),
_appearance_time(0) _appearance_time(0)
{} {}
@ -26,8 +27,18 @@ static int getPt( int n1 , int n2 , float perc )
void ClassicNote::update(const microsec& music_offset) void ClassicNote::update(const microsec& music_offset)
{ {
auto update_time = music_offset - _appearance_time; switch (_state) // States will be objects
auto i = update_time / _trail_path_percent / 100; {
case State::DYING:
_sprite->update();
if (_sprite->isDead())
setState(State::DEAD);
break;
case State::FLYING:
{
auto update_time = music_offset - _appearance_time; // This all will be inside ::update
auto i = update_time / _trail_path_percent / 100; // of an animation object
int xa = getPt( 720./2. , 1280./2. , i ); int xa = getPt( 720./2. , 1280./2. , i );
int ya = getPt( 0 , 720./2. , i ); int ya = getPt( 0 , 720./2. , i );
@ -35,10 +46,12 @@ void ClassicNote::update(const microsec& music_offset)
int yb = getPt( 720./2. , _coordinates.y , i ); int yb = getPt( 720./2. , _coordinates.y , i );
_sprite->update(getPt( xa , xb , i ), getPt( ya , yb , i )); _sprite->update(getPt( xa , xb , i ), getPt( ya , yb , i ));
break;
}
if (_state == State::DYING) default:
if (_sprite->isDead()) break;
_state = State::DEAD; }
} }
void ClassicNote::draw(sf::RenderTarget& target, sf::RenderStates states) const void ClassicNote::draw(sf::RenderTarget& target, sf::RenderStates states) const
@ -51,13 +64,12 @@ auto ClassicNote::input(ClassicInputType&& input_data) -> Grade
auto grade = ClassicNote::Grade::BAD; auto grade = ClassicNote::Grade::BAD;
if (input_data == _action) if (input_data == _action)
{
grade = _evaluator.calculatePrecision(input_data.timestamp()); grade = _evaluator.calculatePrecision(input_data.timestamp());
}
setState(State::DYING);
std::cout << "User input: " << static_cast<int>(grade) << "\n"; std::cout << "User input: " << static_cast<int>(grade) << "\n";
_state = State::DYING;
_sprite->showGrade();
return grade; return grade;
} }
@ -73,6 +85,24 @@ auto ClassicNote::state() const -> State
void ClassicNote::setState(State next_state) void ClassicNote::setState(State next_state)
{ {
switch (next_state) // States will be objects
{
case State::DYING:
_sprite->pulse();
break;
case State::FLYING:
_sprite->setCoordinates(_coordinates.x, _coordinates.y, 720/2, 50);
break;
case State::NONE:
_sprite->reset();
break;
default:
break;
}
_state = next_state; _state = next_state;
} }
@ -91,7 +121,7 @@ void ClassicNote::setSprite(const std::shared_ptr<ClassicSprite>& sprite) noexce
{ {
_sprite = sprite; _sprite = sprite;
if (_sprite) if (_sprite)
_sprite->setCoordinates(_coordinates.x, _coordinates.y, 720/2, 50); setState(State::FLYING);
} }
const Coordinates& ClassicNote::getCoordinates() const noexcept const Coordinates& ClassicNote::getCoordinates() const noexcept

View File

@ -32,6 +32,8 @@ public:
enum class State enum class State
{ {
NONE,
FLYING, FLYING,
DYING, DYING,
DEAD DEAD
@ -60,7 +62,7 @@ private:
const Coordinates _coordinates; const Coordinates _coordinates;
const PrecisionEvaluator<Grade> _evaluator; const PrecisionEvaluator<Grade> _evaluator;
const Action _action; const Action _action;
State _state = State::FLYING; State _state;
std::shared_ptr<ClassicSprite> _sprite; std::shared_ptr<ClassicSprite> _sprite;
microsec _appearance_time; microsec _appearance_time;

View File

@ -16,6 +16,14 @@ void ClassicSprite::draw(sf::RenderTarget& target, sf::RenderStates states) cons
target.draw(_grade_text, states); target.draw(_grade_text, states);
} }
void ClassicSprite::reset()
{
_shape.setPosition(0, 0);
_trail.setPosition(0, 0);
_grade_text.setPosition(0, 0);
_grade_text.setFillColor(sf::Color(255, 255, 255, 0));
}
void ClassicSprite::setCoordinates(float x, float y, float trail_x, float trail_y) noexcept void ClassicSprite::setCoordinates(float x, float y, float trail_x, float trail_y) noexcept
{ {
_shape.setPosition(x, y); _shape.setPosition(x, y);
@ -26,12 +34,17 @@ void ClassicSprite::setCoordinates(float x, float y, float trail_x, float trail_
void ClassicSprite::update(float trail_x, float trail_y) noexcept void ClassicSprite::update(float trail_x, float trail_y) noexcept
{ {
_trail.setPosition(trail_x, trail_y); _trail.setPosition(trail_x, trail_y);
}
void ClassicSprite::update() noexcept
{
fade(); fade();
} }
void ClassicSprite::showGrade() void ClassicSprite::pulse()
{ {
_grade_text.setFillColor(sf::Color(255, 255, 255, 255)); _grade_text.setFillColor(sf::Color(255, 255, 255, 255));
_shape.setFillColor(sf::Color(140, 140, 140));
} }
void ClassicSprite::fade() void ClassicSprite::fade()
@ -41,13 +54,24 @@ void ClassicSprite::fade()
if (fill_color.a == 0) if (fill_color.a == 0)
return; return;
const auto new_alpha = fill_color.a - 55; auto new_alpha = fill_color.a - 55;
fill_color.a = new_alpha < 0 ? 0 : new_alpha; fill_color.a = new_alpha < 0 ? 0 : new_alpha;
_grade_text.setFillColor(fill_color); _grade_text.setFillColor(fill_color);
fill_color = _shape.getFillColor();
if (fill_color.a == 0)
return;
new_alpha = fill_color.a - 55;
fill_color.a = new_alpha < 0 ? 0 : new_alpha;
_shape.setFillColor(fill_color);
} }
bool ClassicSprite::isDead() const bool ClassicSprite::isDead() const
{ {
return _grade_text.getFillColor().a == 0; return _grade_text.getFillColor().a == 0
|| _shape.getFillColor().a == 0;
} }

View File

@ -9,10 +9,13 @@ class ClassicSprite : public Sprite, public sf::Drawable
public: public:
ClassicSprite(const sf::RectangleShape& shape, const sf::Font &font); ClassicSprite(const sf::RectangleShape& shape, const sf::Font &font);
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
virtual void reset() override;
void setCoordinates(float x, float y, float trail_x, float trail_y) noexcept; void setCoordinates(float x, float y, float trail_x, float trail_y) noexcept;
void update(float trail_x, float trail_y) noexcept; void update(float trail_x, float trail_y) noexcept;
void showGrade(); void update() noexcept;
void pulse();
void fade(); void fade();
bool isDead() const; bool isDead() const;

View File

@ -2,7 +2,8 @@
#include "classicactions.h" #include "classicactions.h"
#include "classictimeline.h" #include "classictimeline.h"
#include "classicnote.h" #include "classicnote.h"
#include "classicviewmanager.h" #include "spritecontainer.h"
#include <SFML/Graphics/RenderTarget.hpp> #include <SFML/Graphics/RenderTarget.hpp>
ClassicTimeline::ClassicTimeline() ClassicTimeline::ClassicTimeline()
@ -44,6 +45,7 @@ ClassicTimeline::ClassicTimeline()
x += 70; x += 70;
} }
expire(_first_visible_note);
expire(_last_visible_note); expire(_last_visible_note);
expire(_active_note); expire(_active_note);
_top_note = _timeline.begin(); _top_note = _timeline.begin();
@ -65,9 +67,6 @@ void ClassicTimeline::clear()
delete note; delete note;
_timeline.clear(); _timeline.clear();
expire(_top_note);
expire(_last_visible_note);
expire(_active_note);
} }
void ClassicTimeline::update() void ClassicTimeline::update()
@ -79,8 +78,12 @@ void ClassicTimeline::update()
void ClassicTimeline::checkCurrentActiveNote(const microsec &music_offset) void ClassicTimeline::checkCurrentActiveNote(const microsec &music_offset)
{ {
if (!isExpired(_active_note) if (isExpired(_active_note))
&& ((!(*_active_note)->isActive(music_offset)) || (*_active_note)->state() == ClassicNote::State::DEAD)) return;
auto note = *_active_note;
if (!note->isActive(music_offset))
{ {
expire(_active_note); expire(_active_note);
++_top_note; ++_top_note;
@ -89,16 +92,16 @@ void ClassicTimeline::checkCurrentActiveNote(const microsec &music_offset)
void ClassicTimeline::checkForNextActiveNote(const microsec &music_offset) void ClassicTimeline::checkForNextActiveNote(const microsec &music_offset)
{ {
if (isExpired(_active_note) && (*_top_note)->isActive(music_offset)) if (!isExpired(_active_note))
{ return;
std::cout << "New active note: " << music_offset << '\n';
auto top_note = *_top_note;
if (top_note->isActive(music_offset))
_active_note = _top_note; _active_note = _top_note;
}
} }
ClassicTimeline::Iterator ClassicTimeline::getActiveNote() noexcept ClassicTimeline::Iterator ClassicTimeline::getActiveNote() noexcept
{ {
update();
return _active_note; return _active_note;
} }
@ -117,68 +120,74 @@ microsec ClassicTimeline::currentMusicOffset() const
return _music.getPlayingOffset().asMicroseconds(); return _music.getPlayingOffset().asMicroseconds();
} }
void ClassicTimeline::discardExpiredNotes(const std::unique_ptr<ClassicViewManager> &view_manager)
{
if (_top_note == _timeline.begin())
return;
Iterator past_note = _top_note - 1;
std::shared_ptr<ClassicSprite> sprite = (*past_note)->sprite();
while (sprite)
{
auto state = (*past_note)->state();
if (state == ClassicNote::State::DEAD || state == ClassicNote::State::FLYING)
view_manager->resetNoteSprite(*past_note);
if (past_note == _timeline.begin())
return;
--past_note;
sprite = (*past_note)->sprite();
}
}
bool ClassicTimeline::isVisiblyClose(const Iterator &iterator, const microsec &music_offset) const bool ClassicTimeline::isVisiblyClose(const Iterator &iterator, const microsec &music_offset) const
{ {
return ((*iterator)->offset() - _visibility_offset) <= music_offset; return ((*iterator)->offset() - _visibility_offset) <= music_offset;
} }
void ClassicTimeline::fetchVisibleNotes(const std::unique_ptr<ClassicViewManager>& view_manager) void ClassicTimeline::fetchVisibleNotes(SpriteContainer& sprite_container)
{ {
const microsec music_offset = currentMusicOffset(); const microsec music_offset = currentMusicOffset();
discardExpiredNotes(view_manager); initGraphicsForNewNotes(sprite_container, music_offset);
discardGraphicsForDeadNotes(sprite_container);
}
void ClassicTimeline::initGraphicsForNewNotes(SpriteContainer& sprite_container, const microsec &music_offset)
{
Iterator note_iterator = _top_note; Iterator note_iterator = _top_note;
while (isVisiblyClose(note_iterator, music_offset)) while (isVisiblyClose(note_iterator, music_offset))
{ {
ClassicNote* note = *note_iterator; if (nothingToDraw())
if (!note->sprite()) _first_visible_note = note_iterator;
{
auto note = *note_iterator;
if (note->sprite())
continue;
note->saveAppearanceTime(music_offset); note->saveAppearanceTime(music_offset);
view_manager->initNoteSprite(note);
}
if (note->state() == ClassicNote::State::DEAD) const auto action_type = note->action();
view_manager->resetNoteSprite(note); const auto sprite = sprite_container.getSprite(action_type);
note->setSprite(sprite);
note->update(music_offset);
++note_iterator;
} }
_last_visible_note = note_iterator; _last_visible_note = note_iterator;
} }
void ClassicTimeline::drawVisibleNotes(sf::RenderWindow &window) const void ClassicTimeline::discardGraphicsForDeadNotes(SpriteContainer &sprite_container)
{ {
bool no_visible_notes = isExpired(_last_visible_note) if (nothingToDraw())
|| _top_note > _last_visible_note;
if (no_visible_notes)
return; return;
Iterator note_to_draw = _top_note; auto note_iterator = _first_visible_note;
while (note_to_draw != (_last_visible_note)) while (note_iterator != _last_visible_note)
{ {
if ((*note_to_draw)->sprite()) auto note = *note_iterator;
window.draw(*(*note_to_draw)); if (note->state() == ClassicNote::State::DEAD)
++note_to_draw; {
note->setState(ClassicNote::State::NONE);
const auto action_type = note->action();
sprite_container.resetSprite(note->sprite(), action_type);
note->setSprite(nullptr);
++_first_visible_note;
}
} }
} }
bool ClassicTimeline::nothingToDraw() const noexcept
{
return isExpired(_first_visible_note);
}
void ClassicTimeline::drawVisibleNotes(sf::RenderWindow &window) const
{
if (nothingToDraw())
return;
std::for_each(_first_visible_note, _last_visible_note,
[&window](const auto& note)
{
window.draw(*note);
});
}

View File

@ -6,7 +6,7 @@
#include <SFML/Audio/Music.hpp> #include <SFML/Audio/Music.hpp>
class ClassicNote; class ClassicNote;
class ClassicViewManager; class SpriteContainer;
class ClassicTimeline : public Timeline class ClassicTimeline : public Timeline
{ {
@ -20,7 +20,9 @@ public:
virtual microsec currentMusicOffset() const override; virtual microsec currentMusicOffset() const override;
virtual void drawVisibleNotes(sf::RenderWindow& window) const override; virtual void drawVisibleNotes(sf::RenderWindow& window) const override;
void fetchVisibleNotes(const std::unique_ptr<ClassicViewManager>& view_manager); void fetchVisibleNotes(SpriteContainer& sprite_container);
void initGraphicsForNewNotes(SpriteContainer& sprite_container, const microsec& music_offset);
void discardGraphicsForDeadNotes(SpriteContainer& sprite_container);
using Iterator = std::vector<ClassicNote*>::const_iterator; using Iterator = std::vector<ClassicNote*>::const_iterator;
@ -34,6 +36,7 @@ private:
Iterator _top_note; Iterator _top_note;
Iterator _active_note; Iterator _active_note;
Iterator _last_visible_note; Iterator _last_visible_note;
Iterator _first_visible_note;
microsec _visibility_offset; microsec _visibility_offset;
@ -41,8 +44,8 @@ private:
void checkCurrentActiveNote(const microsec &music_offset); void checkCurrentActiveNote(const microsec &music_offset);
void checkForNextActiveNote(const microsec &music_offset); void checkForNextActiveNote(const microsec &music_offset);
void discardExpiredNotes(const std::unique_ptr<ClassicViewManager>& view_manager);
bool isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const; bool isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const;
bool nothingToDraw() const noexcept;
/* Difference between top and active note is that /* Difference between top and active note is that
* top note is the note handling input right now * top note is the note handling input right now

View File

@ -1,12 +1,12 @@
#include "classicviewmanager.h" #include "spritecontainer.h"
#include "classicsprite.h" #include "classicsprite.h"
#include "classicnote.h"
#include <SFML/Graphics/RectangleShape.hpp> #include <SFML/Graphics/RectangleShape.hpp>
#include <iostream> #include <iostream>
static constexpr std::size_t RESERVED_SIZE = 20; static constexpr std::size_t RESERVED_SIZE = 20;
ClassicViewManager::ClassicViewManager() SpriteContainer::SpriteContainer()
{ {
for (auto kind_of_action : {Action::PRESS_UP, Action::PRESS_DOWN, for (auto kind_of_action : {Action::PRESS_UP, Action::PRESS_DOWN,
Action::PRESS_LEFT, Action::PRESS_RIGHT}) Action::PRESS_LEFT, Action::PRESS_RIGHT})
@ -14,10 +14,12 @@ ClassicViewManager::ClassicViewManager()
reallocatePoll(kind_of_action); reallocatePoll(kind_of_action);
} }
std::cout << "Sprite poll reallocated.\n";
_font.loadFromFile("VeraMono.ttf"); _font.loadFromFile("VeraMono.ttf");
} }
void ClassicViewManager::reallocatePoll(Action kind_of_action) void SpriteContainer::reallocatePoll(Action kind_of_action)
{ {
SpritePoll &poll = _sprite_dispatcher[kind_of_action]; SpritePoll &poll = _sprite_dispatcher[kind_of_action];
for (std::size_t i = 0; i < RESERVED_SIZE; ++i) for (std::size_t i = 0; i < RESERVED_SIZE; ++i)
@ -26,7 +28,7 @@ void ClassicViewManager::reallocatePoll(Action kind_of_action)
} }
} }
std::shared_ptr<ClassicSprite> ClassicViewManager::createSprite(Action kind_of_action) const std::shared_ptr<ClassicSprite> SpriteContainer::createSprite(Action kind_of_action) const
{ {
sf::RectangleShape sprite; sf::RectangleShape sprite;
sprite.setSize({20.f, 20.f}); sprite.setSize({20.f, 20.f});
@ -55,22 +57,22 @@ std::shared_ptr<ClassicSprite> ClassicViewManager::createSprite(Action kind_of_a
return std::make_shared<ClassicSprite>(sprite, _font); return std::make_shared<ClassicSprite>(sprite, _font);
} }
void ClassicViewManager::initNoteSprite(ClassicNote* note) std::shared_ptr<ClassicSprite> SpriteContainer::getSprite(Action action)
{ {
const auto action_type = note->action(); SpritePoll& poll = _sprite_dispatcher.at(action);
SpritePoll& poll = _sprite_dispatcher.at(action_type);
if (poll.empty()) if (poll.empty())
reallocatePoll(action_type); reallocatePoll(action);
note->setSprite(poll.top()); std::shared_ptr<ClassicSprite> sprite = poll.top();
poll.pop(); poll.pop();
std::cout << "Taking a sprite from poll.\n"; std::cout << "Taking a sprite from poll.\n";
return sprite;
} }
void ClassicViewManager::resetNoteSprite(ClassicNote* note) void SpriteContainer::resetSprite(const std::shared_ptr<ClassicSprite> &sprite, Action action)
{ {
_sprite_dispatcher[note->action()].push(note->sprite()); _sprite_dispatcher[action].push(sprite);
note->setSprite(nullptr);
std::cout << "Returning a sprite to poll.\n"; std::cout << "Returning a sprite to poll.\n";
} }

View File

@ -1,24 +1,25 @@
#ifndef CLASSICDIVAVIEWMANAGER_H #pragma once
#define CLASSICDIVAVIEWMANAGER_H
#include "timelineviewmanager.h"
#include "classicactions.h" #include "classicactions.h"
#include <memory> #include <memory>
#include <stack> #include <stack>
#include <map>
#include <SFML/Graphics/Font.hpp> #include <SFML/Graphics/Font.hpp>
class ClassicSprite; class ClassicSprite;
class ClassicNote; class ClassicNote;
class ClassicViewManager : public TimelineViewManager /* Move Sprite initialization into factory
* and make this class template */
class SpriteContainer
{ {
public: public:
explicit ClassicViewManager(); explicit SpriteContainer();
void initNoteSprite(ClassicNote *note); std::shared_ptr<ClassicSprite> getSprite(Action action);
void resetNoteSprite(ClassicNote *note); void resetSprite(const std::shared_ptr<ClassicSprite> &sprite, Action action);
private: private:
void reallocatePoll(Action kind_of_action); void reallocatePoll(Action kind_of_action);
@ -28,5 +29,3 @@ private:
std::map<Action, SpritePoll> _sprite_dispatcher; std::map<Action, SpritePoll> _sprite_dispatcher;
sf::Font _font; sf::Font _font;
}; };
#endif // CLASSICDIVAVIEWMANAGER_H