forked from NaiJi/project-kyoku
Encapsulate note switches into state objects
This commit is contained in:
parent
89a80992cb
commit
d9788b31b8
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue