Add debug helper and clean init code

This commit is contained in:
NaiJi ✨ 2021-04-05 17:17:57 +03:00
parent bf8e6be88f
commit e37fb7b539
6 changed files with 292 additions and 97 deletions

View File

@ -1,81 +1,60 @@
#include "application.h" #include "application.h"
#include <SFML/Graphics/Color.hpp> #include <SFML/Graphics/Color.hpp>
#include <SFML/Window/Event.hpp> #include <SFML/Window/Event.hpp>
#include <SFML/Window/Keyboard.hpp>
const sf::Time TIME_PER_FRAME = sf::seconds(1.f / 60.f); const sf::Time TIME_PER_FRAME = sf::seconds(1.f / 60.f);
Application::Application() : Application::Application() :
game_window({1280, 720}, "Test") _game_window({1280, 720}, "Test"),
_debug(true)
{ {
float x = game_window.getSize().x; _font.loadFromFile("VeraMono.ttf");
float y = game_window.getSize().y; _grade.setFont(_font);
pulse_mask.setSize({x, y}); _grade.setPosition(160, 160);
pulse_mask.setOrigin(0.f, 0.f); _grade.setFillColor(sf::Color(255, 255, 255, 0));
pulse_mask.setFillColor(sf::Color(255, 0, 0, 0)); _grade.setCharacterSize(35);
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);
} }
void Application::run() void Application::run()
{ {
game_window.display(); // BPM of METEOR is 170.
sf::Int64 iter = 9000 + (1412162 * 25); // Length is 1:14
while (iter > 9000) // I calculated that the time between beats is about 1412162 microseconds
sf::Int64 iter = 1412162 * 25;
while (iter > 0)
{ {
Note note(iter, iter + 412162); Note note(iter, iter + 412162);
timeline.push(note); _timeline.push(note);
iter -= 1412162; 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::Clock timer;
sf::Time time_since_last_update = sf::Time::Zero; 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; input();
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;
}
}
}
time_since_last_update += timer.restart(); 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; time_since_last_update -= TIME_PER_FRAME;
update(); 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() 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(); _debug.onBeat();
pulse_mask_green.setFillColor(sf::Color(0, 255, 0, 255));
} }
text.setString(std::to_string(music.getPlayingOffset().asSeconds())); if (!_timeline.empty() && _timeline.top().deathOffset() <= microseconds)
if (pulse_mask.getFillColor().a > 0)
{ {
const auto alpha = pulse_mask.getFillColor().a - 25; _timeline.pop();
pulse_mask.setFillColor(sf::Color(255, 0, 0, alpha < 0 ? 0 : alpha)); _debug.onDeath();
} }
if (pulse_mask_green.getFillColor().a > 0) _debug.update(microseconds);
{
const auto alpha = pulse_mask_green.getFillColor().a - 25;
pulse_mask_green.setFillColor(sf::Color(0, 255, 0, alpha < 0 ? 0 : alpha));
}
if (grade.getFillColor().a > 0) if (_grade.getFillColor().a > 0)
{ {
const auto alpha = grade.getFillColor().a - 20; const auto alpha = _grade.getFillColor().a - 20;
grade.setFillColor(sf::Color(255, 255, 255, alpha < 0 ? 0 : alpha)); _grade.setFillColor(sf::Color(255, 255, 255, alpha < 0 ? 0 : alpha));
} }
} }
void Application::draw() void Application::draw()
{ {
game_window.clear(); _game_window.clear();
game_window.draw(pulse_mask); _debug.drawOn(_game_window);
game_window.draw(pulse_mask_green); _game_window.draw(_grade);
game_window.draw(text); _game_window.display();
game_window.draw(grade);
game_window.display();
} }

View File

@ -1,15 +1,13 @@
#ifndef APPLICATION_H #ifndef APPLICATION_H
#define APPLICATION_H #define APPLICATION_H
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Audio/Music.hpp> #include <SFML/Audio/Music.hpp>
#include <SFML/System/Clock.hpp> #include <SFML/System/Clock.hpp>
#include <SFML/Graphics/Text.hpp> #include <SFML/Window/Keyboard.hpp>
#include <SFML/Graphics/Font.hpp>
#include <SFML/Graphics/RectangleShape.hpp>
#include <stack> #include <stack>
#include "debughelper.h"
#include "note.h" #include "note.h"
class Application class Application
@ -17,22 +15,24 @@ class Application
public: public:
Application(); Application();
void run(); void run();
void input();
void update(); void update();
void draw(); void draw();
private: private:
sf::RenderWindow game_window; sf::RenderWindow _game_window;
sf::Music music; sf::Music _music;
sf::RectangleShape pulse_mask;
sf::RectangleShape pulse_mask_green;
std::stack<Note> timeline; std::stack<Note> _timeline;
sf::Int64 time_since_last_tick; sf::Int64 _time_since_last_tick;
sf::Int64 last_stamp; sf::Int64 _last_stamp;
sf::Font font; sf::Font _font;
sf::Font font2; sf::Text _grade;
sf::Text text;
sf::Text grade; DebugHelper _debug;
void startGameLoop();
void onKeyPressed(const sf::Keyboard::Key& key);
}; };
#endif // APPLICATION_H #endif // APPLICATION_H

84
debughelper.cpp Normal file
View File

@ -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 &microseconds)
{
_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));
}

30
debughelper.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef DEBUGHELPER_H
#define DEBUGHELPER_H
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/RectangleShape.hpp>
#include <SFML/Graphics/Font.hpp>
#include <SFML/Graphics/Text.hpp>
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

View File

@ -7,6 +7,11 @@ Note::Note(microsec offset, microsec death_offset, Note::Arrow type) :
_type(type) _type(type)
{} {}
microsec Note::offset() const noexcept
{
return _offset;
}
microsec Note::deathOffset() const noexcept microsec Note::deathOffset() const noexcept
{ {
return _death_offset; 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 Note::calculatePrecision(microsec odds) const
{ {
NoteGrade ret; NoteGrade ret;
if (odds < 500000) if (odds < 412162)
{ {
ret.score = 50; ret.score = 50;
ret.rating = NoteGrade::Rating::GREAT; ret.rating = NoteGrade::Rating::GREAT;
} }
return ret; return ret;

6
note.h
View File

@ -25,15 +25,17 @@ public:
UP, UP,
RIGHT, RIGHT,
DOWN, DOWN,
LEFT LEFT,
NONE
}; };
Note(microsec offset, microsec death_offset, Note::Arrow type = Note::Arrow::UP); Note(microsec offset, microsec death_offset, Note::Arrow type = Note::Arrow::UP);
NoteGrade onTap(Arrow arrow_type, microsec tap_time_stamp) const; NoteGrade onTap(Arrow arrow_type, microsec tap_time_stamp) const;
microsec offset() const noexcept;
microsec deathOffset() const noexcept; microsec deathOffset() const noexcept;
private: private:
microsec _offset; microsec _offset;
microsec _death_offset; microsec _death_offset;