Implement generic SpriteContainer

This commit is contained in:
NaiJi ✨ 2021-06-16 20:11:00 +03:00
parent 106697f6af
commit cbe0fbb673
13 changed files with 210 additions and 144 deletions

58
include/spritecontainer.h Normal file
View File

@ -0,0 +1,58 @@
#pragma once
#include <memory>
#include <stack>
#include <map>
template<typename Type, class SpriteFactory, class Sprite,
typename = std::enable_if_t<std::is_enum<Type>::value>>
class SpriteContainer
{
public:
explicit SpriteContainer(std::initializer_list<Type>&& types,
std::unique_ptr<SpriteFactory>&& factory,
std::size_t reserve_size = 20) :
_sprite_factory(std::move(factory)),
_poll_reserve_size(reserve_size)
{
for (const Type& type : types)
reallocatePoll(type);
}
inline std::shared_ptr<Sprite> getSprite(Type type)
{
SpritePoll& poll = _sprite_dispatcher.at(type);
if (poll.empty())
reallocatePoll(type);
std::shared_ptr<Sprite> sprite = poll.top();
poll.pop();
return sprite;
}
inline void resetSprite(const std::shared_ptr<Sprite> &sprite, Type action) noexcept
{
_sprite_dispatcher[action].push(sprite);
}
~SpriteContainer(){}
private:
inline void reallocatePoll(Type sprite_type)
{
SpritePoll &poll = _sprite_dispatcher[sprite_type];
for (std::size_t i = 0; i < _poll_reserve_size; ++i)
{
poll.push(_sprite_factory->create(sprite_type));
}
}
using SpritePoll = std::stack<std::shared_ptr<Sprite>>;
std::map<Type, SpritePoll> _sprite_dispatcher;
std::unique_ptr<SpriteFactory> _sprite_factory;
std::size_t _poll_reserve_size;
};

View File

@ -11,7 +11,9 @@ enum class Action
PRESS_LEFT, RELEASE_LEFT, PRESS_LEFT, RELEASE_LEFT,
PRESS_SLIDER_RIGHT, RELEASE_SLIDER_RIGHT, PRESS_SLIDER_RIGHT, RELEASE_SLIDER_RIGHT,
PRESS_SLIDER_LEFT, RELEASE_SLIDER_LEFT PRESS_SLIDER_LEFT, RELEASE_SLIDER_LEFT,
COUNT
}; };
enum class Button enum class Button

View File

@ -1,11 +1,13 @@
#include "classicgame.h" #include "classicgame.h"
#include "classicinputtype.h" #include "classicinputtype.h"
#include "classictimeline.h" #include "classictimeline.h"
#include "classicgraphicsmanager.h"
#include "spritecontainer.h" #include "spritecontainer.h"
#include "classicnote.h" #include "classicnote.h"
ClassicGame::ClassicGame() : ClassicGame::ClassicGame() :
_timeline(std::make_unique<ClassicTimeline>()) _timeline(std::make_unique<ClassicTimeline>()),
_graphics_manager(std::make_unique<ClassicGraphicsManager>())
{ {
_keys_to_buttons = _keys_to_buttons =
{ {
@ -51,7 +53,7 @@ ClassicGame::~ClassicGame()
void ClassicGame::run() void ClassicGame::run()
{ {
_timeline->fetchVisibleNotes(_sprite_container); _timeline->fetchVisibleNotes(_graphics_manager);
_timeline->run(); _timeline->run();
} }
@ -100,7 +102,7 @@ Action ClassicGame::getActionKeyReleased(Button button) const
void ClassicGame::update() void ClassicGame::update()
{ {
_timeline->update(); _timeline->update();
_timeline->fetchVisibleNotes(_sprite_container); _timeline->fetchVisibleNotes(_graphics_manager);
} }
void ClassicGame::draw(sf::RenderWindow& window) const void ClassicGame::draw(sf::RenderWindow& window) const

View File

@ -8,6 +8,8 @@
#include "spritecontainer.h" #include "spritecontainer.h"
class ClassicTimeline; class ClassicTimeline;
class ClassicSprite;
class ClassicGraphicsManager;
class ClassicGame final : public Game class ClassicGame final : public Game
{ {
@ -30,7 +32,7 @@ private:
Action getActionKeyReleased(Button button) const; Action getActionKeyReleased(Button button) const;
std::unique_ptr<ClassicTimeline> _timeline; std::unique_ptr<ClassicTimeline> _timeline;
SpriteContainer _sprite_container; std::unique_ptr<ClassicGraphicsManager> _graphics_manager;
}; };
#endif // CLASSICGAME_H #endif // CLASSICGAME_H

View File

@ -0,0 +1,19 @@
#include "classicgraphicsmanager.h"
#include "classicnote.h"
ClassicGraphicsManager::ClassicGraphicsManager() :
_sprite_container({Action::PRESS_UP, Action::PRESS_DOWN,
Action::PRESS_LEFT, Action::PRESS_RIGHT},
std::make_unique<ClassicSpriteFactory>("VeraMono.ttf"))
{}
void ClassicGraphicsManager::initSprite(ClassicNote* note)
{
const auto action_type = note->action();
note->setSprite(_sprite_container.getSprite(action_type));
}
void ClassicGraphicsManager::resetSprite(ClassicNote* note)
{
_sprite_container.resetSprite(note->sprite(), note->action());
}

View File

@ -0,0 +1,20 @@
#pragma once
#include "classicactions.h"
#include "spritecontainer.h"
#include "classicspritefactory.h"
class ClassicSprite;
class ClassicNote;
class ClassicGraphicsManager
{
public:
explicit ClassicGraphicsManager();
void initSprite(ClassicNote* note);
void resetSprite(ClassicNote* note);
private:
SpriteContainer<Action, ClassicSpriteFactory, ClassicSprite> _sprite_container;
};

View File

@ -2,6 +2,7 @@
#include <SFML/Graphics/RenderTarget.hpp> #include <SFML/Graphics/RenderTarget.hpp>
ClassicSprite::ClassicSprite(const sf::RectangleShape& shape, const sf::Font& font) : ClassicSprite::ClassicSprite(const sf::RectangleShape& shape, const sf::Font& font) :
_prototype(shape),
_shape(shape), _shape(shape),
_trail(shape), _trail(shape),
_font(font) _font(font)
@ -22,6 +23,9 @@ void ClassicSprite::reset()
_trail.setPosition(0, 0); _trail.setPosition(0, 0);
_grade_text.setPosition(0, 0); _grade_text.setPosition(0, 0);
_grade_text.setFillColor(sf::Color(255, 255, 255, 0)); _grade_text.setFillColor(sf::Color(255, 255, 255, 0));
_shape = _prototype;
_trail = _prototype;
} }
void ClassicSprite::setCoordinates(float x, float y, float trail_x, float trail_y) noexcept void ClassicSprite::setCoordinates(float x, float y, float trail_x, float trail_y) noexcept
@ -45,6 +49,8 @@ void ClassicSprite::pulse()
{ {
_grade_text.setFillColor(sf::Color(255, 255, 255, 255)); _grade_text.setFillColor(sf::Color(255, 255, 255, 255));
_shape.setFillColor(sf::Color(140, 140, 140)); _shape.setFillColor(sf::Color(140, 140, 140));
_trail.setPosition(0, 0);
_trail.setFillColor(sf::Color(0, 0, 0, 0));
} }
void ClassicSprite::fade() void ClassicSprite::fade()
@ -54,7 +60,7 @@ void ClassicSprite::fade()
if (fill_color.a == 0) if (fill_color.a == 0)
return; return;
auto new_alpha = fill_color.a - 55; auto new_alpha = fill_color.a - 15;
fill_color.a = new_alpha < 0 ? 0 : new_alpha; fill_color.a = new_alpha < 0 ? 0 : new_alpha;
_grade_text.setFillColor(fill_color); _grade_text.setFillColor(fill_color);
@ -64,7 +70,7 @@ void ClassicSprite::fade()
if (fill_color.a == 0) if (fill_color.a == 0)
return; return;
new_alpha = fill_color.a - 55; new_alpha = fill_color.a - 15;
fill_color.a = new_alpha < 0 ? 0 : new_alpha; fill_color.a = new_alpha < 0 ? 0 : new_alpha;
_shape.setFillColor(fill_color); _shape.setFillColor(fill_color);

View File

@ -20,6 +20,8 @@ public:
bool isDead() const; bool isDead() const;
private: private:
sf::RectangleShape _prototype;
sf::RectangleShape _shape; sf::RectangleShape _shape;
sf::RectangleShape _trail; sf::RectangleShape _trail;
sf::Text _grade_text; sf::Text _grade_text;

View File

@ -0,0 +1,47 @@
#pragma once
#include <memory>
#include "classicinputtype.h"
#include "classicsprite.h"
class ClassicSpriteFactory
{
public:
inline ClassicSpriteFactory(const std::string& font_filename)
{
_font.loadFromFile(font_filename);
}
inline std::shared_ptr<ClassicSprite> create(Action action)
{
sf::RectangleShape sprite;
sprite.setSize({20.f, 20.f});
switch (action)
{
case Action::PRESS_UP:
sprite.setFillColor(sf::Color(255, 0, 0));
break;
case Action::PRESS_DOWN:
sprite.setFillColor(sf::Color(0, 255, 0));
break;
case Action::PRESS_LEFT:
sprite.setFillColor(sf::Color(0, 0, 255));
break;
case Action::PRESS_RIGHT:
sprite.setFillColor(sf::Color(255, 0, 255));
break;
default: // yellow
sprite.setFillColor(sf::Color(255, 239, 0));
}
return std::make_shared<ClassicSprite>(sprite, _font);
}
private:
sf::Font _font;
};

View File

@ -3,6 +3,7 @@
#include "classictimeline.h" #include "classictimeline.h"
#include "classicnote.h" #include "classicnote.h"
#include "spritecontainer.h" #include "spritecontainer.h"
#include "classicgraphicsmanager.h"
#include <SFML/Graphics/RenderTarget.hpp> #include <SFML/Graphics/RenderTarget.hpp>
@ -63,7 +64,7 @@ ClassicTimeline::~ClassicTimeline()
void ClassicTimeline::clear() void ClassicTimeline::clear()
{ {
for (auto note : _timeline) for (auto& note : _timeline)
delete note; delete note;
_timeline.clear(); _timeline.clear();
@ -74,23 +75,25 @@ void ClassicTimeline::update()
const microsec& offset = currentMusicOffset(); const microsec& offset = currentMusicOffset();
checkCurrentActiveNote(offset); checkCurrentActiveNote(offset);
checkForNextActiveNote(offset); checkForNextActiveNote(offset);
updateVisibleSprites(offset);
} }
void ClassicTimeline::checkCurrentActiveNote(const microsec &music_offset) void ClassicTimeline::checkCurrentActiveNote(const microsec& music_offset)
{ {
if (isExpired(_active_note)) if (isExpired(_active_note))
return; return;
auto note = *_active_note; auto note = *_active_note;
if (!note->isActive(music_offset)) if (note->state() != ClassicNote::State::FLYING || !note->isActive(music_offset))
{ {
note->setState(ClassicNote::State::DYING);
expire(_active_note); expire(_active_note);
++_top_note; ++_top_note;
} }
} }
void ClassicTimeline::checkForNextActiveNote(const microsec &music_offset) void ClassicTimeline::checkForNextActiveNote(const microsec& music_offset)
{ {
if (!isExpired(_active_note)) if (!isExpired(_active_note))
return; return;
@ -100,17 +103,29 @@ void ClassicTimeline::checkForNextActiveNote(const microsec &music_offset)
_active_note = _top_note; _active_note = _top_note;
} }
void ClassicTimeline::updateVisibleSprites(const microsec& music_offset)
{
if (nothingToDraw())
return;
std::for_each(_first_visible_note, _last_visible_note,
[&music_offset](const auto& note)
{
note->update(music_offset);
});
}
ClassicTimeline::Iterator ClassicTimeline::getActiveNote() noexcept ClassicTimeline::Iterator ClassicTimeline::getActiveNote() noexcept
{ {
return _active_note; return _active_note;
} }
bool ClassicTimeline::isExpired(const Iterator &iterator) const bool ClassicTimeline::isExpired(const Iterator& iterator) const
{ {
return iterator == _timeline.end(); return iterator == _timeline.end();
} }
void ClassicTimeline::expire(Iterator &iterator) void ClassicTimeline::expire(Iterator& iterator)
{ {
iterator = _timeline.end(); iterator = _timeline.end();
} }
@ -120,19 +135,19 @@ microsec ClassicTimeline::currentMusicOffset() const
return _music.getPlayingOffset().asMicroseconds(); return _music.getPlayingOffset().asMicroseconds();
} }
bool ClassicTimeline::isVisiblyClose(const Iterator &iterator, const microsec &music_offset) const bool ClassicTimeline::isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const
{ {
return ((*iterator)->offset() - _visibility_offset) <= music_offset; return ((*iterator)->offset() - _visibility_offset) <= music_offset;
} }
void ClassicTimeline::fetchVisibleNotes(SpriteContainer& sprite_container) void ClassicTimeline::fetchVisibleNotes(const std::unique_ptr<ClassicGraphicsManager>& graphics_manager)
{ {
const microsec music_offset = currentMusicOffset(); const microsec music_offset = currentMusicOffset();
initGraphicsForNewNotes(sprite_container, music_offset); initGraphicsForNewNotes(graphics_manager, music_offset);
discardGraphicsForDeadNotes(sprite_container); discardGraphicsForDeadNotes(graphics_manager);
} }
void ClassicTimeline::initGraphicsForNewNotes(SpriteContainer& sprite_container, const microsec &music_offset) void ClassicTimeline::initGraphicsForNewNotes(const std::unique_ptr<ClassicGraphicsManager>& graphics_manager, const microsec &music_offset)
{ {
Iterator note_iterator = _top_note; Iterator note_iterator = _top_note;
while (isVisiblyClose(note_iterator, music_offset)) while (isVisiblyClose(note_iterator, music_offset))
@ -142,20 +157,19 @@ void ClassicTimeline::initGraphicsForNewNotes(SpriteContainer& sprite_container,
auto note = *note_iterator; auto note = *note_iterator;
if (note->sprite()) if (!note->sprite())
continue; {
note->saveAppearanceTime(music_offset);
graphics_manager->initSprite(note);
}
note->saveAppearanceTime(music_offset); ++note_iterator;
const auto action_type = note->action();
const auto sprite = sprite_container.getSprite(action_type);
note->setSprite(sprite);
} }
_last_visible_note = note_iterator; _last_visible_note = note_iterator;
} }
void ClassicTimeline::discardGraphicsForDeadNotes(SpriteContainer &sprite_container) void ClassicTimeline::discardGraphicsForDeadNotes(const std::unique_ptr<ClassicGraphicsManager>& graphics_manager)
{ {
if (nothingToDraw()) if (nothingToDraw())
return; return;
@ -167,11 +181,12 @@ void ClassicTimeline::discardGraphicsForDeadNotes(SpriteContainer &sprite_contai
if (note->state() == ClassicNote::State::DEAD) if (note->state() == ClassicNote::State::DEAD)
{ {
note->setState(ClassicNote::State::NONE); note->setState(ClassicNote::State::NONE);
const auto action_type = note->action(); graphics_manager->resetSprite(note);
sprite_container.resetSprite(note->sprite(), action_type);
note->setSprite(nullptr);
++_first_visible_note; ++_first_visible_note;
} }
++note_iterator;
} }
} }
@ -191,3 +206,4 @@ void ClassicTimeline::drawVisibleNotes(sf::RenderWindow &window) const
window.draw(*note); window.draw(*note);
}); });
} }

View File

@ -5,8 +5,8 @@
#include "timeline.h" #include "timeline.h"
#include <SFML/Audio/Music.hpp> #include <SFML/Audio/Music.hpp>
class ClassicGraphicsManager;
class ClassicNote; class ClassicNote;
class SpriteContainer;
class ClassicTimeline : public Timeline class ClassicTimeline : public Timeline
{ {
@ -20,9 +20,9 @@ public:
virtual microsec currentMusicOffset() const override; virtual microsec currentMusicOffset() const override;
virtual void drawVisibleNotes(sf::RenderWindow& window) const override; virtual void drawVisibleNotes(sf::RenderWindow& window) const override;
void fetchVisibleNotes(SpriteContainer& sprite_container); void fetchVisibleNotes(const std::unique_ptr<ClassicGraphicsManager>& graphics_manager);
void initGraphicsForNewNotes(SpriteContainer& sprite_container, const microsec& music_offset); void initGraphicsForNewNotes(const std::unique_ptr<ClassicGraphicsManager>& graphics_manager, const microsec& music_offset);
void discardGraphicsForDeadNotes(SpriteContainer& sprite_container); void discardGraphicsForDeadNotes(const std::unique_ptr<ClassicGraphicsManager>& graphics_manager);
using Iterator = std::vector<ClassicNote*>::const_iterator; using Iterator = std::vector<ClassicNote*>::const_iterator;
@ -42,8 +42,9 @@ private:
sf::Music _music; sf::Music _music;
void checkCurrentActiveNote(const microsec &music_offset); void updateVisibleSprites(const microsec& music_offset);
void checkForNextActiveNote(const microsec &music_offset); void checkCurrentActiveNote(const microsec& music_offset);
void checkForNextActiveNote(const microsec& music_offset);
bool isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const; bool isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const;
bool nothingToDraw() const noexcept; bool nothingToDraw() const noexcept;

View File

@ -1,78 +0,0 @@
#include "spritecontainer.h"
#include "classicsprite.h"
#include <SFML/Graphics/RectangleShape.hpp>
#include <iostream>
static constexpr std::size_t RESERVED_SIZE = 20;
SpriteContainer::SpriteContainer()
{
for (auto kind_of_action : {Action::PRESS_UP, Action::PRESS_DOWN,
Action::PRESS_LEFT, Action::PRESS_RIGHT})
{
reallocatePoll(kind_of_action);
}
std::cout << "Sprite poll reallocated.\n";
_font.loadFromFile("VeraMono.ttf");
}
void SpriteContainer::reallocatePoll(Action kind_of_action)
{
SpritePoll &poll = _sprite_dispatcher[kind_of_action];
for (std::size_t i = 0; i < RESERVED_SIZE; ++i)
{
poll.push(createSprite(kind_of_action));
}
}
std::shared_ptr<ClassicSprite> SpriteContainer::createSprite(Action kind_of_action) const
{
sf::RectangleShape sprite;
sprite.setSize({20.f, 20.f});
switch (kind_of_action)
{
case Action::PRESS_UP:
sprite.setFillColor(sf::Color(255, 0, 0));
break;
case Action::PRESS_DOWN:
sprite.setFillColor(sf::Color(0, 255, 0));
break;
case Action::PRESS_LEFT:
sprite.setFillColor(sf::Color(0, 0, 255));
break;
case Action::PRESS_RIGHT:
sprite.setFillColor(sf::Color(255, 0, 255));
break;
default: // yellow
sprite.setFillColor(sf::Color(255, 239, 0));
}
return std::make_shared<ClassicSprite>(sprite, _font);
}
std::shared_ptr<ClassicSprite> SpriteContainer::getSprite(Action action)
{
SpritePoll& poll = _sprite_dispatcher.at(action);
if (poll.empty())
reallocatePoll(action);
std::shared_ptr<ClassicSprite> sprite = poll.top();
poll.pop();
std::cout << "Taking a sprite from poll.\n";
return sprite;
}
void SpriteContainer::resetSprite(const std::shared_ptr<ClassicSprite> &sprite, Action action)
{
_sprite_dispatcher[action].push(sprite);
std::cout << "Returning a sprite to poll.\n";
}

View File

@ -1,31 +0,0 @@
#pragma once
#include "classicactions.h"
#include <memory>
#include <stack>
#include <SFML/Graphics/Font.hpp>
class ClassicSprite;
class ClassicNote;
/* Move Sprite initialization into factory
* and make this class template */
class SpriteContainer
{
public:
explicit SpriteContainer();
std::shared_ptr<ClassicSprite> getSprite(Action action);
void resetSprite(const std::shared_ptr<ClassicSprite> &sprite, Action action);
private:
void reallocatePoll(Action kind_of_action);
std::shared_ptr<ClassicSprite> createSprite(Action kind_of_action) const;
using SpritePoll = std::stack<std::shared_ptr<ClassicSprite>>;
std::map<Action, SpritePoll> _sprite_dispatcher;
sf::Font _font;
};