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_SLIDER_RIGHT, RELEASE_SLIDER_RIGHT,
PRESS_SLIDER_LEFT, RELEASE_SLIDER_LEFT
PRESS_SLIDER_LEFT, RELEASE_SLIDER_LEFT,
COUNT
};
enum class Button

View File

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

View File

@ -8,6 +8,8 @@
#include "spritecontainer.h"
class ClassicTimeline;
class ClassicSprite;
class ClassicGraphicsManager;
class ClassicGame final : public Game
{
@ -30,7 +32,7 @@ private:
Action getActionKeyReleased(Button button) const;
std::unique_ptr<ClassicTimeline> _timeline;
SpriteContainer _sprite_container;
std::unique_ptr<ClassicGraphicsManager> _graphics_manager;
};
#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>
ClassicSprite::ClassicSprite(const sf::RectangleShape& shape, const sf::Font& font) :
_prototype(shape),
_shape(shape),
_trail(shape),
_font(font)
@ -22,6 +23,9 @@ void ClassicSprite::reset()
_trail.setPosition(0, 0);
_grade_text.setPosition(0, 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
@ -45,6 +49,8 @@ void ClassicSprite::pulse()
{
_grade_text.setFillColor(sf::Color(255, 255, 255, 255));
_shape.setFillColor(sf::Color(140, 140, 140));
_trail.setPosition(0, 0);
_trail.setFillColor(sf::Color(0, 0, 0, 0));
}
void ClassicSprite::fade()
@ -54,7 +60,7 @@ void ClassicSprite::fade()
if (fill_color.a == 0)
return;
auto new_alpha = fill_color.a - 55;
auto new_alpha = fill_color.a - 15;
fill_color.a = new_alpha < 0 ? 0 : new_alpha;
_grade_text.setFillColor(fill_color);
@ -64,7 +70,7 @@ void ClassicSprite::fade()
if (fill_color.a == 0)
return;
new_alpha = fill_color.a - 55;
new_alpha = fill_color.a - 15;
fill_color.a = new_alpha < 0 ? 0 : new_alpha;
_shape.setFillColor(fill_color);

View File

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

View File

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