Improve and encapsulate DebugHelper, work on Note

This commit is contained in:
NaiJi ✨ 2021-04-06 20:39:29 +03:00
parent e37fb7b539
commit 47277ee754
6 changed files with 162 additions and 79 deletions

4
.gitignore vendored
View File

@ -1,6 +1,10 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
SFML*
CMake*
*~
*.autosave
*.a

View File

@ -20,16 +20,26 @@ void Application::run()
// 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;
std::string song_filename = "/home/naiji/METEOR.flac";
microsec starting_beat_offset = 372162;
int amount_of_beats = 209;
microsec time_between_beats = 1412162;
microsec note_input_offset = 412162;
sf::Int64 iter = starting_beat_offset + (time_between_beats * amount_of_beats);
Note::setPrecisionQualifier(note_input_offset / 2);
while (iter > 0)
{
Note note(iter, iter + 412162);
Note note(iter, note_input_offset);
_timeline.push(note);
iter -= 1412162;
iter -= time_between_beats;
}
// // // // // // // //
_music.openFromFile("/home/naiji/METEOR.flac");
_music.openFromFile(song_filename);
_music.play();
_music.setVolume(5);
@ -154,7 +164,7 @@ void Application::onKeyPressed(const sf::Keyboard::Key &key)
if (arrow != Note::Arrow::NONE)
{
_debug.onUserTap();
_debug.spawnGreenPulse();
if (!_timeline.empty())
{
@ -169,17 +179,29 @@ void Application::update()
{
const auto microseconds = _music.getPlayingOffset().asMicroseconds();
if (!_timeline.empty() && _timeline.top().offset() <= microseconds)
// To Do: Here we notice when next note becomes active and ready for user input.
// Here I explicitly calculate its birth time for now.
if (!_timeline.empty() && _timeline.top().offset() - 412162 <= microseconds)
{
_debug.onBeat();
_debug.spawnBluePulse();
}
if (!_timeline.empty() && _timeline.top().deathOffset() <= microseconds)
// To do: Actual note offset should pulse only once and the note shouldn't die right after it,
// because there is also "after pulse" offset, like, you know, player can be a little late
if (!_timeline.empty() && _timeline.top().offset() <= microseconds)
{
_timeline.pop();
_debug.onDeath();
_debug.spawnRedPulse();
}
// To do: Here should be the end of "after pulse" time. When user fucked up all the time and the
// note dies with "Missed" grade.
//
// if ( . . . _timeline.top().offset() + 412162 <= microseconds)
// {
//
// }
_debug.update(microseconds);
if (_grade.getFillColor().a > 0)

View File

@ -1,18 +1,11 @@
#include "debughelper.h"
DebugHelper::DebugHelper(bool init) :
_toggled(init)
_toggled(init),
_red_pulse({0.f, 0.f}, sf::Color(255, 0, 0)),
_green_pulse({460.f, 0.f}, sf::Color(0, 255, 0)),
_blue_pulse({460.f, 360.f}, sf::Color(0, 100, 255))
{
_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);
@ -26,59 +19,71 @@ void DebugHelper::toggle()
_toggled = !_toggled;
}
static bool isVisible(const sf::Shape* shape)
{
return shape->getFillColor().a > 0;
}
void DebugHelper::update(const sf::Int64 &microseconds)
void DebugHelper::update(const microsec &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));
}
_red_pulse.fade();
_green_pulse.fade();
_blue_pulse.fade();
}
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);
_red_pulse.drawOn(game_window);
_green_pulse.drawOn(game_window);
_blue_pulse.drawOn(game_window);
game_window.draw(_time_print);
}
}
void DebugHelper::onUserTap()
void DebugHelper::spawnGreenPulse()
{
const sf::Uint8 alpha = 255;
_tap_pulse.setFillColor(sf::Color(255, 0, 0, alpha));
_green_pulse.appear();
}
void DebugHelper::onBeat()
void DebugHelper::spawnRedPulse()
{
const sf::Uint8 alpha = 255;
_bpm_pulse.setFillColor(sf::Color(0, 255, 0, alpha));
_red_pulse.appear();
}
void DebugHelper::onDeath()
void DebugHelper::spawnBluePulse()
{
const sf::Uint8 alpha = 255;
_death_pulse.setFillColor(sf::Color(0, 100, 255, alpha));
_blue_pulse.appear();
}
DebugHelper::Pulse::Pulse(sf::Vector2f position, sf::Color fill_color)
{
_pulse_shape.setSize({480, 360});
_pulse_shape.move(position.x, position.y);
fill_color.a = 0;
_pulse_shape.setFillColor(fill_color);
}
void DebugHelper::Pulse::appear()
{
auto fill_color = _pulse_shape.getFillColor();
fill_color.a = 255;
_pulse_shape.setFillColor(fill_color);
}
void DebugHelper::Pulse::fade()
{
auto fill_color = _pulse_shape.getFillColor();
if (fill_color.a == 0)
return;
const auto new_alpha = fill_color.a - 25;
fill_color.a = new_alpha < 0 ? 0 : new_alpha;
_pulse_shape.setFillColor(fill_color);
}
void DebugHelper::Pulse::drawOn(sf::RenderWindow &game_window) const
{
game_window.draw(_pulse_shape);
}

View File

@ -6,25 +6,42 @@
#include <SFML/Graphics/Font.hpp>
#include <SFML/Graphics/Text.hpp>
using microsec = sf::Int64;
class DebugHelper
{
public:
DebugHelper(bool init = true);
void toggle();
void update(const sf::Int64& microseconds);
void update(const microsec& microseconds);
void drawOn(sf::RenderWindow &game_window) const;
void onUserTap();
void onBeat();
void onDeath();
void spawnGreenPulse();
void spawnRedPulse();
void spawnBluePulse();
private:
bool _toggled;
sf::RectangleShape _bpm_pulse;
sf::RectangleShape _tap_pulse;
sf::RectangleShape _death_pulse;
sf::Font _font;
sf::Text _time_print;
class Pulse
{
public:
Pulse(sf::Vector2f position, sf::Color fill_color);
void appear();
void fade();
void drawOn(sf::RenderWindow &game_window) const;
private:
sf::RectangleShape _pulse_shape;
};
Pulse _red_pulse;
Pulse _green_pulse;
Pulse _blue_pulse;
};
#endif // DEBUGHELPER_H

View File

@ -1,22 +1,28 @@
#include "note.h"
#include <cmath>
Note::Note(microsec offset, microsec death_offset, Note::Arrow type) :
Note::Note(microsec offset, microsec life_span_offset, Note::Arrow type) :
_offset(offset),
_death_offset(death_offset),
_start_handling_offset(_offset + life_span_offset),
_end_handling_offset(_offset - life_span_offset),
_type(type)
{}
void Note::setPosition(coordinates position)
{
_position = position;
}
coordinates Note::position() const noexcept
{
return _position;
}
microsec Note::offset() const noexcept
{
return _offset;
}
microsec Note::deathOffset() const noexcept
{
return _death_offset;
}
NoteGrade Note::onTap(Arrow arrow_type, microsec tap_time_stamp) const
{
if (arrow_type != _type)
@ -28,12 +34,25 @@ NoteGrade Note::onTap(Arrow arrow_type, microsec tap_time_stamp) const
NoteGrade Note::calculatePrecision(microsec odds) const
{
NoteGrade ret;
if (odds < 412162)
NoteGrade ret(0, NoteGrade::Rating::BAD);
if (odds < _precision_qualifier)
{
ret.score = 50;
ret.rating = NoteGrade::Rating::GREAT;
ret = {50, NoteGrade::Rating::GREAT};
}
return ret;
}
bool Note::isActive(microsec music_play_offset) const noexcept
{
return music_play_offset > _start_handling_offset
&& music_play_offset < _end_handling_offset;
}
void Note::setPrecisionQualifier(microsec qualifier)
{
_precision_qualifier = qualifier;
}
microsec Note::_precision_qualifier = 500000; // Default initialization as 0.5 second.

28
note.h
View File

@ -2,21 +2,29 @@
#define NOTE_H
#include <SFML/System/Clock.hpp>
#include <SFML/System/Vector2.hpp>
////////////////////////////////
using microsec = sf::Int64;
using coordinates = sf::Vector2i;
struct NoteGrade
{
int score = 0;
int score;
enum class Rating
{
WRONG,
BAD,
GOOD,
GREAT
} rating = Rating::BAD;
} rating;
NoteGrade(int s, Rating r) : score(s), rating(r) {}
};
////////////////////////////////
class Note
{
public:
@ -30,17 +38,25 @@ public:
NONE
};
Note(microsec offset, microsec death_offset, Note::Arrow type = Note::Arrow::UP);
Note(microsec offset, microsec life_span_offset, Note::Arrow type = Note::Arrow::UP);
void setPosition(coordinates position);
coordinates position() const noexcept;
microsec offset() const noexcept;
NoteGrade onTap(Arrow arrow_type, microsec tap_time_stamp) const;
microsec offset() const noexcept;
microsec deathOffset() const noexcept;
bool isActive(microsec music_play_offset) const noexcept;
static void setPrecisionQualifier(microsec qualifier);
private:
coordinates _position;
microsec _offset;
microsec _death_offset;
microsec _start_handling_offset;
microsec _end_handling_offset;
Arrow _type = Arrow::UP;
static microsec _precision_qualifier;
NoteGrade calculatePrecision(microsec odds) const;
};