forked from NaiJi/project-kyoku
Add debug helper and clean init code
This commit is contained in:
parent
bf8e6be88f
commit
e37fb7b539
228
application.cpp
228
application.cpp
|
@ -1,81 +1,60 @@
|
|||
#include "application.h"
|
||||
#include <SFML/Graphics/Color.hpp>
|
||||
#include <SFML/Window/Event.hpp>
|
||||
#include <SFML/Window/Keyboard.hpp>
|
||||
|
||||
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())
|
||||
{
|
||||
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())
|
||||
while (_game_window.isOpen())
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
#ifndef APPLICATION_H
|
||||
#define APPLICATION_H
|
||||
|
||||
#include <SFML/Graphics/RenderWindow.hpp>
|
||||
#include <SFML/Audio/Music.hpp>
|
||||
#include <SFML/System/Clock.hpp>
|
||||
#include <SFML/Graphics/Text.hpp>
|
||||
#include <SFML/Graphics/Font.hpp>
|
||||
#include <SFML/Graphics/RectangleShape.hpp>
|
||||
#include <SFML/Window/Keyboard.hpp>
|
||||
|
||||
#include <stack>
|
||||
|
||||
#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<Note> 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<Note> _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
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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
|
7
note.cpp
7
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,7 +29,7 @@ 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;
|
||||
|
|
6
note.h
6
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;
|
||||
|
|
Loading…
Reference in New Issue