forked from NaiJi/project-kyoku
Improve and encapsulate DebugHelper, work on Note
This commit is contained in:
parent
e37fb7b539
commit
47277ee754
|
@ -1,6 +1,10 @@
|
||||||
# This file is used to ignore files which are generated
|
# This file is used to ignore files which are generated
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
SFML*
|
||||||
|
CMake*
|
||||||
|
|
||||||
*~
|
*~
|
||||||
*.autosave
|
*.autosave
|
||||||
*.a
|
*.a
|
||||||
|
|
|
@ -20,16 +20,26 @@ void Application::run()
|
||||||
// BPM of METEOR is 170.
|
// BPM of METEOR is 170.
|
||||||
// Length is 1:14
|
// Length is 1:14
|
||||||
// I calculated that the time between beats is about 1412162 microseconds
|
// 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)
|
while (iter > 0)
|
||||||
{
|
{
|
||||||
Note note(iter, iter + 412162);
|
Note note(iter, note_input_offset);
|
||||||
_timeline.push(note);
|
_timeline.push(note);
|
||||||
iter -= 1412162;
|
iter -= time_between_beats;
|
||||||
}
|
}
|
||||||
|
|
||||||
// // // // // // // //
|
// // // // // // // //
|
||||||
|
|
||||||
_music.openFromFile("/home/naiji/METEOR.flac");
|
_music.openFromFile(song_filename);
|
||||||
_music.play();
|
_music.play();
|
||||||
_music.setVolume(5);
|
_music.setVolume(5);
|
||||||
|
|
||||||
|
@ -154,7 +164,7 @@ void Application::onKeyPressed(const sf::Keyboard::Key &key)
|
||||||
|
|
||||||
if (arrow != Note::Arrow::NONE)
|
if (arrow != Note::Arrow::NONE)
|
||||||
{
|
{
|
||||||
_debug.onUserTap();
|
_debug.spawnGreenPulse();
|
||||||
|
|
||||||
if (!_timeline.empty())
|
if (!_timeline.empty())
|
||||||
{
|
{
|
||||||
|
@ -169,17 +179,29 @@ void Application::update()
|
||||||
{
|
{
|
||||||
const auto microseconds = _music.getPlayingOffset().asMicroseconds();
|
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();
|
_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);
|
_debug.update(microseconds);
|
||||||
|
|
||||||
if (_grade.getFillColor().a > 0)
|
if (_grade.getFillColor().a > 0)
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
#include "debughelper.h"
|
#include "debughelper.h"
|
||||||
|
|
||||||
DebugHelper::DebugHelper(bool init) :
|
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");
|
_font.loadFromFile("VeraMono.ttf");
|
||||||
|
|
||||||
_time_print.setFont(_font);
|
_time_print.setFont(_font);
|
||||||
|
@ -26,59 +19,71 @@ void DebugHelper::toggle()
|
||||||
_toggled = !_toggled;
|
_toggled = !_toggled;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isVisible(const sf::Shape* shape)
|
void DebugHelper::update(const microsec µseconds)
|
||||||
{
|
|
||||||
return shape->getFillColor().a > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebugHelper::update(const sf::Int64 µseconds)
|
|
||||||
{
|
{
|
||||||
_time_print.setString(std::to_string(microseconds));
|
_time_print.setString(std::to_string(microseconds));
|
||||||
|
|
||||||
if (isVisible(&_bpm_pulse))
|
_red_pulse.fade();
|
||||||
{
|
_green_pulse.fade();
|
||||||
const auto new_alpha = _bpm_pulse.getFillColor().a - 25;
|
_blue_pulse.fade();
|
||||||
_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
|
void DebugHelper::drawOn(sf::RenderWindow &game_window) const
|
||||||
{
|
{
|
||||||
if (_toggled)
|
if (_toggled)
|
||||||
{
|
{
|
||||||
game_window.draw(_bpm_pulse);
|
_red_pulse.drawOn(game_window);
|
||||||
game_window.draw(_tap_pulse);
|
_green_pulse.drawOn(game_window);
|
||||||
game_window.draw(_death_pulse);
|
_blue_pulse.drawOn(game_window);
|
||||||
game_window.draw(_time_print);
|
game_window.draw(_time_print);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugHelper::onUserTap()
|
void DebugHelper::spawnGreenPulse()
|
||||||
{
|
{
|
||||||
const sf::Uint8 alpha = 255;
|
_green_pulse.appear();
|
||||||
_tap_pulse.setFillColor(sf::Color(255, 0, 0, alpha));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugHelper::onBeat()
|
void DebugHelper::spawnRedPulse()
|
||||||
{
|
{
|
||||||
const sf::Uint8 alpha = 255;
|
_red_pulse.appear();
|
||||||
_bpm_pulse.setFillColor(sf::Color(0, 255, 0, alpha));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugHelper::onDeath()
|
void DebugHelper::spawnBluePulse()
|
||||||
{
|
{
|
||||||
const sf::Uint8 alpha = 255;
|
_blue_pulse.appear();
|
||||||
_death_pulse.setFillColor(sf::Color(0, 100, 255, alpha));
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,25 +6,42 @@
|
||||||
#include <SFML/Graphics/Font.hpp>
|
#include <SFML/Graphics/Font.hpp>
|
||||||
#include <SFML/Graphics/Text.hpp>
|
#include <SFML/Graphics/Text.hpp>
|
||||||
|
|
||||||
|
using microsec = sf::Int64;
|
||||||
|
|
||||||
class DebugHelper
|
class DebugHelper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DebugHelper(bool init = true);
|
DebugHelper(bool init = true);
|
||||||
|
|
||||||
void toggle();
|
void toggle();
|
||||||
void update(const sf::Int64& microseconds);
|
void update(const microsec& microseconds);
|
||||||
void drawOn(sf::RenderWindow &game_window) const;
|
void drawOn(sf::RenderWindow &game_window) const;
|
||||||
void onUserTap();
|
|
||||||
void onBeat();
|
void spawnGreenPulse();
|
||||||
void onDeath();
|
void spawnRedPulse();
|
||||||
|
void spawnBluePulse();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _toggled;
|
bool _toggled;
|
||||||
sf::RectangleShape _bpm_pulse;
|
|
||||||
sf::RectangleShape _tap_pulse;
|
|
||||||
sf::RectangleShape _death_pulse;
|
|
||||||
sf::Font _font;
|
sf::Font _font;
|
||||||
sf::Text _time_print;
|
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
|
#endif // DEBUGHELPER_H
|
||||||
|
|
41
note.cpp
41
note.cpp
|
@ -1,22 +1,28 @@
|
||||||
#include "note.h"
|
#include "note.h"
|
||||||
#include <cmath>
|
#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),
|
_offset(offset),
|
||||||
_death_offset(death_offset),
|
_start_handling_offset(_offset + life_span_offset),
|
||||||
|
_end_handling_offset(_offset - life_span_offset),
|
||||||
_type(type)
|
_type(type)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
void Note::setPosition(coordinates position)
|
||||||
|
{
|
||||||
|
_position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
coordinates Note::position() const noexcept
|
||||||
|
{
|
||||||
|
return _position;
|
||||||
|
}
|
||||||
|
|
||||||
microsec Note::offset() const noexcept
|
microsec Note::offset() const noexcept
|
||||||
{
|
{
|
||||||
return _offset;
|
return _offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
microsec Note::deathOffset() const noexcept
|
|
||||||
{
|
|
||||||
return _death_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
NoteGrade Note::onTap(Arrow arrow_type, microsec tap_time_stamp) const
|
NoteGrade Note::onTap(Arrow arrow_type, microsec tap_time_stamp) const
|
||||||
{
|
{
|
||||||
if (arrow_type != _type)
|
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 Note::calculatePrecision(microsec odds) const
|
||||||
{
|
{
|
||||||
NoteGrade ret;
|
NoteGrade ret(0, NoteGrade::Rating::BAD);
|
||||||
if (odds < 412162)
|
|
||||||
|
if (odds < _precision_qualifier)
|
||||||
{
|
{
|
||||||
ret.score = 50;
|
ret = {50, NoteGrade::Rating::GREAT};
|
||||||
ret.rating = NoteGrade::Rating::GREAT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
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
28
note.h
|
@ -2,21 +2,29 @@
|
||||||
#define NOTE_H
|
#define NOTE_H
|
||||||
|
|
||||||
#include <SFML/System/Clock.hpp>
|
#include <SFML/System/Clock.hpp>
|
||||||
|
#include <SFML/System/Vector2.hpp>
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
using microsec = sf::Int64;
|
using microsec = sf::Int64;
|
||||||
|
using coordinates = sf::Vector2i;
|
||||||
|
|
||||||
struct NoteGrade
|
struct NoteGrade
|
||||||
{
|
{
|
||||||
int score = 0;
|
int score;
|
||||||
enum class Rating
|
enum class Rating
|
||||||
{
|
{
|
||||||
WRONG,
|
WRONG,
|
||||||
BAD,
|
BAD,
|
||||||
GOOD,
|
GOOD,
|
||||||
GREAT
|
GREAT
|
||||||
} rating = Rating::BAD;
|
} rating;
|
||||||
|
|
||||||
|
NoteGrade(int s, Rating r) : score(s), rating(r) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
class Note
|
class Note
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -30,17 +38,25 @@ public:
|
||||||
NONE
|
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;
|
NoteGrade onTap(Arrow arrow_type, microsec tap_time_stamp) const;
|
||||||
microsec offset() const noexcept;
|
bool isActive(microsec music_play_offset) const noexcept;
|
||||||
microsec deathOffset() const noexcept;
|
|
||||||
|
static void setPrecisionQualifier(microsec qualifier);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
coordinates _position;
|
||||||
microsec _offset;
|
microsec _offset;
|
||||||
microsec _death_offset;
|
microsec _start_handling_offset;
|
||||||
|
microsec _end_handling_offset;
|
||||||
Arrow _type = Arrow::UP;
|
Arrow _type = Arrow::UP;
|
||||||
|
|
||||||
|
static microsec _precision_qualifier;
|
||||||
NoteGrade calculatePrecision(microsec odds) const;
|
NoteGrade calculatePrecision(microsec odds) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue