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_REQUIRED ON)
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 #
# You need to build SFML from sources with cmake

View File

@ -13,11 +13,14 @@ public:
_perfect_offset(perfect_offset) {}
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 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;
}

View File

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

View File

@ -9,14 +9,27 @@ ClassicNote::ClassicNote(const std::vector<microsec>& intervals, microsec perfec
_coordinates(coord),
_evaluator(intervals, _perfect_offset),
_action(action),
_state(State::NONE),
_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 )
@ -28,29 +41,20 @@ static int getPt( float n1 , float n2 , float perc )
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:
_sprite->update();
if (_sprite->isDead())
setState(State::DEAD);
setState(State::NONE);
break;
case State::FLYING:
{
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
}
else
{
auto update_time = music_offset + 10000 - _appearance_time;
i = update_time / _trail_path_percent * 0.01;
}
_last_offset = music_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
float xa = getPt( _coordinates.x + 20. , _coordinates.x + 90. , 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 ));
if (i >= 1)
{
_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;
}
default:
break;
}
} */
}
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)
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";
@ -92,12 +124,7 @@ Action ClassicNote::action() const
return _action;
}
auto ClassicNote::state() const -> State
{
return _state;
}
void ClassicNote::setState(State next_state)
/*void ClassicNote::setState(State next_state)
{
switch (next_state) // States will be objects
{
@ -118,24 +145,16 @@ void ClassicNote::setState(State next_state)
}
_state = next_state;
}
} */
std::shared_ptr<ClassicSprite> 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<ClassicSprite>& sprite) noexcept
{
_sprite = sprite;
if (_sprite)
setState(State::FLYING);
}
const Coordinates& ClassicNote::getCoordinates() const noexcept

View File

@ -3,8 +3,10 @@
#include "note.h"
#include "precisionevaluator.h"
#include "classicinputtype.h"
#include "classicnotestate/classicnotestate.h"
#include <memory>
#include <array>
struct Coordinates
{
@ -30,28 +32,19 @@ public:
BAD
};
enum class State
{
NONE,
FLYING,
DYING,
DEAD
};
explicit ClassicNote(const std::vector<microsec>& intervals, microsec perfect_offset,
Action action, const Coordinates& coord);
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 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);
Action action() const;
State state() const;
void setState(State next_state);
std::shared_ptr<ClassicSprite> sprite() const noexcept;
void saveAppearanceTime(const microsec& offset);
@ -62,10 +55,11 @@ private:
const Coordinates _coordinates;
const PrecisionEvaluator<Grade> _evaluator;
const Action _action;
State _state;
std::shared_ptr<ClassicSprite> _sprite;
microsec _appearance_time;
microsec _last_offset;
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.setVolume(10);
_timeline.reserve(1000);
microsec starting_beat_offset = 352162;
int amount_of_beats = 209;
microsec interval = 1412162;
microsec tempo_interval = interval / 4;
microsec tempo_interval = interval / 2;
microsec note_input_offset = 412162 / 3;
microsec bpm_iterator = starting_beat_offset;
microsec bpm_end = starting_beat_offset + (interval * amount_of_beats);
@ -71,34 +69,32 @@ void ClassicTimeline::clear()
void ClassicTimeline::update()
{
const microsec& offset = currentMusicOffset();
checkCurrentActiveNote(offset);
checkForNextActiveNote(offset);
updateVisibleSprites(offset);
checkCurrentActiveNote();
checkForNextActiveNote();
updateVisibleSprites(currentMusicOffset());
}
void ClassicTimeline::checkCurrentActiveNote(const microsec& music_offset)
void ClassicTimeline::checkCurrentActiveNote()
{
if (isExpired(_active_note))
return;
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);
++_top_note;
}
}
void ClassicTimeline::checkForNextActiveNote(const microsec& music_offset)
void ClassicTimeline::checkForNextActiveNote()
{
if (!isExpired(_active_note))
return;
auto top_note = *_top_note;
if (top_note->isActive(music_offset))
if (top_note->isActive())
_active_note = _top_note;
}
@ -158,8 +154,8 @@ void ClassicTimeline::initGraphicsForNewNotes(const std::unique_ptr<ClassicGraph
if (!note->sprite())
{
note->saveAppearanceTime(music_offset);
graphics_manager->initSprite(note);
note->putToGame(music_offset);
}
++note_iterator;
@ -177,9 +173,8 @@ void ClassicTimeline::discardGraphicsForDeadNotes(const std::unique_ptr<ClassicG
while (note_iterator != _last_visible_note)
{
auto note = *note_iterator;
if (note->state() == ClassicNote::State::DEAD)
if (note->isExpired())
{
note->setState(ClassicNote::State::NONE);
graphics_manager->resetSprite(note);
++_first_visible_note;

View File

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