diff --git a/application.cpp b/application.cpp index 269cb64..c9b6e76 100644 --- a/application.cpp +++ b/application.cpp @@ -1,81 +1,60 @@ #include "application.h" #include #include -#include const sf::Time TIME_PER_FRAME = sf::seconds(1.f / 60.f); Application::Application() : - game_window({1280, 720}, "Test") + _game_window({1280, 720}, "Test"), + _debug(true) { - float x = game_window.getSize().x; - float y = game_window.getSize().y; - pulse_mask.setSize({x, y}); - pulse_mask.setOrigin(0.f, 0.f); - pulse_mask.setFillColor(sf::Color(255, 0, 0, 0)); - pulse_mask_green.setSize({x, y}); - pulse_mask_green.setOrigin(0.f, 0.f); - pulse_mask_green.setFillColor(sf::Color(0, 255, 0, 0)); - font.loadFromFile("VeraMono.ttf"); - font2.loadFromFile("VeraMono.ttf"); - text.setFont(font); - text.setPosition(60, 60); - text.setFillColor(sf::Color(255, 255, 255)); - text.setCharacterSize(25); - - grade.setFont(font2); - grade.setPosition(100, 100); - grade.setFillColor(sf::Color(255, 255, 255, 0)); - grade.setCharacterSize(35); + _font.loadFromFile("VeraMono.ttf"); + _grade.setFont(_font); + _grade.setPosition(160, 160); + _grade.setFillColor(sf::Color(255, 255, 255, 0)); + _grade.setCharacterSize(35); } void Application::run() { - game_window.display(); - sf::Int64 iter = 9000 + (1412162 * 25); - while (iter > 9000) + // BPM of METEOR is 170. + // Length is 1:14 + // I calculated that the time between beats is about 1412162 microseconds + sf::Int64 iter = 1412162 * 25; + while (iter > 0) { Note note(iter, iter + 412162); - timeline.push(note); + _timeline.push(note); iter -= 1412162; } + // // // // // // // // + _music.openFromFile("/home/naiji/METEOR.flac"); + _music.play(); + _music.setVolume(5); + + _game_window.display(); + + startGameLoop(); +} + + +static bool isOneFramePassed(const sf::Time& time_since_last_update) +{ + return time_since_last_update >= TIME_PER_FRAME; +} + +void Application::startGameLoop() +{ sf::Clock timer; sf::Time time_since_last_update = sf::Time::Zero; - music.openFromFile("/home/naiji/METEOR.flac"); - music.play(); - while (game_window.isOpen()) + + while (_game_window.isOpen()) { - sf::Event event; - while (game_window.pollEvent(event)) - { - if (event.type == sf::Event::Closed) - game_window.close(); - - if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Z && !timeline.empty()) - { - const auto current_note = timeline.top(); - const auto grade_result = current_note.onTap(Note::Arrow::UP, music.getPlayingOffset().asMicroseconds()); - pulse_mask.setFillColor(sf::Color(255, 0, 0, 255)); - switch (grade_result.rating) - { - case (NoteGrade::Rating::BAD): - grade.setString("BAD"); - grade.setFillColor(sf::Color(255, 255, 255, 255)); - break; - - case (NoteGrade::Rating::GREAT): - grade.setString("GREAT"); - grade.setFillColor(sf::Color(255, 255, 0, 255)); - break; - } - - } - - } + input(); time_since_last_update += timer.restart(); - if (time_since_last_update >= TIME_PER_FRAME) + if (isOneFramePassed(time_since_last_update)) { time_since_last_update -= TIME_PER_FRAME; update(); @@ -84,42 +63,137 @@ void Application::run() } } +static sf::Text makeGradeString(const NoteGrade::Rating& rating) +{ + sf::Text ret; + switch (rating) + { + case (NoteGrade::Rating::BAD): + ret.setString("BAD"); + ret.setFillColor(sf::Color(255, 255, 255, 255)); + break; + + case (NoteGrade::Rating::GREAT): + ret.setString("GREAT"); + ret.setFillColor(sf::Color(255, 255, 0, 255)); + break; + + case (NoteGrade::Rating::WRONG): + ret.setString("WRONG"); + ret.setFillColor(sf::Color(120, 120, 120, 255)); + break; + + case (NoteGrade::Rating::GOOD): + ret.setString("GOOD"); + ret.setFillColor(sf::Color(255, 100, 120, 255)); + break; + } + + return ret; +} + +void Application::input() +{ + sf::Event event; + while (_game_window.pollEvent(event)) + { + switch (event.type) + { + default: + break; + + case (sf::Event::Closed): + _game_window.close(); + break; + + case (sf::Event::KeyPressed): + onKeyPressed(event.key.code); + break; + } + } +} + +static Note::Arrow keyToArrow(const sf::Keyboard::Key &key) +{ + switch (key) + { + case sf::Keyboard::A: + case sf::Keyboard::Left: + case sf::Keyboard::Num4: + return Note::Arrow::LEFT; + + case sf::Keyboard::W: + case sf::Keyboard::Up: + case sf::Keyboard::Num8: + return Note::Arrow::UP; + + case sf::Keyboard::D: + case sf::Keyboard::Right: + case sf::Keyboard::Num6: + return Note::Arrow::RIGHT; + + case sf::Keyboard::S: + case sf::Keyboard::Down: + case sf::Keyboard::Num2: + return Note::Arrow::DOWN; + + default: + return Note::Arrow::NONE; + } +} + +void Application::onKeyPressed(const sf::Keyboard::Key &key) +{ + if (key == sf::Keyboard::D) + { + _debug.toggle(); + return; + } + + const auto arrow = keyToArrow(key); + + if (arrow != Note::Arrow::NONE) + { + _debug.onUserTap(); + + if (!_timeline.empty()) + { + const auto current_note = _timeline.top(); + const auto grade_result = current_note.onTap(arrow, _music.getPlayingOffset().asMicroseconds()); + _grade = makeGradeString(grade_result.rating); + } + } +} + void Application::update() { - if (!timeline.empty() && timeline.top().deathOffset() <= music.getPlayingOffset().asMicroseconds()) + const auto microseconds = _music.getPlayingOffset().asMicroseconds(); + + if (!_timeline.empty() && _timeline.top().offset() <= microseconds) { - timeline.pop(); - pulse_mask_green.setFillColor(sf::Color(0, 255, 0, 255)); + _debug.onBeat(); } - text.setString(std::to_string(music.getPlayingOffset().asSeconds())); - - if (pulse_mask.getFillColor().a > 0) + if (!_timeline.empty() && _timeline.top().deathOffset() <= microseconds) { - const auto alpha = pulse_mask.getFillColor().a - 25; - pulse_mask.setFillColor(sf::Color(255, 0, 0, alpha < 0 ? 0 : alpha)); + _timeline.pop(); + _debug.onDeath(); } - if (pulse_mask_green.getFillColor().a > 0) - { - const auto alpha = pulse_mask_green.getFillColor().a - 25; - pulse_mask_green.setFillColor(sf::Color(0, 255, 0, alpha < 0 ? 0 : alpha)); - } + _debug.update(microseconds); - if (grade.getFillColor().a > 0) + if (_grade.getFillColor().a > 0) { - const auto alpha = grade.getFillColor().a - 20; - grade.setFillColor(sf::Color(255, 255, 255, alpha < 0 ? 0 : alpha)); + const auto alpha = _grade.getFillColor().a - 20; + _grade.setFillColor(sf::Color(255, 255, 255, alpha < 0 ? 0 : alpha)); } } void Application::draw() { - game_window.clear(); - game_window.draw(pulse_mask); - game_window.draw(pulse_mask_green); - game_window.draw(text); - game_window.draw(grade); - game_window.display(); + _game_window.clear(); + _debug.drawOn(_game_window); + _game_window.draw(_grade); + _game_window.display(); } diff --git a/application.h b/application.h index c937a94..3e67278 100644 --- a/application.h +++ b/application.h @@ -1,15 +1,13 @@ #ifndef APPLICATION_H #define APPLICATION_H -#include #include #include -#include -#include -#include +#include #include +#include "debughelper.h" #include "note.h" class Application @@ -17,22 +15,24 @@ class Application public: Application(); void run(); + void input(); void update(); void draw(); private: - sf::RenderWindow game_window; - sf::Music music; - sf::RectangleShape pulse_mask; - sf::RectangleShape pulse_mask_green; + sf::RenderWindow _game_window; + sf::Music _music; - std::stack timeline; - sf::Int64 time_since_last_tick; - sf::Int64 last_stamp; - sf::Font font; - sf::Font font2; - sf::Text text; - sf::Text grade; + std::stack _timeline; + sf::Int64 _time_since_last_tick; + sf::Int64 _last_stamp; + sf::Font _font; + sf::Text _grade; + + DebugHelper _debug; + + void startGameLoop(); + void onKeyPressed(const sf::Keyboard::Key& key); }; #endif // APPLICATION_H diff --git a/debughelper.cpp b/debughelper.cpp new file mode 100644 index 0000000..55a8077 --- /dev/null +++ b/debughelper.cpp @@ -0,0 +1,84 @@ +#include "debughelper.h" + +DebugHelper::DebugHelper(bool init) : + _toggled(init) +{ + _bpm_pulse.setSize({460, 360}); + _bpm_pulse.setOrigin(0.f, 0.f); + _bpm_pulse.setFillColor(sf::Color(255, 0, 0, 0)); + _tap_pulse.setSize({460, 360}); + _tap_pulse.move(460.f, 0.f); + _tap_pulse.setFillColor(sf::Color(0, 255, 0, 0)); + _death_pulse.setSize({460, 360}); + _death_pulse.move(460.f, 360.f); + _death_pulse.setFillColor(sf::Color(0, 100, 255, 0)); + + _font.loadFromFile("VeraMono.ttf"); + + _time_print.setFont(_font); + _time_print.setPosition(60, 60); + _time_print.setFillColor(sf::Color(255, 255, 255)); + _time_print.setCharacterSize(25); +} + +void DebugHelper::toggle() +{ + _toggled = !_toggled; +} + +static bool isVisible(const sf::Shape* shape) +{ + return shape->getFillColor().a > 0; +} + +void DebugHelper::update(const sf::Int64 µseconds) +{ + _time_print.setString(std::to_string(microseconds)); + + if (isVisible(&_bpm_pulse)) + { + const auto new_alpha = _bpm_pulse.getFillColor().a - 25; + _bpm_pulse.setFillColor(sf::Color(255, 0, 0, (new_alpha < 0) ? 0 : new_alpha)); + } + + if (isVisible(&_tap_pulse)) + { + const auto new_alpha = _tap_pulse.getFillColor().a - 25; + _tap_pulse.setFillColor(sf::Color(0, 255, 0, (new_alpha < 0) ? 0 : new_alpha)); + } + + if (isVisible(&_death_pulse)) + { + const auto new_alpha = _death_pulse.getFillColor().a - 25; + _death_pulse.setFillColor(sf::Color(0, 100, 255, (new_alpha < 0) ? 0 : new_alpha)); + } +} + +void DebugHelper::drawOn(sf::RenderWindow &game_window) const +{ + if (_toggled) + { + game_window.draw(_bpm_pulse); + game_window.draw(_tap_pulse); + game_window.draw(_death_pulse); + game_window.draw(_time_print); + } +} + +void DebugHelper::onUserTap() +{ + const sf::Uint8 alpha = 255; + _tap_pulse.setFillColor(sf::Color(255, 0, 0, alpha)); +} + +void DebugHelper::onBeat() +{ + const sf::Uint8 alpha = 255; + _bpm_pulse.setFillColor(sf::Color(0, 255, 0, alpha)); +} + +void DebugHelper::onDeath() +{ + const sf::Uint8 alpha = 255; + _death_pulse.setFillColor(sf::Color(0, 100, 255, alpha)); +} diff --git a/debughelper.h b/debughelper.h new file mode 100644 index 0000000..348105f --- /dev/null +++ b/debughelper.h @@ -0,0 +1,30 @@ +#ifndef DEBUGHELPER_H +#define DEBUGHELPER_H + +#include +#include +#include +#include + +class DebugHelper +{ +public: + DebugHelper(bool init = true); + + void toggle(); + void update(const sf::Int64& microseconds); + void drawOn(sf::RenderWindow &game_window) const; + void onUserTap(); + void onBeat(); + void onDeath(); + +private: + bool _toggled; + sf::RectangleShape _bpm_pulse; + sf::RectangleShape _tap_pulse; + sf::RectangleShape _death_pulse; + sf::Font _font; + sf::Text _time_print; +}; + +#endif // DEBUGHELPER_H diff --git a/note.cpp b/note.cpp index 3f97888..ce865b0 100644 --- a/note.cpp +++ b/note.cpp @@ -7,6 +7,11 @@ Note::Note(microsec offset, microsec death_offset, Note::Arrow type) : _type(type) {} +microsec Note::offset() const noexcept +{ + return _offset; +} + microsec Note::deathOffset() const noexcept { return _death_offset; @@ -24,10 +29,10 @@ NoteGrade Note::onTap(Arrow arrow_type, microsec tap_time_stamp) const NoteGrade Note::calculatePrecision(microsec odds) const { NoteGrade ret; - if (odds < 500000) + if (odds < 412162) { ret.score = 50; - ret.rating = NoteGrade::Rating::GREAT; + ret.rating = NoteGrade::Rating::GREAT; } return ret; diff --git a/note.h b/note.h index 7844cad..0f24dcc 100644 --- a/note.h +++ b/note.h @@ -25,15 +25,17 @@ public: UP, RIGHT, DOWN, - LEFT + LEFT, + + NONE }; Note(microsec offset, microsec death_offset, Note::Arrow type = Note::Arrow::UP); NoteGrade onTap(Arrow arrow_type, microsec tap_time_stamp) const; + microsec offset() const noexcept; microsec deathOffset() const noexcept; - private: microsec _offset; microsec _death_offset;