Encapsulate note switches into state objects

This commit is contained in:
NaiJi ✨ 2021-06-21 22:10:50 +03:00
parent 89a80992cb
commit d9788b31b8
8 changed files with 118 additions and 72 deletions

View File

@ -5,7 +5,13 @@ project(project-kyoku LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g")
file(GLOB SOURCES "src/*.cpp" "src/classicgame/*") set(CMAKE_THREAD_LIBS_INIT "-lpthread")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
set(CMAKE_HAVE_THREADS_LIBRARY 1)
set(CMAKE_USE_WIN32_THREADS_INIT 0)
set(CMAKE_USE_PTHREADS_INIT 1)
set(THREADS_PREFER_PTHREAD_FLAG ON)
file(GLOB SOURCES "src/*.cpp" "src/classicgame/*.*" "src/classicgame/classicnotestate/*")
# STATIC # # STATIC #
# You need to build SFML from sources with cmake # You need to build SFML from sources with cmake

View File

@ -13,11 +13,14 @@ public:
_perfect_offset(perfect_offset) {} _perfect_offset(perfect_offset) {}
virtual ~Note() = default; virtual ~Note() = default;
virtual bool isActive(const microsec& music_offset) const = 0; virtual bool isActive() const = 0;
virtual void update(const microsec& music_offset) = 0; virtual void update(const microsec& music_offset) = 0;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const = 0; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const = 0;
virtual microsec offset() const virtual void putToGame(const microsec &offset) = 0;
virtual bool isExpired() const = 0;
microsec offset() const
{ {
return _perfect_offset; return _perfect_offset;
} }

View File

@ -9,7 +9,7 @@
const sf::Time TIME_PER_FRAME = sf::seconds(1.f / 90.f); const sf::Time TIME_PER_FRAME = sf::seconds(1.f / 90.f);
Application::Application() : Application::Application() :
_game_window({1280, 720}, "Test", sf::Style::Fullscreen ), _game_window({1280, 720}, "Test", sf::Style::Default ),
_game(std::make_unique<ClassicGame>()) _game(std::make_unique<ClassicGame>())
{ {
_game_window.setFramerateLimit(60); _game_window.setFramerateLimit(60);

View File

@ -9,14 +9,27 @@ 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),
_last_offset(0) _current_state(ClassicNoteState::NONE)
{} {}
bool ClassicNote::isActive(const microsec& music_offset) const bool ClassicNote::isActive() const
{ {
return _evaluator.isActive(music_offset); return _states.at(_current_state)->value() == ClassicNoteState::ACTIVE;
}
void ClassicNote::putToGame(const microsec &offset)
{
_appearance_time = offset; // To animation manager
_trail_path_percent = ((_perfect_offset - _appearance_time) * 0.01);
_current_state = ClassicNoteState::FLYING;
_states.at(_current_state)->onEntering(this);
}
bool ClassicNote::isExpired() const
{
return _states.at(_current_state)->value() == ClassicNoteState::NONE;
} }
static int getPt( float n1 , float n2 , float perc ) static int getPt( float n1 , float n2 , float perc )
@ -28,29 +41,20 @@ static int getPt( float n1 , float n2 , float perc )
void ClassicNote::update(const microsec& music_offset) void ClassicNote::update(const microsec& music_offset)
{ {
switch (_state) // States will be objects _states.at(_current_state)->update(this, music_offset);
/*switch (_state) // States will be objects
{ {
case State::DYING: case State::DYING:
_sprite->update(); _sprite->update();
if (_sprite->isDead()) if (_sprite->isDead())
setState(State::DEAD); setState(State::NONE);
break; break;
case State::FLYING: case State::FLYING:
{ {
float i; float i;
if (music_offset != _last_offset) auto update_time = music_offset - _appearance_time; // This all will be inside ::update
{ i = update_time / _trail_path_percent * 0.01; // of an animation object
auto update_time = music_offset - _appearance_time; // This all will be inside ::update
i = update_time / _trail_path_percent * 0.01; // of an animation object
}
else
{
auto update_time = music_offset + 10000 - _appearance_time;
i = update_time / _trail_path_percent * 0.01;
}
_last_offset = music_offset;
float xa = getPt( _coordinates.x + 20. , _coordinates.x + 90. , i ); float xa = getPt( _coordinates.x + 20. , _coordinates.x + 90. , i );
float ya = getPt( _coordinates.y - 600. , _coordinates.y - 150. , i ); float ya = getPt( _coordinates.y - 600. , _coordinates.y - 150. , i );
@ -59,13 +63,40 @@ void ClassicNote::update(const microsec& music_offset)
_sprite->update(getPt( xa , xb , i ), getPt( ya , yb , i )); _sprite->update(getPt( xa , xb , i ), getPt( ya , yb , i ));
if (i >= 1) if (i >= 1)
{
_sprite->trailFade(); _sprite->trailFade();
}
if (_evaluator.isActive(music_offset))
setState(State::ACTIVE);
break;
}
case State::ACTIVE:
{
float i;
auto update_time = music_offset - _appearance_time; // This all will be inside ::update
i = update_time / _trail_path_percent * 0.01; // of an animation object
float xa = getPt( _coordinates.x + 20. , _coordinates.x + 90. , i );
float ya = getPt( _coordinates.y - 600. , _coordinates.y - 150. , i );
float xb = getPt( _coordinates.x + 90. , _coordinates.x , i );
float yb = getPt( _coordinates.y - 150. , _coordinates.y , i );
_sprite->update(getPt( xa , xb , i ), getPt( ya , yb , i ));
if (i >= 1)
{
_sprite->trailFade();
}
if (!_evaluator.isActive(music_offset))
setState(State::DYING);
break; break;
} }
default: default:
break; break;
} } */
} }
void ClassicNote::draw(sf::RenderTarget& target, sf::RenderStates states) const void ClassicNote::draw(sf::RenderTarget& target, sf::RenderStates states) const
@ -80,7 +111,8 @@ auto ClassicNote::input(ClassicInputType&& input_data) -> Grade
if (input_data == _action) if (input_data == _action)
grade = _evaluator.calculatePrecision(input_data.timestamp()); grade = _evaluator.calculatePrecision(input_data.timestamp());
setState(State::DYING); _current_state = ClassicNoteState::DYING;
_states.at(_current_state)->onEntering(this);
std::cout << "User input: " << static_cast<int>(grade) << "\n"; std::cout << "User input: " << static_cast<int>(grade) << "\n";
@ -92,12 +124,7 @@ Action ClassicNote::action() const
return _action; return _action;
} }
auto ClassicNote::state() const -> State /*void ClassicNote::setState(State next_state)
{
return _state;
}
void ClassicNote::setState(State next_state)
{ {
switch (next_state) // States will be objects switch (next_state) // States will be objects
{ {
@ -118,24 +145,16 @@ void ClassicNote::setState(State next_state)
} }
_state = next_state; _state = next_state;
} } */
std::shared_ptr<ClassicSprite> ClassicNote::sprite() const noexcept std::shared_ptr<ClassicSprite> ClassicNote::sprite() const noexcept
{ {
return _sprite; 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<ClassicSprite>& sprite) noexcept void ClassicNote::setSprite(const std::shared_ptr<ClassicSprite>& sprite) noexcept
{ {
_sprite = sprite; _sprite = sprite;
if (_sprite)
setState(State::FLYING);
} }
const Coordinates& ClassicNote::getCoordinates() const noexcept const Coordinates& ClassicNote::getCoordinates() const noexcept

View File

@ -3,8 +3,10 @@
#include "note.h" #include "note.h"
#include "precisionevaluator.h" #include "precisionevaluator.h"
#include "classicinputtype.h" #include "classicinputtype.h"
#include "classicnotestate/classicnotestate.h"
#include <memory> #include <memory>
#include <array>
struct Coordinates struct Coordinates
{ {
@ -30,28 +32,19 @@ public:
BAD BAD
}; };
enum class State
{
NONE,
FLYING,
DYING,
DEAD
};
explicit ClassicNote(const std::vector<microsec>& intervals, microsec perfect_offset, explicit ClassicNote(const std::vector<microsec>& intervals, microsec perfect_offset,
Action action, const Coordinates& coord); Action action, const Coordinates& coord);
virtual ~ClassicNote() = default; virtual ~ClassicNote() = default;
virtual bool isActive(const microsec& music_offset) const override; virtual bool isActive() const override;
virtual void update(const microsec &music_offset) override; virtual void update(const microsec &music_offset) override;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
virtual void putToGame(const microsec &offset) override;
virtual bool isExpired() const override;
Grade input(ClassicInputType&& input_data); Grade input(ClassicInputType&& input_data);
Action action() const; Action action() const;
State state() const;
void setState(State next_state);
std::shared_ptr<ClassicSprite> sprite() const noexcept; std::shared_ptr<ClassicSprite> sprite() const noexcept;
void saveAppearanceTime(const microsec& offset); void saveAppearanceTime(const microsec& offset);
@ -62,10 +55,11 @@ private:
const Coordinates _coordinates; const Coordinates _coordinates;
const PrecisionEvaluator<Grade> _evaluator; const PrecisionEvaluator<Grade> _evaluator;
const Action _action; const Action _action;
State _state;
std::shared_ptr<ClassicSprite> _sprite; std::shared_ptr<ClassicSprite> _sprite;
microsec _appearance_time; microsec _appearance_time;
microsec _last_offset;
float _trail_path_percent; //100% for sprite falling trajectory float _trail_path_percent; //100% for sprite falling trajectory
std::array<std::shared_ptr<ClassicNoteState>, ClassicNoteState::COUNT> _states;
ClassicNoteState::Value _current_state;
}; };

View File

@ -0,0 +1,29 @@
#ifndef CLASSICNOTESTATE_H
#define CLASSICNOTESTATE_H
#include <SFML/System/Clock.hpp>
using microsec = sf::Int64;
class ClassicNote;
class ClassicNoteState
{
public:
enum Value
{
NONE,
DYING,
FLYING,
ACTIVE,
COUNT
};
virtual Value value() const = 0;
virtual Value update(const ClassicNote* note, const microsec& offset) = 0;
virtual void onEntering(const ClassicNote* note) = 0;
};
#endif // CLASSICNOTESTATE_H

View File

@ -18,12 +18,10 @@ ClassicTimeline::ClassicTimeline()
_music.openFromFile(song_filename); _music.openFromFile(song_filename);
_music.setVolume(10); _music.setVolume(10);
_timeline.reserve(1000);
microsec starting_beat_offset = 352162; microsec starting_beat_offset = 352162;
int amount_of_beats = 209; int amount_of_beats = 209;
microsec interval = 1412162; microsec interval = 1412162;
microsec tempo_interval = interval / 4; microsec tempo_interval = interval / 2;
microsec note_input_offset = 412162 / 3; microsec note_input_offset = 412162 / 3;
microsec bpm_iterator = starting_beat_offset; microsec bpm_iterator = starting_beat_offset;
microsec bpm_end = starting_beat_offset + (interval * amount_of_beats); microsec bpm_end = starting_beat_offset + (interval * amount_of_beats);
@ -71,34 +69,32 @@ void ClassicTimeline::clear()
void ClassicTimeline::update() void ClassicTimeline::update()
{ {
const microsec& offset = currentMusicOffset(); checkCurrentActiveNote();
checkCurrentActiveNote(offset); checkForNextActiveNote();
checkForNextActiveNote(offset); updateVisibleSprites(currentMusicOffset());
updateVisibleSprites(offset);
} }
void ClassicTimeline::checkCurrentActiveNote(const microsec& music_offset) void ClassicTimeline::checkCurrentActiveNote()
{ {
if (isExpired(_active_note)) if (isExpired(_active_note))
return; return;
auto note = *_active_note; auto note = *_active_note;
if (note->state() != ClassicNote::State::FLYING || !note->isActive(music_offset)) if (!note->isActive())
{ {
note->setState(ClassicNote::State::DYING);
expire(_active_note); expire(_active_note);
++_top_note; ++_top_note;
} }
} }
void ClassicTimeline::checkForNextActiveNote(const microsec& music_offset) void ClassicTimeline::checkForNextActiveNote()
{ {
if (!isExpired(_active_note)) if (!isExpired(_active_note))
return; return;
auto top_note = *_top_note; auto top_note = *_top_note;
if (top_note->isActive(music_offset)) if (top_note->isActive())
_active_note = _top_note; _active_note = _top_note;
} }
@ -158,8 +154,8 @@ void ClassicTimeline::initGraphicsForNewNotes(const std::unique_ptr<ClassicGraph
if (!note->sprite()) if (!note->sprite())
{ {
note->saveAppearanceTime(music_offset);
graphics_manager->initSprite(note); graphics_manager->initSprite(note);
note->putToGame(music_offset);
} }
++note_iterator; ++note_iterator;
@ -177,9 +173,8 @@ void ClassicTimeline::discardGraphicsForDeadNotes(const std::unique_ptr<ClassicG
while (note_iterator != _last_visible_note) while (note_iterator != _last_visible_note)
{ {
auto note = *note_iterator; auto note = *note_iterator;
if (note->state() == ClassicNote::State::DEAD) if (note->isExpired())
{ {
note->setState(ClassicNote::State::NONE);
graphics_manager->resetSprite(note); graphics_manager->resetSprite(note);
++_first_visible_note; ++_first_visible_note;

View File

@ -28,7 +28,7 @@ public:
Iterator getActiveNote() noexcept; Iterator getActiveNote() noexcept;
inline bool isExpired(const Iterator& iterator) const; bool isExpired(const Iterator& iterator) const;
inline void expire(Iterator& iterator); inline void expire(Iterator& iterator);
private: private:
@ -39,8 +39,8 @@ private:
sf::Music _music; sf::Music _music;
void updateVisibleSprites(const microsec& music_offset); void updateVisibleSprites(const microsec& music_offset);
void checkCurrentActiveNote(const microsec& music_offset); void checkCurrentActiveNote();
void checkForNextActiveNote(const microsec& music_offset); void checkForNextActiveNote();
bool isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const; bool isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const;
inline bool nothingToDraw() const noexcept; inline bool nothingToDraw() const noexcept;