forked from NaiJi/project-kyoku
Implement basic ClassicNote
This commit is contained in:
parent
3c733cd490
commit
f66951bcec
|
@ -8,6 +8,7 @@
|
|||
#include "debughelper.h"
|
||||
#include "timeline.h"
|
||||
#include "note.h"
|
||||
#include "game.h"
|
||||
|
||||
class Application
|
||||
{
|
||||
|
@ -28,9 +29,9 @@ private:
|
|||
std::unique_ptr<Timeline> _timeline;
|
||||
DebugHelper _debug;
|
||||
|
||||
std::unique_ptr<Game> _game;
|
||||
|
||||
void startGameLoop();
|
||||
void onKeyPressed(const sf::Keyboard::Key& key);
|
||||
void onTap(const Note::Arrow& arrow);
|
||||
};
|
||||
|
||||
#endif // APPLICATION_H
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <SFML/System/Clock.hpp>
|
||||
|
||||
using microsec = sf::Int64;
|
||||
|
||||
class Note
|
||||
{
|
||||
public:
|
||||
explicit Note(microsec perfect_offset) :
|
||||
_perfect_offset(perfect_offset) {}
|
||||
virtual ~Note() = 0;
|
||||
|
||||
protected:
|
||||
microsec _perfect_offset;
|
||||
};
|
|
@ -1,31 +0,0 @@
|
|||
#ifndef NOTEGRAPHICSENTITY_H
|
||||
#define NOTEGRAPHICSENTITY_H
|
||||
|
||||
#include <SFML/Graphics/Drawable.hpp>
|
||||
#include <SFML/Graphics/Transformable.hpp>
|
||||
|
||||
class NoteGraphicsEntity : public sf::Drawable, public sf::Transformable
|
||||
{
|
||||
public:
|
||||
explicit NoteGraphicsEntity();
|
||||
virtual ~NoteGraphicsEntity() = 0;
|
||||
|
||||
virtual void update() = 0;
|
||||
|
||||
virtual void attach() noexcept final;
|
||||
virtual void detach() noexcept final;
|
||||
|
||||
virtual void onKeyPressed() = 0;
|
||||
virtual void onKeyReleased() = 0;
|
||||
|
||||
virtual void show() = 0;
|
||||
virtual void killAsExpired() = 0;
|
||||
virtual void reset() = 0;
|
||||
|
||||
virtual bool isActive() const = 0;
|
||||
|
||||
protected:
|
||||
bool _attached;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -14,9 +14,9 @@ template<typename GRADE, typename = std::enable_if_t<std::is_enum<GRADE>::value>
|
|||
class PrecisionEvaluator
|
||||
{
|
||||
public:
|
||||
PrecisionEvaluator(std::vector<microsec>&& intervals, microsec offset) :
|
||||
PrecisionEvaluator(const std::vector<microsec>& intervals, microsec offset) :
|
||||
_offset(offset),
|
||||
_intervals(std::move(intervals))
|
||||
_intervals(intervals)
|
||||
{
|
||||
microsec&& handling_offset = std::accumulate(intervals.begin(), intervals.end(), 0);
|
||||
_start_handling_offset = _offset - handling_offset;
|
||||
|
@ -41,7 +41,7 @@ public:
|
|||
std::size_t raw_grade;
|
||||
for (raw_grade = 0; raw_grade < _intervals.size(); ++raw_grade)
|
||||
{
|
||||
if (shift_from_perfect <= _intervals[raw_grade])
|
||||
if (shift_from_perfect <= _intervals.at(raw_grade))
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ private:
|
|||
* V0 is PERFECT SCORE and the last V represents the worst
|
||||
* grades which is death of note by expiration */
|
||||
|
||||
std::vector<microsec> _intervals;
|
||||
const std::vector<microsec>& _intervals;
|
||||
};
|
||||
|
||||
#endif // PRECISIONEVALUATOR_H
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
class Sprite
|
||||
{
|
||||
|
||||
};
|
|
@ -2,56 +2,18 @@
|
|||
#define TIMELINE_H
|
||||
|
||||
#include <SFML/Config.hpp>
|
||||
#include <SFML/Graphics/RectangleShape.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
using microsec = sf::Int64;
|
||||
class Note;
|
||||
class TimelineViewManager;
|
||||
|
||||
class Timeline : public sf::Drawable // Probably it's bad
|
||||
class Timeline
|
||||
{
|
||||
public:
|
||||
explicit Timeline(std::unique_ptr<TimelineViewManager> view_manager);
|
||||
virtual ~Timeline();
|
||||
virtual ~Timeline() = default;
|
||||
|
||||
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
|
||||
|
||||
void update(const microsec& music_offset);
|
||||
Note *fetchActiveNote(const microsec &music_offset) noexcept;
|
||||
|
||||
/* void init(); */
|
||||
void clear();
|
||||
|
||||
private:
|
||||
std::vector<Note*> _timeline;
|
||||
std::vector<Note*>::const_iterator _top_note;
|
||||
Note* _active_note;
|
||||
|
||||
std::vector<Note*>::const_iterator _last_visible_note;
|
||||
microsec _visibility_offset;
|
||||
|
||||
std::unique_ptr<TimelineViewManager> _view_manager;
|
||||
|
||||
void checkCurrentActiveNote(const microsec &music_offset);
|
||||
void checkForNextActiveNote(const microsec &music_offset);
|
||||
void prepareNotesToDraw(const microsec &music_offset);
|
||||
|
||||
/* Difference between top and active note is that
|
||||
* top note is the note handling input right now
|
||||
* OR it's the closest note from current music offset
|
||||
* position, not necessarily active. A note stops being top only
|
||||
* after dying or being tapped by player, even if it's already
|
||||
* past her perfect offset.
|
||||
*
|
||||
* Meanwhile active note is the note which is currently handling
|
||||
* player input for grade.
|
||||
*
|
||||
* An active note is always top note but a top note
|
||||
* is not always active note.
|
||||
* */
|
||||
virtual void update(const microsec& music_offset) = 0;
|
||||
virtual void init() = 0;
|
||||
virtual void clear() = 0;
|
||||
};
|
||||
|
||||
#endif // TIMELINE_H
|
||||
|
|
|
@ -6,8 +6,7 @@ class Note;
|
|||
class TimelineViewManager
|
||||
{
|
||||
public:
|
||||
explicit TimelineViewManager();
|
||||
virtual ~TimelineViewManager() = 0;
|
||||
virtual ~TimelineViewManager() = default;
|
||||
|
||||
virtual void initNoteGraphics(Note *note) = 0;
|
||||
};
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
#include "application.h"
|
||||
|
||||
#include "classicgame/classicgame.h"
|
||||
#include <SFML/Graphics/Color.hpp>
|
||||
#include <SFML/Window/Event.hpp>
|
||||
#include "classicviewmanager.h"
|
||||
|
||||
const sf::Time TIME_PER_FRAME = sf::seconds(1.f / 60.f);
|
||||
|
||||
Application::Application() :
|
||||
_game_window({1280, 720}, "Test"),
|
||||
_debug(true)
|
||||
_debug(true),
|
||||
_game(std::make_unique<ClassicGame>())
|
||||
{
|
||||
_timeline = std::make_unique<Timeline>(std::make_unique<ClassicViewManager>());
|
||||
_font.loadFromFile("/usr/share/qtcreator/fonts/SourceCodePro-Regular.ttf");
|
||||
_grade.setFont(_font);
|
||||
_grade.setPosition(160, 160);
|
||||
|
@ -53,121 +52,17 @@ void Application::startGameLoop()
|
|||
}
|
||||
}
|
||||
|
||||
static void makeGradeString(const NoteGrade::Rating& rating, sf::Text& text)
|
||||
{
|
||||
switch (rating)
|
||||
{
|
||||
case (NoteGrade::Rating::BAD):
|
||||
text.setString("BAD");
|
||||
text.setFillColor(sf::Color(255, 255, 255, 255));
|
||||
break;
|
||||
|
||||
case (NoteGrade::Rating::GREAT):
|
||||
text.setString("GREAT");
|
||||
text.setFillColor(sf::Color(255, 255, 0, 255));
|
||||
break;
|
||||
|
||||
case (NoteGrade::Rating::WRONG):
|
||||
text.setString("WRONG");
|
||||
text.setFillColor(sf::Color(120, 120, 120, 255));
|
||||
break;
|
||||
|
||||
case (NoteGrade::Rating::GOOD):
|
||||
text.setString("GOOD");
|
||||
text.setFillColor(sf::Color(255, 100, 120, 255));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
onTap(keyToArrow(key));
|
||||
}
|
||||
|
||||
void Application::onTap(const Note::Arrow &arrow)
|
||||
{
|
||||
if (arrow == Note::Arrow::NONE)
|
||||
return;
|
||||
|
||||
const auto music_offset = _music.getPlayingOffset().asMicroseconds();
|
||||
auto note = _timeline->fetchActiveNote(music_offset);
|
||||
|
||||
if (note)
|
||||
{
|
||||
auto tap_result = note->onTap(arrow, music_offset);
|
||||
makeGradeString(tap_result.rating, _grade);
|
||||
_grade.setFillColor(sf::Color(255, 255, 255, 255));
|
||||
_game->input(event);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::update()
|
||||
{
|
||||
const auto music_offset = _music.getPlayingOffset().asMicroseconds();
|
||||
|
||||
_timeline->update(music_offset);
|
||||
_debug.update(music_offset);
|
||||
|
||||
if (_grade.getFillColor().a > 0) // TODO: Encapsulate
|
||||
{
|
||||
const auto alpha = _grade.getFillColor().a - 20;
|
||||
_grade.setFillColor(sf::Color(255, 255, 255, alpha < 0 ? 0 : alpha));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#include "classicgame.h"
|
||||
#include "classicinputtype.h"
|
||||
#include "classictimeline.h"
|
||||
|
||||
ClassicGame::ClassicGame()
|
||||
ClassicGame::ClassicGame() :
|
||||
_timeline(std::make_unique<ClassicTimeline>())
|
||||
{
|
||||
_keys_to_buttons =
|
||||
{
|
||||
|
@ -71,7 +73,8 @@ void ClassicGame::input(const sf::Event& event)
|
|||
}
|
||||
|
||||
ClassicInputType input(timestamp, new_action);
|
||||
/* Here get active Note from timeline and pass the input object to it */
|
||||
auto note = _timeline->getActiveNote(timestamp);
|
||||
note->
|
||||
}
|
||||
|
||||
Action ClassicGame::getActionKeyPressed(Button button) const
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "game.h"
|
||||
#include "classicactions.h"
|
||||
|
||||
class ClassicTimeline;
|
||||
|
||||
class ClassicGame final : public Game
|
||||
{
|
||||
public:
|
||||
|
@ -25,6 +27,9 @@ private:
|
|||
|
||||
Action getActionKeyPressed(Button button) const;
|
||||
Action getActionKeyReleased(Button button) const;
|
||||
|
||||
std::unique_ptr<ClassicTimeline> _timeline;
|
||||
|
||||
};
|
||||
|
||||
#endif // CLASSICGAME_H
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#include "classicnote.h"
|
||||
|
||||
ClassicNote::ClassicNote(const std::vector<microsec>& intervals, microsec perfect_offset) :
|
||||
Note(perfect_offset),
|
||||
_evaluator(intervals, _perfect_offset)
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "note.h"
|
||||
#include "precisionevaluator.h"
|
||||
|
||||
class ClassicNote : public Note
|
||||
{
|
||||
public:
|
||||
|
||||
enum class GRADE
|
||||
{
|
||||
PERFECT,
|
||||
GOOD,
|
||||
BAD
|
||||
};
|
||||
|
||||
explicit ClassicNote(const std::vector<microsec>& intervals, microsec perfect_offset);
|
||||
virtual ~ClassicNote() = default;
|
||||
|
||||
private:
|
||||
PrecisionEvaluator<GRADE> _evaluator;
|
||||
};
|
|
@ -1,12 +1,9 @@
|
|||
#include "timeline.h"
|
||||
#include "note.h"
|
||||
#include "timelineviewmanager.h"
|
||||
|
||||
#include <SFML/Graphics/RenderTarget.hpp>
|
||||
#include <iostream>
|
||||
#include "classicactions.h"
|
||||
#include "classictimeline.h"
|
||||
#include "note.h"
|
||||
|
||||
Timeline::Timeline(std::unique_ptr<TimelineViewManager> view_manager) :
|
||||
_view_manager(std::move(view_manager))
|
||||
ClassicTimeline::ClassicTimeline()
|
||||
{
|
||||
// BPM of METEOR is 170.
|
||||
// Length is 1:14
|
||||
|
@ -22,15 +19,13 @@ Timeline::Timeline(std::unique_ptr<TimelineViewManager> view_manager) :
|
|||
microsec bpm_end = starting_beat_offset + (interval * amount_of_beats);
|
||||
_visibility_offset = note_input_offset * 12;
|
||||
|
||||
Note::resetPrecisionQualifier(note_input_offset / 3);
|
||||
|
||||
_timeline.emplace_back(new Note(bpm_iterator, note_input_offset, Note::Arrow::DOWN));
|
||||
_timeline.emplace_back(new Note(bpm_iterator, note_input_offset, Button::DOWN));
|
||||
bpm_iterator += interval;
|
||||
|
||||
_timeline.emplace_back(new Note(bpm_iterator, note_input_offset, Note::Arrow::LEFT));
|
||||
_timeline.emplace_back(new Note(bpm_iterator, note_input_offset, Button::LEFT));
|
||||
bpm_iterator += interval;
|
||||
|
||||
_timeline.emplace_back(new Note(bpm_iterator, note_input_offset, Note::Arrow::LEFT));
|
||||
_timeline.emplace_back(new Note(bpm_iterator, note_input_offset, Button::LEFT));
|
||||
bpm_iterator += interval;
|
||||
|
||||
while (bpm_iterator < bpm_end)
|
||||
|
@ -39,19 +34,6 @@ Timeline::Timeline(std::unique_ptr<TimelineViewManager> view_manager) :
|
|||
bpm_iterator += interval;
|
||||
}
|
||||
|
||||
_timeline[0]->setPosition({200, 200});
|
||||
_timeline[1]->setPosition({250, 200});
|
||||
_timeline[2]->setPosition({300, 200});
|
||||
_timeline[3]->setPosition({350, 200});
|
||||
_timeline[4]->setPosition({400, 200});
|
||||
_timeline[5]->setPosition({450, 200});
|
||||
_timeline[6]->setPosition({200, 300});
|
||||
_timeline[7]->setPosition({250, 300});
|
||||
_timeline[8]->setPosition({300, 300});
|
||||
_timeline[9]->setPosition({350, 300});
|
||||
_timeline[10]->setPosition({400, 300});
|
||||
_timeline[11]->setPosition({450, 300});
|
||||
|
||||
_active_note = nullptr;
|
||||
_last_visible_note = _timeline.end();
|
||||
_top_note = _timeline.begin();
|
||||
|
@ -93,19 +75,6 @@ void Timeline::clear()
|
|||
Note::resetPrecisionQualifier();
|
||||
}
|
||||
|
||||
void Timeline::draw(sf::RenderTarget& target, sf::RenderStates states) const // Temporary solution
|
||||
{
|
||||
if (_last_visible_note == _timeline.end() || _top_note > _last_visible_note)
|
||||
return;
|
||||
|
||||
auto note_to_draw = _top_note;
|
||||
while (note_to_draw != (_last_visible_note + 1))
|
||||
{
|
||||
target.draw(*(*note_to_draw), states);
|
||||
++note_to_draw;
|
||||
}
|
||||
}
|
||||
|
||||
void Timeline::update(const microsec &music_offset)
|
||||
{
|
||||
checkCurrentActiveNote(music_offset);
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "timeline.h"
|
||||
|
||||
class Note;
|
||||
|
||||
class ClassicTimeline : public Timeline
|
||||
{
|
||||
public:
|
||||
explicit ClassicTimeline();
|
||||
virtual void update(const microsec& music_offset) override;
|
||||
virtual void init() override;
|
||||
virtual void clear() override;
|
||||
|
||||
Note *getActiveNote(const microsec &music_offset) noexcept;
|
||||
|
||||
private:
|
||||
std::vector<Note*> _timeline;
|
||||
std::vector<Note*>::const_iterator _top_note;
|
||||
Note* _active_note;
|
||||
|
||||
std::vector<Note*>::const_iterator _last_visible_note;
|
||||
microsec _visibility_offset;
|
||||
|
||||
void checkCurrentActiveNote(const microsec &music_offset);
|
||||
void checkForNextActiveNote(const microsec &music_offset);
|
||||
void prepareNotesToDraw(const microsec &music_offset);
|
||||
|
||||
/* Difference between top and active note is that
|
||||
* top note is the note handling input right now
|
||||
* OR it's the closest note from current music offset
|
||||
* position, not necessarily active. A note stops being top only
|
||||
* after dying or being tapped by player, even if it's already
|
||||
* past her perfect offset.
|
||||
*
|
||||
* Meanwhile active note is the note which is currently handling
|
||||
* player input for grade.
|
||||
*
|
||||
* An active note is always top note but a top note
|
||||
* is not always active note.
|
||||
* */
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
#include "notegraphicsentity.h"
|
||||
|
||||
NoteGraphicsEntity::NoteGraphicsEntity() :
|
||||
_attached(false)
|
||||
{}
|
||||
|
||||
void NoteGraphicsEntity::attach() noexcept
|
||||
{
|
||||
_attached = true;
|
||||
}
|
||||
|
||||
void NoteGraphicsEntity::detach() noexcept
|
||||
{
|
||||
_attached = false;
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
#include "timelineviewmanager.h"
|
||||
|
||||
TimelineViewManager::TimelineViewManager()
|
||||
{}
|
Loading…
Reference in New Issue