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:
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;
while (_game_window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
switch(event.type)
{
case sf::Event::Closed:
_game_window.close();
break;
_game->input(event);
default:
_game->input(event);
break;
}
}
}

View File

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

View File

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

View File

@ -9,6 +9,7 @@ ClassicNote::ClassicNote(const std::vector<microsec>& intervals, microsec perfec
_coordinates(coord),
_evaluator(intervals, _perfect_offset),
_action(action),
_state(State::NONE),
_appearance_time(0)
{}
@ -26,19 +27,31 @@ static int getPt( int n1 , int n2 , float 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->update(getPt( xa , xb , i ), getPt( ya , yb , i ));
if (_state == State::DYING)
switch (_state) // States will be objects
{
case State::DYING:
_sprite->update();
if (_sprite->isDead())
_state = State::DEAD;
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 ya = getPt( 0 , 720./2. , i );
int xb = getPt( 1280./2. , _coordinates.x , i );
int yb = getPt( 720./2. , _coordinates.y , i );
_sprite->update(getPt( xa , xb , i ), getPt( ya , yb , i ));
break;
}
default:
break;
}
}
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;
if (input_data == _action)
{
grade = _evaluator.calculatePrecision(input_data.timestamp());
}
setState(State::DYING);
std::cout << "User input: " << static_cast<int>(grade) << "\n";
_state = State::DYING;
_sprite->showGrade();
return grade;
}
@ -73,6 +85,24 @@ auto ClassicNote::state() const -> 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;
}
@ -91,7 +121,7 @@ void ClassicNote::setSprite(const std::shared_ptr<ClassicSprite>& sprite) noexce
{
_sprite = sprite;
if (_sprite)
_sprite->setCoordinates(_coordinates.x, _coordinates.y, 720/2, 50);
setState(State::FLYING);
}
const Coordinates& ClassicNote::getCoordinates() const noexcept

View File

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

View File

@ -16,6 +16,14 @@ void ClassicSprite::draw(sf::RenderTarget& target, sf::RenderStates states) cons
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
{
_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
{
_trail.setPosition(trail_x, trail_y);
}
void ClassicSprite::update() noexcept
{
fade();
}
void ClassicSprite::showGrade()
void ClassicSprite::pulse()
{
_grade_text.setFillColor(sf::Color(255, 255, 255, 255));
_shape.setFillColor(sf::Color(140, 140, 140));
}
void ClassicSprite::fade()
@ -41,13 +54,24 @@ void ClassicSprite::fade()
if (fill_color.a == 0)
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;
_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
{
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:
ClassicSprite(const sf::RectangleShape& shape, const sf::Font &font);
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 update(float trail_x, float trail_y) noexcept;
void showGrade();
void update() noexcept;
void pulse();
void fade();
bool isDead() const;

View File

@ -2,7 +2,8 @@
#include "classicactions.h"
#include "classictimeline.h"
#include "classicnote.h"
#include "classicviewmanager.h"
#include "spritecontainer.h"
#include <SFML/Graphics/RenderTarget.hpp>
ClassicTimeline::ClassicTimeline()
@ -44,6 +45,7 @@ ClassicTimeline::ClassicTimeline()
x += 70;
}
expire(_first_visible_note);
expire(_last_visible_note);
expire(_active_note);
_top_note = _timeline.begin();
@ -65,9 +67,6 @@ void ClassicTimeline::clear()
delete note;
_timeline.clear();
expire(_top_note);
expire(_last_visible_note);
expire(_active_note);
}
void ClassicTimeline::update()
@ -79,8 +78,12 @@ void ClassicTimeline::update()
void ClassicTimeline::checkCurrentActiveNote(const microsec &music_offset)
{
if (!isExpired(_active_note)
&& ((!(*_active_note)->isActive(music_offset)) || (*_active_note)->state() == ClassicNote::State::DEAD))
if (isExpired(_active_note))
return;
auto note = *_active_note;
if (!note->isActive(music_offset))
{
expire(_active_note);
++_top_note;
@ -89,16 +92,16 @@ void ClassicTimeline::checkCurrentActiveNote(const microsec &music_offset)
void ClassicTimeline::checkForNextActiveNote(const microsec &music_offset)
{
if (isExpired(_active_note) && (*_top_note)->isActive(music_offset))
{
std::cout << "New active note: " << music_offset << '\n';
if (!isExpired(_active_note))
return;
auto top_note = *_top_note;
if (top_note->isActive(music_offset))
_active_note = _top_note;
}
}
ClassicTimeline::Iterator ClassicTimeline::getActiveNote() noexcept
{
update();
return _active_note;
}
@ -117,68 +120,74 @@ microsec ClassicTimeline::currentMusicOffset() const
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
{
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();
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;
while (isVisiblyClose(note_iterator, music_offset))
{
ClassicNote* note = *note_iterator;
if (!note->sprite())
{
note->saveAppearanceTime(music_offset);
view_manager->initNoteSprite(note);
}
if (nothingToDraw())
_first_visible_note = note_iterator;
if (note->state() == ClassicNote::State::DEAD)
view_manager->resetNoteSprite(note);
auto note = *note_iterator;
note->update(music_offset);
++note_iterator;
if (note->sprite())
continue;
note->saveAppearanceTime(music_offset);
const auto action_type = note->action();
const auto sprite = sprite_container.getSprite(action_type);
note->setSprite(sprite);
}
_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)
|| _top_note > _last_visible_note;
if (no_visible_notes)
if (nothingToDraw())
return;
Iterator note_to_draw = _top_note;
while (note_to_draw != (_last_visible_note))
auto note_iterator = _first_visible_note;
while (note_iterator != _last_visible_note)
{
if ((*note_to_draw)->sprite())
window.draw(*(*note_to_draw));
++note_to_draw;
auto note = *note_iterator;
if (note->state() == ClassicNote::State::DEAD)
{
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>
class ClassicNote;
class ClassicViewManager;
class SpriteContainer;
class ClassicTimeline : public Timeline
{
@ -20,7 +20,9 @@ public:
virtual microsec currentMusicOffset() 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;
@ -34,6 +36,7 @@ private:
Iterator _top_note;
Iterator _active_note;
Iterator _last_visible_note;
Iterator _first_visible_note;
microsec _visibility_offset;
@ -41,8 +44,8 @@ private:
void checkCurrentActiveNote(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 nothingToDraw() const noexcept;
/* Difference between top and active note is that
* 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 "classicnote.h"
#include <SFML/Graphics/RectangleShape.hpp>
#include <iostream>
static constexpr std::size_t RESERVED_SIZE = 20;
ClassicViewManager::ClassicViewManager()
SpriteContainer::SpriteContainer()
{
for (auto kind_of_action : {Action::PRESS_UP, Action::PRESS_DOWN,
Action::PRESS_LEFT, Action::PRESS_RIGHT})
@ -14,10 +14,12 @@ ClassicViewManager::ClassicViewManager()
reallocatePoll(kind_of_action);
}
std::cout << "Sprite poll reallocated.\n";
_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];
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;
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);
}
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_type);
SpritePoll& poll = _sprite_dispatcher.at(action);
if (poll.empty())
reallocatePoll(action_type);
reallocatePoll(action);
note->setSprite(poll.top());
std::shared_ptr<ClassicSprite> sprite = poll.top();
poll.pop();
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());
note->setSprite(nullptr);
_sprite_dispatcher[action].push(sprite);
std::cout << "Returning a sprite to poll.\n";
}

View File

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