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
 | ||||
|  | ||||
							
								
								
									
										17
									
								
								include/note.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								include/note.h
									
									
									
									
									
										Normal file
									
								
							| @ -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
 | ||||
|  | ||||
							
								
								
									
										6
									
								
								include/sprite.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								include/sprite.h
									
									
									
									
									
										Normal file
									
								
							| @ -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
 | ||||
|  | ||||
							
								
								
									
										8
									
								
								src/classicgame/classicnote.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/classicgame/classicnote.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| #include "classicnote.h" | ||||
| 
 | ||||
| ClassicNote::ClassicNote(const std::vector<microsec>& intervals, microsec perfect_offset) : | ||||
|     Note(perfect_offset), | ||||
|     _evaluator(intervals, _perfect_offset) | ||||
| { | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										22
									
								
								src/classicgame/classicnote.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/classicgame/classicnote.h
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| }; | ||||
							
								
								
									
										0
									
								
								src/classicgame/classicsprite.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/classicgame/classicsprite.cpp
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								src/classicgame/classicsprite.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/classicgame/classicsprite.h
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
							
								
								
									
										43
									
								
								src/classicgame/classictimeline.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/classicgame/classictimeline.h
									
									
									
									
									
										Normal file
									
								
							| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user