Separate SFML graphics logic from game logic

This commit is contained in:
NaiJi ✨ 2021-12-28 21:04:50 +03:00
parent 51d83f524a
commit ac88cd9dfa
45 changed files with 480 additions and 592 deletions

View File

@ -12,7 +12,7 @@ public:
virtual void input(PlayerInput&& inputdata) = 0;
virtual void update(UpdateData&& updatedata) = 0;
virtual void draw() const = 0;
virtual void display() const = 0;
virtual void recalculate(const microsec& timestamp) = 0;
void setBPMSections(const std::set<BPMSection, BPMSectionCompt>& sections) noexcept

View File

@ -2,9 +2,8 @@
#include "core/inputtype.h"
#include "core/updatedata.h"
#include <SFML/Graphics/Drawable.hpp>
class Game : public sf::Drawable
class Game
{
public:
virtual ~Game() = default;
@ -12,7 +11,5 @@ public:
virtual void run() = 0;
virtual void input(PlayerInput&& inputdata) = 0;
virtual void update(UpdateData&& updatedata) = 0;
// Separate CORE from SFML in the future
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const = 0;
virtual void display() const = 0;
};

View File

@ -5,4 +5,5 @@ class Sprite
public:
virtual ~Sprite() = default;
virtual void reset() = 0;
virtual void display() const = 0;
};

View File

@ -10,9 +10,9 @@ class SpriteContainer
{
public:
explicit SpriteContainer(std::initializer_list<Type>&& types,
std::unique_ptr<SpriteFactory>&& factory,
const std::shared_ptr<SpriteFactory>& factory,
std::size_t reserve_size = 20) :
_sprite_factory(std::move(factory)),
_sprite_factory(factory),
_poll_reserve_size(reserve_size)
{
for (const Type& type : types)
@ -52,7 +52,7 @@ private:
using SpritePoll = std::stack<std::shared_ptr<Sprite>>;
std::map<Type, SpritePoll> _sprite_dispatcher;
std::unique_ptr<SpriteFactory> _sprite_factory;
const std::shared_ptr<SpriteFactory> _sprite_factory;
std::size_t _poll_reserve_size;
};

View File

@ -6,7 +6,13 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
include_directories(${CMAKE_SOURCE_DIR}/include)
file(GLOB_RECURSE HEADERS "shared/*.h" )
file(GLOB_RECURSE SOURCES "editor/*.h" "editor/*.cpp" "graphics/*.h" "graphics/*.cpp" "game/*.h" "game/*.cpp" "./classicfactory.cpp")
file(GLOB_RECURSE SOURCES "editor/*.h" "editor/*.cpp"
"graphics/*.h" "graphics/*.cpp"
"game/*.h" "game/*.cpp"
"sfml/*.h" "sfml/*.cpp"
"./classicfactory.cpp")
add_library(classicmode STATIC ${SOURCES} ${HEADERS})
target_include_directories(classicmode PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)

View File

@ -1,18 +0,0 @@
#include "shared/classicmode/classicfactory.h"
#include "game/classicgame.h"
#include "graphics/classicgraphicsmanager.h"
#include "tools/music.h"
#include "editor/classiceditor.h"
#include <SFML/Graphics/RenderWindow.hpp>
std::unique_ptr<Game> classic::initGame()
{
return std::make_unique<ClassicGame>();
}
std::unique_ptr<Editor> classic::initEditor()
{
return std::make_unique<ClassicEditor>();
}

View File

@ -1,15 +1,21 @@
#include "classiceditor.h"
ClassicEditor::ClassicEditor() :
#include "game/classicarrownote.h"
// Replace with interface by dependency injection
#include "graphics/animations/classicflyinganimationscenario.h"
#include "graphics/animations/classicdyinganimationscenario.h"
//
ClassicEditor::ClassicEditor(const std::shared_ptr<Timeline<ClassicNote>>& timeline,
const std::shared_ptr<ClassicGraphicsManager>& graphics_manager) :
_timeline(timeline),
_graphics_manager(graphics_manager),
_selected_type(Type::UP),
_current_time(0),
_scroll_step(500000)
{
std::set<MockClassicNote*, NotePtrCompt> set = {};
// VISIBILITY 1648648
_timeline.setNotes(set);
_timeline->setNotes({});
}
microsec ClassicEditor::adjustOffset(microsec offset) const noexcept
@ -33,24 +39,24 @@ void ClassicEditor::input(PlayerInput&& inputdata)
case sf::Event::MouseButtonPressed:
{
const auto note = _timeline.getNoteBy(_current_time);
if (_timeline.isExpired(note) && !_bpm_sections.empty() && _current_time >= (*_bpm_sections.begin()).offset_start)
const auto note = _timeline->getNoteBy(_current_time);
if (_timeline->isExpired(note) && !_bpm_sections.empty() && _current_time >= (*_bpm_sections.begin()).offset_start)
{
NoteInitializer init;
init.context = &_context;
init.intervals = {};
init.perfect_offset = adjustOffset(_current_time);
ArrowNoteInitializer init;
ArrowElementInitializer element;
init.initializer.intervals = {};
init.initializer.perfect_offset = inputdata.timestamp;
init.hold = false;
init.initializer.context = nullptr;
ElementInitializer elem_init;
elem_init.type = _selected_type;
elem_init.coordinates = Coordinates{ event.mouseButton.x, event.mouseButton.y };
elem_init.falling_curve_interpolation = {};
element.element.coordinates = Coordinates(event.mouseButton.x, event.mouseButton.y);
element.element.falling_curve_interpolation = {};
element.keys = {sf::Keyboard::W, sf::Keyboard::Up};
element.element.type = Type::UP;
MockArrowNoteInitializer mock_init;
mock_init.elements = {elem_init};
mock_init.initializer = init;
init.elements = {element};
_timeline.insertNote(new MockClassicNote(std::move(mock_init)));
_timeline->insertNote(new ClassicArrowNote(std::move(init)));
}
break;
}
@ -60,16 +66,16 @@ void ClassicEditor::input(PlayerInput&& inputdata)
void ClassicEditor::update(UpdateData&& updatedata)
{
_timeline.update(updatedata.timestamp);
_timeline->update(updatedata.timestamp);
}
void ClassicEditor::draw() const
void ClassicEditor::display() const
{
}
void ClassicEditor::recalculate(const microsec& timestamp)
{
_timeline.recalculate(timestamp);
_timeline->recalculate(timestamp);
}
void ClassicEditor::selectNoteType(Type type) noexcept

View File

@ -5,18 +5,20 @@
#include "core/editor.h"
#include "core/timeline.h"
#include "mockclassicnote.h"
#include "game/classicnote.h"
#include "classicmode/classicactions.h"
class ClassicGraphicsManager;
class ClassicEditor : public Editor
{
public:
explicit ClassicEditor();
explicit ClassicEditor(const std::shared_ptr<Timeline<ClassicNote>>& timeline,
const std::shared_ptr<ClassicGraphicsManager>& graphics_manager);
virtual void input(PlayerInput&& inputdata) override;
virtual void update(UpdateData&& updatedata) override;
virtual void draw() const override;
virtual void display() const override;
virtual void recalculate(const microsec& timestamp) override;
void selectNoteType(Type type) noexcept;
@ -26,8 +28,8 @@ private:
Context _context;
std::shared_ptr<ClassicGraphicsManager> _graphics_manager;
Timeline<MockClassicNote> _timeline;
const std::shared_ptr<Timeline<ClassicNote>> _timeline;
const std::shared_ptr<ClassicGraphicsManager> _graphics_manager;
Type _selected_type;
microsec _current_time;

View File

@ -1,137 +0,0 @@
#include "classiceditorgraphicsmanager.h"
#include "graphics/classicsprite.h"
#include "graphics/classicflyinganimationscenario.h"
#include "graphics/classicdyinganimationscenario.h"
ClassicEditorGraphicsManager::ClassicEditorGraphicsManager(Timeline<MockClassicNote> &timeline, const microsec& visibility_offset) :
ClassicGraphicsManager(visibility_offset),
_timeline(&timeline)
{
_timeline->expire(_first);
_timeline->expire(_last);
}
void ClassicEditorGraphicsManager::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
if (nothingToDraw())
return;
for (auto it = _first; it != _last; ++it)
{
(*it)->draw(this, target, states);
}
}
void ClassicEditorGraphicsManager::draw(const std::vector<MockClassicNote::MockElement>& elements, sf::RenderTarget& target, sf::RenderStates states) const
{
for (std::size_t i = 0; i < elements.size(); ++i)
{
const auto& sprite = elements[i].sprite;
if (i >= 1)
{
const auto& neighbor_sprite = elements[i - 1].sprite;
const auto c1 = neighbor_sprite->trailCoordinates();
const auto c2 = sprite->trailCoordinates();
target.draw(makeLine(c1, c2));
}
target.draw(*sprite, states);
}
}
sf::VertexArray ClassicEditorGraphicsManager::makeLine(const Coordinates& c1, const Coordinates& c2) const
{
sf::VertexArray line(sf::LinesStrip, 2);
line[0].color = sf::Color::Yellow;
line[0].position = {c1.x + 10, c1.y};
line[1].color = sf::Color::Blue;
line[1].position = {c2.x + 10, c2.y};
return line;
}
void ClassicEditorGraphicsManager::setGraphics(std::vector<MockClassicNote::MockElement>& elements, TimeRange &&range)
{
for (auto& element : elements)
{
element.sprite = _sprite_container.getSprite(element.type);
element.sprite->setCoordinates(element.coordinates);
element.sprite->setTrailCoordinates(Coordinates( 0.f, 9.f ));
element.animations[MockClassicNote::State::NONE] = nullptr;
element.animations[MockClassicNote::State::FLYING] = std::make_shared<ClassicFlyingAnimationScenario>();
element.animations[MockClassicNote::State::DYING] = std::make_shared<ClassicDyingAnimationScenario>();
element.animations[MockClassicNote::State::DEAD] = nullptr;
element.animations[MockClassicNote::State::FLYING]->launch(element.sprite, range.begin, range.end);
}
}
void ClassicEditorGraphicsManager::update(const microsec &offset)
{
fetchLastNote(offset);
fetchFirstNote(offset);
updateVisibleNotes(offset);
}
void ClassicEditorGraphicsManager::updateVisibleNotes(const microsec &offset)
{
for (auto it = _first; it != _last; ++it)
(*it)->update(offset);
}
void ClassicEditorGraphicsManager::fetchFirstNote(const microsec& offset)
{
(void)offset; // ????
if (nothingToDraw())
return;
Iterator note_iterator = _first;
while (note_iterator != _last)
{
auto note = *note_iterator;
if (note->shouldRemove())
++_first;
++note_iterator;
}
}
void ClassicEditorGraphicsManager::fetchLastNote(const microsec& offset)
{
Iterator note_iterator = _timeline->getTopNote();
while (!_timeline->isExpired(note_iterator) && isVisiblyClose(note_iterator, offset))
{
if (nothingToDraw())
_first = note_iterator;
auto note = *note_iterator;
if (!note->isInGame())
{
note->putToGame();
note->setGraphics(this, TimeRange{offset, note->offset()});
}
++note_iterator;
}
_last = note_iterator;
}
bool ClassicEditorGraphicsManager::nothingToDraw() const noexcept
{
return _timeline->isExpired(_first)
|| _timeline->isExpired(_last);
}
bool ClassicEditorGraphicsManager::isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const noexcept
{
return ((*iterator)->offset() - _visibility_offset) <= music_offset;
}

View File

@ -1,37 +0,0 @@
#pragma once
#include "mockclassicnote.h"
#include "graphics/classicgraphicsmanager.h"
#include "core/timeline.h"
#include <SFML/Graphics/RenderTarget.hpp>
class ClassicSprite;
class ClassicEditorGraphicsManager : public ClassicGraphicsManager
{
public:
explicit ClassicEditorGraphicsManager(Timeline<MockClassicNote>& timeline, const microsec& visibility_offset);
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
void draw(const std::vector<MockClassicNote::MockElement>& elements, sf::RenderTarget& target, sf::RenderStates states) const;
void setGraphics(std::vector<MockClassicNote::MockElement> &elements, TimeRange&& range);
virtual void update(const microsec& offset) override;
private:
using Iterator = Timeline<MockClassicNote>::Iterator;
Iterator _first;
Iterator _last;
Timeline<MockClassicNote> * const _timeline;
inline bool nothingToDraw() const noexcept;
inline bool isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const noexcept;
inline sf::VertexArray makeLine(const Coordinates& c1, const Coordinates& c2) const;
void fetchFirstNote(const microsec& offset);
void fetchLastNote(const microsec& offset);
void updateVisibleNotes(const microsec& offset);
};

View File

@ -1,17 +1,9 @@
#include "mockclassicnote.h"
#include "graphics/classicgraphicsmanager.h"
// Replace with interface by dependency injection
#include "graphics/classicflyinganimationscenario.h"
#include "graphics/classicdyinganimationscenario.h"
//
#include <iostream>
// A LOT OF CODE DUPLICATES game/arrowclassicnote, DO SOMETHING D:<
#include "graphics/animations/classicanimationscenario.h"
MockClassicNote::MockClassicNote(MockArrowNoteInitializer&& init) :
Note(init.initializer.perfect_offset),
ClassicNote({nullptr, {}, init.initializer.perfect_offset}),
_state(State::NONE),
_context(init.initializer.context)
{
@ -24,23 +16,6 @@ MockClassicNote::MockClassicNote(MockArrowNoteInitializer&& init) :
}
}
bool MockClassicNote::isActive(const microsec& offset) const
{
return offset == Note::offset();
}
bool MockClassicNote::isInGame() const
{
return _state == State::FLYING
|| _state == State::DYING;
}
bool MockClassicNote::shouldRemove() const
{
return _state == State::DEAD
|| _state == State::NONE;
}
void MockClassicNote::putToGame()
{
_state = State::FLYING;

View File

@ -3,44 +3,22 @@
#include <memory>
#include <array>
#include "core/note.h"
#include "mockelement.h"
#include "game/classicnote.h"
#include "initializers/mockarrownoteinitializer.h"
class ClassicSprite;
class ClassicAnimationScenario;
class MockClassicNote : public Note
class MockClassicNote : public ClassicNote
{
public:
enum State
{
NONE,
FLYING,
DYING,
DEAD
};
explicit MockClassicNote(MockArrowNoteInitializer&& init);
virtual ~MockClassicNote() override = default;
virtual bool isActive(const microsec& offset) const override final;
virtual bool isInGame() const override final;
virtual bool shouldRemove() const override final;
virtual void putToGame() override;
virtual void update(const microsec &music_offset) override;
virtual void input(PlayerInput&& inputdata) override;
virtual void putToGame() override final;
virtual void update(const microsec &music_offset) override final;
struct MockElement
{
std::shared_ptr<ClassicSprite> sprite;
std::array<std::shared_ptr<ClassicAnimationScenario>, 4> animations;
Type type = Type::NONE;
Coordinates coordinates;
std::vector<Coordinates> falling_curve_interpolation;
};
virtual void display(const ClassicGraphicsManager * const manager) const override;
virtual void setGraphics(ClassicGraphicsManager * const manager, TimeRange&& range) override;
private:
std::vector<MockElement> _elements;

View File

@ -0,0 +1,21 @@
#pragma once
#include "classicmode/classicactions.h"
#include "tools/mathutils.h"
#include <vector>
#include <memory>
#include <array>
class ClassicSprite;
class ClassicAnimationScenario;
struct MockElement
{
std::shared_ptr<ClassicSprite> sprite;
std::array<std::shared_ptr<ClassicAnimationScenario>, 4> animations;
Type type = Type::NONE;
Coordinates coordinates;
std::vector<Coordinates> falling_curve_interpolation;
};

View File

@ -0,0 +1,30 @@
#pragma once
#include "classicmode/classicactions.h"
#include "core/inputtype.h"
#include "tools/mathutils.h"
#include <memory>
#include <vector>
#include <array>
class ClassicSprite;
class ClassicAnimationScenario;
struct ArrowElement
{
std::shared_ptr<ClassicSprite> sprite;
std::array<std::shared_ptr<ClassicAnimationScenario>, 5> animations;
sf::Keyboard::Key pressed_as = sf::Keyboard::Unknown;
Coordinates coordinates;
std::vector<Coordinates> falling_curve_interpolation;
std::array<sf::Keyboard::Key, 2> keys;
Type type = Type::NONE;
bool pressed = false;
// Each note may consist of several buttons.
// For example, ↑ → or ↓ → ←
// Note Element represents this idea.
};

View File

@ -1,6 +1,6 @@
#include "classicarrownote.h"
#include "game/classicgamegraphicsmanager.h"
#include "graphics/classicanimationscenario.h"
#include "graphics/classicscenegraphicsmanager.h"
#include "graphics/animations/classicanimationscenario.h"
#include "holdmanager.h"
ClassicArrowNote::ClassicArrowNote(ArrowNoteInitializer&& init) :
@ -89,12 +89,12 @@ void ClassicArrowNote::update(const microsec& music_offset)
element.animations[_state]->update(music_offset);
}
void ClassicArrowNote::draw(const ClassicGameGraphicsManager * const manager, sf::RenderTarget& target, sf::RenderStates states) const
void ClassicArrowNote::display(const ClassicGraphicsManager * const manager) const
{
manager->draw(_elements, target, states);
manager->display(_elements);
}
void ClassicArrowNote::setGraphics(ClassicGameGraphicsManager * const manager, TimeRange&& range)
void ClassicArrowNote::setGraphics(ClassicGraphicsManager * const manager, TimeRange&& range)
{
manager->setGraphics(_elements, std::move(range));
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "arrowelement.h"
#include "classicnote.h"
#include "initializers/arrownoteinitializer.h"
@ -13,33 +14,16 @@ public:
virtual void update(const microsec &music_offset) override;
virtual void input(PlayerInput&& inputdata) override;
virtual void draw(const ClassicGameGraphicsManager * const manager, sf::RenderTarget& target, sf::RenderStates states) const override;
virtual void setGraphics(ClassicGameGraphicsManager * const manager, TimeRange&& range) override;
virtual void display(const ClassicGraphicsManager * const manager) const override;
virtual void setGraphics(ClassicGraphicsManager * const manager, TimeRange&& range) override;
bool allElementsPressed() const;
bool isPressedAs(sf::Keyboard::Key key) const;
inline bool isHold() const;
struct ArrowElement
{
std::shared_ptr<ClassicSprite> sprite;
std::array<std::shared_ptr<ClassicAnimationScenario>, 5> animations;
sf::Keyboard::Key pressed_as = sf::Keyboard::Unknown;
Coordinates coordinates;
std::vector<Coordinates> falling_curve_interpolation;
std::array<sf::Keyboard::Key, 2> keys;
Type type = Type::NONE;
bool pressed = false;
// Each note may consist of several buttons.
// For example, ↑ → or ↓ → ←
// Note Element represents this idea.
};
private:
std::vector<ArrowElement> _elements;
bool _is_hold;
};
using ArrowElements = std::vector<ClassicArrowNote::ArrowElement>;
using ArrowElements = std::vector<ArrowElement>;

View File

@ -1,11 +1,13 @@
#include "classicgame.h"
#include "classicnote.h"
#include "classicmapcreator.h"
#include "game/classicgamegraphicsmanager.h"
#include "graphics/classicscenegraphicsmanager.h"
#include "holdmanager.h"
ClassicGame::ClassicGame() :
_graphics_manager(new ClassicGameGraphicsManager(_timeline, 1648648)),
ClassicGame::ClassicGame(const std::shared_ptr<Timeline<ClassicNote>>& timeline,
const std::shared_ptr<ClassicGraphicsManager>& graphics_manager) :
_timeline(timeline),
_graphics_manager(graphics_manager),
_hold_manager(std::make_unique<HoldManager>())
{
_slap_buffer.loadFromFile("Tick.ogg");
@ -59,7 +61,7 @@ void ClassicGame::run()
_context.hold_manager = _hold_manager;
auto beatmap = classic::createBeatmap("aa", _context);
_timeline.setNotes(beatmap.notes);
_timeline->setNotes(beatmap.notes);
}
void ClassicGame::input(PlayerInput&& inputdata)
@ -72,9 +74,9 @@ void ClassicGame::input(PlayerInput&& inputdata)
case sf::Event::KeyPressed:
{
auto note_it = _timeline.getActiveNote(inputdata.timestamp);
auto note_it = _timeline->getActiveNote(inputdata.timestamp);
if (!_timeline.isExpired(note_it))
if (!_timeline->isExpired(note_it))
{
auto note = (*note_it);
note->input(std::move(inputdata));
@ -95,20 +97,20 @@ void ClassicGame::input(PlayerInput&& inputdata)
void ClassicGame::update(UpdateData&& updatedata)
{
// UNCOMMENT TO TEST AUTOPLAY
/*auto note_it = _timeline.getActiveNote(updatedata.timestamp);
auto note_it = _timeline->getActiveNote(updatedata.timestamp);
if (!_timeline.isExpired(note_it) && updatedata.timestamp >= (*note_it)->offset())
if (!_timeline->isExpired(note_it) && updatedata.timestamp >= (*note_it)->offset())
{
auto note = (*note_it);
note->input(PlayerInput{updatedata.timestamp, sf::Event{}});
_slap.play();
}*/
}
_timeline.update(updatedata.timestamp);
_timeline->update(updatedata.timestamp);
_graphics_manager->update(updatedata.timestamp);
}
void ClassicGame::draw(sf::RenderTarget& target, sf::RenderStates states) const
void ClassicGame::display() const
{
_graphics_manager->draw(target, states);
_graphics_manager->display();
}

View File

@ -20,23 +20,24 @@ class HoldManager;
class ClassicGame final : public Game
{
public:
explicit ClassicGame();
explicit ClassicGame(const std::shared_ptr<Timeline<ClassicNote>>& timeline,
const std::shared_ptr<ClassicGraphicsManager>& graphics_manager);
virtual ~ClassicGame() override;
virtual void run() override;
virtual void input(PlayerInput&& inputdata) override;
virtual void update(UpdateData&& updatedata) override;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
virtual void display() const override;
private:
std::map<sf::Keyboard::Key, Type> _keys_to_buttons;
std::map<Type, Action> _buttons_to_pressed_actions;
std::map<Type, Action> _buttons_to_released_actions;
ClassicGraphicsManager * const _graphics_manager;
const std::shared_ptr<Timeline<ClassicNote>> _timeline;
const std::shared_ptr<ClassicGraphicsManager> _graphics_manager;
std::shared_ptr<HoldManager> _hold_manager;
Timeline<ClassicNote> _timeline;
sf::SoundBuffer _slap_buffer;
sf::Sound _slap;

View File

@ -1,137 +0,0 @@
#include "classicgamegraphicsmanager.h"
#include "graphics/classicsprite.h"
#include "graphics/classicflyinganimationscenario.h"
#include "graphics/classicdyinganimationscenario.h"
ClassicGameGraphicsManager::ClassicGameGraphicsManager(Timeline<ClassicNote> &timeline, const microsec& visibility_offset) :
ClassicGraphicsManager(visibility_offset),
_timeline(&timeline)
{
_timeline->expire(_first);
_timeline->expire(_last);
}
void ClassicGameGraphicsManager::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
if (nothingToDraw())
return;
for (auto it = _first; it != _last; ++it)
{
(*it)->draw(this, target, states);
}
}
void ClassicGameGraphicsManager::draw(const std::vector<ClassicArrowNote::ArrowElement>& elements, sf::RenderTarget& target, sf::RenderStates states) const
{
for (std::size_t i = 0; i < elements.size(); ++i)
{
const auto& sprite = elements[i].sprite;
if (i >= 1)
{
const auto& neighbor_sprite = elements[i - 1].sprite;
const auto c1 = neighbor_sprite->trailCoordinates();
const auto c2 = sprite->trailCoordinates();
target.draw(makeLine(c1, c2));
}
target.draw(*sprite, states);
}
}
sf::VertexArray ClassicGameGraphicsManager::makeLine(const Coordinates& c1, const Coordinates& c2) const
{
sf::VertexArray line(sf::LinesStrip, 2);
line[0].color = sf::Color::Yellow;
line[0].position = {c1.x + 10, c1.y};
line[1].color = sf::Color::Blue;
line[1].position = {c2.x + 10, c2.y};
return line;
}
void ClassicGameGraphicsManager::setGraphics(std::vector<ClassicArrowNote::ArrowElement>& elements, TimeRange &&range)
{
for (auto& element : elements)
{
element.sprite = _sprite_container.getSprite(element.type);
element.sprite->setCoordinates(element.coordinates);
element.sprite->setTrailCoordinates(Coordinates( 0.f, 9.f ));
element.animations[ClassicArrowNote::State::NONE] = nullptr;
element.animations[ClassicArrowNote::State::FLYING] = std::make_shared<ClassicFlyingAnimationScenario>();
element.animations[ClassicArrowNote::State::DYING] = std::make_shared<ClassicDyingAnimationScenario>();
element.animations[ClassicArrowNote::State::DEAD] = nullptr;
element.animations[ClassicArrowNote::State::FLYING]->launch(element.sprite, range.begin, range.end);
}
}
void ClassicGameGraphicsManager::update(const microsec &offset)
{
fetchLastNote(offset);
fetchFirstNote(offset);
updateVisibleNotes(offset);
}
void ClassicGameGraphicsManager::updateVisibleNotes(const microsec &offset)
{
for (auto it = _first; it != _last; ++it)
(*it)->update(offset);
}
void ClassicGameGraphicsManager::fetchFirstNote(const microsec& offset)
{
(void)offset; // ????
if (nothingToDraw())
return;
Iterator note_iterator = _first;
while (note_iterator != _last)
{
auto note = *note_iterator;
if (note->shouldRemove())
++_first;
++note_iterator;
}
}
void ClassicGameGraphicsManager::fetchLastNote(const microsec& offset)
{
Iterator note_iterator = _timeline->getTopNote();
while (!_timeline->isExpired(note_iterator) && isVisiblyClose(note_iterator, offset))
{
if (nothingToDraw())
_first = note_iterator;
auto note = *note_iterator;
if (!note->isInGame())
{
note->putToGame();
note->setGraphics(this, TimeRange{offset, note->offset()});
}
++note_iterator;
}
_last = note_iterator;
}
bool ClassicGameGraphicsManager::nothingToDraw() const noexcept
{
return _timeline->isExpired(_first)
|| _timeline->isExpired(_last);
}
bool ClassicGameGraphicsManager::isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const noexcept
{
return ((*iterator)->offset() - _visibility_offset) <= music_offset;
}

View File

@ -1,37 +0,0 @@
#pragma once
#include "game/classicarrownote.h"
#include "graphics/classicgraphicsmanager.h"
#include "core/timeline.h"
#include <SFML/Graphics/RenderTarget.hpp>
class ClassicSprite;
class ClassicGameGraphicsManager : public ClassicGraphicsManager
{
public:
explicit ClassicGameGraphicsManager(Timeline<ClassicNote>& timeline, const microsec& visibility_offset);
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
void draw(const std::vector<ClassicArrowNote::ArrowElement>& elements, sf::RenderTarget& target, sf::RenderStates states) const;
void setGraphics(std::vector<ClassicArrowNote::ArrowElement> &elements, TimeRange&& range);
virtual void update(const microsec& offset) override;
private:
using Iterator = Timeline<ClassicNote>::Iterator;
Iterator _first;
Iterator _last;
Timeline<ClassicNote> * const _timeline;
inline bool nothingToDraw() const noexcept;
inline bool isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const noexcept;
inline sf::VertexArray makeLine(const Coordinates& c1, const Coordinates& c2) const;
void fetchFirstNote(const microsec& offset);
void fetchLastNote(const microsec& offset);
void updateVisibleNotes(const microsec& offset);
};

View File

@ -2,8 +2,8 @@
#include "classicarrownote.h"
// Replace with interface by dependency injection
#include "graphics/classicflyinganimationscenario.h"
#include "graphics/classicdyinganimationscenario.h"
#include "graphics/animations/classicflyinganimationscenario.h"
#include "graphics/animations/classicdyinganimationscenario.h"
//
auto classic::createBeatmap(const std::string& filepath, const Context &context) -> Beatmap

View File

@ -7,9 +7,7 @@
class ClassicSprite;
class ClassicAnimationScenario;
class ClassicGameGraphicsManager;
namespace sf { class RenderTarget; class RenderStates; }
class ClassicGraphicsManager;
class ClassicNote : public Note
{
@ -43,10 +41,8 @@ public:
virtual void input(PlayerInput&& inputdata) = 0;
// encapsulate
virtual void draw(const ClassicGameGraphicsManager * const manager, sf::RenderTarget& target, sf::RenderStates states) const = 0;
virtual void setGraphics(ClassicGameGraphicsManager * const manager, TimeRange&& range) = 0;
//
virtual void display(const ClassicGraphicsManager * const manager) const = 0;
virtual void setGraphics(ClassicGraphicsManager * const manager, TimeRange&& range) = 0;
protected:
const PrecisionEvaluator<Grade> _evaluator;

View File

@ -1,5 +1,5 @@
#include "classicdyinganimationscenario.h"
#include "classicsprite.h"
#include "graphics/classicsprite.h"
void ClassicDyingAnimationScenario::launch(const std::shared_ptr<ClassicSprite> sprite, const microsec& time_begin, const microsec &time_end)
{

View File

@ -1,5 +1,5 @@
#include "classicflyinganimationscenario.h"
#include "classicsprite.h"
#include "graphics/classicsprite.h"
void ClassicFlyingAnimationScenario::launch(const std::shared_ptr<ClassicSprite> sprite, const microsec& time_begin, const microsec &time_end)
{

View File

@ -4,22 +4,24 @@
#include "classicmode/classicactions.h"
#include "graphics/classicspritefactory.h"
#include <SFML/Graphics/RenderTarget.hpp>
class ClassicSprite;
struct ArrowElement;
class ClassicGraphicsManager : public sf::Drawable
class ClassicGraphicsManager : public std::enable_shared_from_this<ClassicGraphicsManager>
{
public:
virtual ~ClassicGraphicsManager() = default;
explicit ClassicGraphicsManager(const microsec& visibility_offset) :
explicit ClassicGraphicsManager(const std::shared_ptr<ClassicSpriteFactory>& factory, const microsec& visibility_offset) :
_sprite_container({Type::UP, Type::DOWN,
Type::LEFT, Type::RIGHT},
std::make_unique<ClassicSpriteFactory>()),
factory),
_visibility_offset(visibility_offset)
{}
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override = 0;
virtual void display(const std::vector<ArrowElement>& elements) const = 0;
virtual void setGraphics(std::vector<ArrowElement>& elements, TimeRange&& range) = 0;
virtual void display() const = 0;
virtual void update(const microsec& offset) = 0;
protected:

View File

@ -0,0 +1,142 @@
#include "classicscenegraphicsmanager.h"
#include "graphics/classicsprite.h"
#include "game/arrowelement.h"
#include "editor/mockelement.h"
#include "graphics/animations/classicflyinganimationscenario.h"
#include "graphics/animations/classicdyinganimationscenario.h"
ClassicSceneGraphicsManager::ClassicSceneGraphicsManager(const std::shared_ptr<Timeline<ClassicNote>>& timeline,
const std::shared_ptr<ClassicSpriteFactory>& factory,
const microsec& visibility_offset) :
ClassicGraphicsManager(factory, visibility_offset),
_timeline(timeline)
{
_timeline->expire(_first);
_timeline->expire(_last);
}
void ClassicSceneGraphicsManager::display() const
{
if (nothingToDraw())
return;
for (auto it = _first; it != _last; ++it)
{
(*it)->display(this);
}
}
void ClassicSceneGraphicsManager::update(const microsec &offset)
{
fetchLastNote(offset);
fetchFirstNote(offset);
updateVisibleNotes(offset);
}
void ClassicSceneGraphicsManager::display(const std::vector<ArrowElement>& elements) const
{
for (std::size_t i = 0; i < elements.size(); ++i)
{
const auto& sprite = elements[i].sprite;
if (i >= 1)
{
//const auto& neighbor_sprite = elements[i - 1].sprite;
//const auto c1 = neighbor_sprite->trailCoordinates();
//const auto c2 = sprite->trailCoordinates();
//_render_target->draw(makeLine(c1, c2));
}
sprite->display();
}
}
void ClassicSceneGraphicsManager::setGraphics(std::vector<ArrowElement>& elements, TimeRange &&range)
{
for (auto& element : elements)
{
element.sprite = _sprite_container.getSprite(element.type);
element.sprite->setCoordinates(element.coordinates);
element.sprite->setTrailCoordinates(Coordinates( 0.f, 9.f ));
element.animations[ClassicNote::State::NONE] = nullptr;
element.animations[ClassicNote::State::FLYING] = std::make_shared<ClassicFlyingAnimationScenario>();
element.animations[ClassicNote::State::DYING] = std::make_shared<ClassicDyingAnimationScenario>();
element.animations[ClassicNote::State::DEAD] = nullptr;
element.animations[ClassicNote::State::FLYING]->launch(element.sprite, range.begin, range.end);
}
}
/*sf::VertexArray ClassicSceneGraphicsSFML::makeLine(const Coordinates& c1, const Coordinates& c2) const
{
sf::VertexArray line(sf::LinesStrip, 2);
line[0].color = sf::Color::Yellow;
line[0].position = {c1.x + 10, c1.y};
line[1].color = sf::Color::Blue;
line[1].position = {c2.x + 10, c2.y};
return line;
}*/
void ClassicSceneGraphicsManager::updateVisibleNotes(const microsec &offset)
{
for (auto it = _first; it != _last; ++it)
(*it)->update(offset);
}
void ClassicSceneGraphicsManager::fetchFirstNote(const microsec& offset)
{
(void)offset; // ????
if (nothingToDraw())
return;
Iterator note_iterator = _first;
while (note_iterator != _last)
{
auto note = *note_iterator;
if (note->shouldRemove())
++_first;
++note_iterator;
}
}
void ClassicSceneGraphicsManager::fetchLastNote(const microsec& offset)
{
Iterator note_iterator = _timeline->getTopNote();
while (!_timeline->isExpired(note_iterator) && isVisiblyClose(note_iterator, offset))
{
if (nothingToDraw())
_first = note_iterator;
auto note = *note_iterator;
if (!note->isInGame())
{
note->putToGame();
note->setGraphics(this, TimeRange{offset, note->offset()});
}
++note_iterator;
}
_last = note_iterator;
}
bool ClassicSceneGraphicsManager::nothingToDraw() const noexcept
{
return _timeline->isExpired(_first)
|| _timeline->isExpired(_last);
}
bool ClassicSceneGraphicsManager::isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const noexcept
{
return ((*iterator)->offset() - _visibility_offset) <= music_offset;
}

View File

@ -0,0 +1,37 @@
#pragma once
#include "game/classicnote.h"
#include "graphics/classicgraphicsmanager.h"
#include "core/timeline.h"
class ClassicSprite;
class ClassicSceneGraphicsManager : public ClassicGraphicsManager
{
public:
explicit ClassicSceneGraphicsManager(const std::shared_ptr<Timeline<ClassicNote>>& timeline,
const std::shared_ptr<ClassicSpriteFactory>& factory,
const microsec& visibility_offset);
virtual void display() const override;
virtual void update(const microsec& offset) override;
virtual void display(const std::vector<ArrowElement>& elements) const override;
virtual void setGraphics(std::vector<ArrowElement>& elements, TimeRange&& range) override;
protected:
using Iterator = Timeline<ClassicNote>::Iterator;
Iterator _first;
Iterator _last;
const std::shared_ptr<Timeline<ClassicNote>> _timeline;
inline bool nothingToDraw() const noexcept;
inline bool isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const noexcept;
//inline sf::VertexArray makeLine(const Coordinates& c1, const Coordinates& c2) const;
void fetchFirstNote(const microsec& offset);
void fetchLastNote(const microsec& offset);
void updateVisibleNotes(const microsec& offset);
};

View File

@ -1,18 +1,14 @@
#include "classicsprite.h"
#include <SFML/Graphics/RenderTarget.hpp>
ClassicSprite::ClassicSprite(const sf::RectangleShape& shape) :
ClassicSprite::ClassicSprite(const std::shared_ptr<sf::RenderTarget> &render_target,
const sf::RectangleShape& shape) :
_prototype(shape),
_shape(shape),
_trail(shape)
_trail(shape),
_render_target(render_target)
{}
void ClassicSprite::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
target.draw(_shape, states);
target.draw(_trail, states);
}
void ClassicSprite::reset()
{
_shape.setPosition(0, 0);
@ -61,3 +57,9 @@ sf::Color ClassicSprite::trailColor() const
{
return _trail.getFillColor();
}
void ClassicSprite::display() const
{
_render_target->draw(_shape);
_render_target->draw(_trail);
}

View File

@ -1,15 +1,18 @@
#pragma once
#include <memory>
#include "tools/mathutils.h"
#include "sprite.h"
#include <SFML/Graphics/RectangleShape.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
class ClassicSprite : public Sprite, public sf::Drawable
class ClassicSprite : public Sprite
{
public:
explicit ClassicSprite(const sf::RectangleShape& shape);
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
explicit ClassicSprite(const std::shared_ptr<sf::RenderTarget>& render_target,
const sf::RectangleShape& shape);
virtual void reset() override;
virtual void display() const override;
void setCoordinates(const Coordinates &coordinates);
void setTrailCoordinates(const Coordinates &coordinates);
@ -26,4 +29,6 @@ private:
sf::RectangleShape _shape;
sf::RectangleShape _trail;
const std::shared_ptr<sf::RenderTarget> _render_target;
};

View File

@ -1,39 +1,13 @@
#pragma once
#include <memory>
#include "classicmode/classicactions.h"
#include "classicsprite.h"
#include "graphics/classicsprite.h"
class ClassicSpriteFactory
{
public:
inline std::shared_ptr<ClassicSprite> create(Type type)
{
sf::RectangleShape sprite;
sprite.setSize({20.f, 20.f});
switch (type)
{
case Type::UP:
sprite.setFillColor(sf::Color(255, 0, 0));
break;
case Type::DOWN:
sprite.setFillColor(sf::Color(0, 255, 0));
break;
case Type::LEFT:
sprite.setFillColor(sf::Color(0, 0, 255));
break;
case Type::RIGHT:
sprite.setFillColor(sf::Color(255, 0, 255));
break;
default: // yellow
sprite.setFillColor(sf::Color(255, 239, 0));
}
return std::make_shared<ClassicSprite>(sprite);
}
virtual ~ClassicSpriteFactory() = default;
virtual std::shared_ptr<ClassicSprite> create(Type type) const = 0;
};

View File

@ -0,0 +1,9 @@
#pragma once
#include <vector>
#include <memory>
class ClassicNotePainter
{
virtual ~ClassicNotePainter() = default;
};

View File

@ -0,0 +1,32 @@
#include "shared/classicmode/classicfactorysfml.h"
#include "sfml/spritefactorysfml.h"
#include "graphics/classicscenegraphicsmanager.h"
#include "core/timeline.h"
#include "game/classicgame.h"
#include "editor/classiceditor.h"
std::unique_ptr<Game> classic::initGame(const std::shared_ptr<sf::RenderTarget>& render_target)
{
// read offset from beatmap metadata
const microsec visibility_offset = 1648648;
const auto factory = std::make_shared<ClassicSpriteFactorySFML>(render_target);
const auto timeline = std::make_shared<Timeline<ClassicNote>>();
const auto graphics_manager = std::make_shared<ClassicSceneGraphicsManager>(timeline, factory, visibility_offset);
return std::make_unique<ClassicGame>(timeline, graphics_manager);
}
std::unique_ptr<Editor> classic::initEditor(const std::shared_ptr<sf::RenderTarget>& render_target)
{
// read offset from beatmap metadata
const microsec visibility_offset = 1648648;
const auto factory = std::make_shared<ClassicSpriteFactorySFML>(render_target);
const auto timeline = std::make_shared<Timeline<ClassicNote>>();
const auto graphics_manager = std::make_shared<ClassicSceneGraphicsManager>(timeline, factory, visibility_offset);
return std::make_unique<ClassicEditor>(timeline, graphics_manager);
}

View File

@ -0,0 +1,34 @@
#include "spritefactorysfml.h"
ClassicSpriteFactorySFML::ClassicSpriteFactorySFML(const std::shared_ptr<sf::RenderTarget>& render_target) :
_render_target(render_target)
{}
std::shared_ptr<ClassicSprite> ClassicSpriteFactorySFML::create(Type type) const
{
sf::RectangleShape sprite;
sprite.setSize({20.f, 20.f});
switch (type)
{
case Type::UP:
sprite.setFillColor(sf::Color(255, 0, 0));
break;
case Type::DOWN:
sprite.setFillColor(sf::Color(0, 255, 0));
break;
case Type::LEFT:
sprite.setFillColor(sf::Color(0, 0, 255));
break;
case Type::RIGHT:
sprite.setFillColor(sf::Color(255, 0, 255));
break;
default: // yellow
sprite.setFillColor(sf::Color(255, 239, 0));
}
return std::make_shared<ClassicSprite>(_render_target, sprite);
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "graphics/classicspritefactory.h"
#include <SFML/Graphics/RenderTarget.hpp>
class ClassicSpriteFactorySFML : public ClassicSpriteFactory
{
public:
explicit ClassicSpriteFactorySFML(const std::shared_ptr<sf::RenderTarget>& render_target);
virtual std::shared_ptr<ClassicSprite> create(Type type) const override;
private:
const std::shared_ptr<sf::RenderTarget> _render_target;
};

View File

@ -1,12 +0,0 @@
#pragma once
#include <memory>
class Game;
class Editor;
namespace classic
{
std::unique_ptr<Game> initGame();
std::unique_ptr<Editor> initEditor();
}

View File

@ -0,0 +1,14 @@
#pragma once
#include <memory>
#include <SFML/Graphics/RenderWindow.hpp>
class Game;
class Editor;
namespace classic
{
std::unique_ptr<Game> initGame(const std::shared_ptr<sf::RenderTarget>& render_target);
std::unique_ptr<Editor> initEditor(const std::shared_ptr<sf::RenderTarget>& render_target);
}

View File

@ -7,21 +7,21 @@
#include "editorstate.h"
#include "tools/music.h"
#include "classicmode/classicfactory.h"
#include "classicmode/classicfactorysfml.h"
#include <iostream>
const sf::Time TIME_PER_FRAME = sf::seconds(1.f / 90.f);
Application::Application() :
_game_window({1280, 720}, "Test", sf::Style::Default)
_game_window(std::make_unique<sf::RenderWindow>(sf::VideoMode{1280, 720}, "Test", sf::Style::Default))
{
_font_holder.load(Fonts::Id::GUI, "SourceCodePro-Regular.ttf");
_game_window.setFramerateLimit(60);
_game_window.setKeyRepeatEnabled(false);
_game_window.setMouseCursorGrabbed(false);
_game_window.setVerticalSyncEnabled(true);
_game_window->setFramerateLimit(60);
_game_window->setKeyRepeatEnabled(false);
_game_window->setMouseCursorGrabbed(false);
_game_window->setVerticalSyncEnabled(true);
MainMenu::Callbacks callbacks =
{
@ -32,8 +32,8 @@ Application::Application() :
EditorState::Callbacks editor_callbacks = {[&](){ popState(); }};
const auto main_menu = std::make_shared<MainMenu>(std::move(callbacks), _font_holder);
const auto game_state = std::make_shared<GameState>(classic::initGame(), GameState::Callbacks());
const auto editor = std::make_shared<EditorState>(classic::initEditor(), std::move(editor_callbacks), _font_holder);
const auto game_state = std::make_shared<GameState>(classic::initGame(_game_window), GameState::Callbacks());
const auto editor = std::make_shared<EditorState>(classic::initEditor(_game_window), std::move(editor_callbacks), _font_holder);
_states[GUIState::Tag::MAIN_MENU] = main_menu;
_states[GUIState::Tag::GAME] = game_state;
@ -44,7 +44,7 @@ Application::Application() :
void Application::run()
{
_game_window.display();
_game_window->display();
exec();
}
@ -53,7 +53,7 @@ void Application::exec()
sf::Clock timer;
sf::Time time_since_last_update = sf::Time::Zero;
while (_game_window.isOpen())
while (_game_window->isOpen())
{
time_since_last_update += timer.restart();
@ -72,12 +72,12 @@ void Application::exec()
void Application::input()
{
sf::Event event;
while (_game_window.pollEvent(event))
while (_game_window->pollEvent(event))
{
switch(event.type)
{
case sf::Event::Closed:
_game_window.close();
_game_window->close();
break;
default:
@ -94,12 +94,12 @@ void Application::update(const sf::Time& dt)
void Application::draw()
{
_game_window.clear();
_game_window->clear();
for (const auto& state : _state_stack)
_game_window.draw(*state);
_game_window->draw(*state);
_game_window.display();
_game_window->display();
}
void Application::pushState(GUIState::Tag new_state)
@ -108,12 +108,12 @@ void Application::pushState(GUIState::Tag new_state)
_state_stack.back()->leave();
_state_stack.emplace_back(_states.at(new_state));
_state_stack.back()->enter(_game_window.getSize());
_state_stack.back()->enter(_game_window->getSize());
}
void Application::popState()
{
_state_stack.back()->leave();
_state_stack.pop_back();
_state_stack.back()->enter(_game_window.getSize());
_state_stack.back()->enter(_game_window->getSize());
}

View File

@ -29,7 +29,7 @@ private:
std::array<std::shared_ptr<GUIState>, GUIState::Tag::AMOUNT> _states;
std::vector<std::shared_ptr<GUIState>> _state_stack;
sf::RenderWindow _game_window;
std::shared_ptr<sf::RenderWindow> _game_window;
Music _music;
void exec();

View File

@ -151,7 +151,7 @@ void EditorState::enter(sf::Vector2u &&render_size)
callbacks.onDraw = [&editor](sf::RenderTarget& target, sf::RenderStates states)
{
(void)target; (void)states; // fucking shit i am a retard damn fuck fuck
editor->draw();
editor->display();
};
callbacks.onInput = [&editor, &music](const sf::Event& event)

View File

@ -24,7 +24,8 @@ void GameState::update(const sf::Time& dt)
void GameState::draw(sf::RenderTarget &target, sf::RenderStates states) const
{
_game->draw(target, states);
(void)target; (void)states;
_game->display();
}
void GameState::enter(sf::Vector2u&& render_size)