Implemented rotation for sprites, among other things
This commit is contained in:
parent
24963b2d0a
commit
e8319fd6b9
20 changed files with 301 additions and 76 deletions
|
@ -137,6 +137,9 @@ set(COORDINATES_00_SOURCES
|
||||||
src/coordinates/translated_coordinates.h
|
src/coordinates/translated_coordinates.h
|
||||||
src/coordinates/translated_coordinates.cpp)
|
src/coordinates/translated_coordinates.cpp)
|
||||||
|
|
||||||
|
set(MINIMAP_00_SOURCES
|
||||||
|
src/prototypes/minimap_00.cpp)
|
||||||
|
|
||||||
# Add an executable target
|
# Add an executable target
|
||||||
add_executable(Holesome ${SOURCES})
|
add_executable(Holesome ${SOURCES})
|
||||||
add_executable(Physics_00 ${PHYSICS_00_SOURCES})
|
add_executable(Physics_00 ${PHYSICS_00_SOURCES})
|
||||||
|
@ -145,6 +148,8 @@ add_executable(Math_00 ${MATH_00_SOURCES})
|
||||||
|
|
||||||
add_executable(Coordinates_00 ${COORDINATES_00_SOURCES})
|
add_executable(Coordinates_00 ${COORDINATES_00_SOURCES})
|
||||||
|
|
||||||
|
add_executable(Minimap_00 ${MINIMAP_00_SOURCES})
|
||||||
|
|
||||||
# Link SFML and other libraries to your executable target
|
# Link SFML and other libraries to your executable target
|
||||||
target_link_libraries(Holesome sfml-graphics sfml-audio)
|
target_link_libraries(Holesome sfml-graphics sfml-audio)
|
||||||
target_link_libraries(Holesome Eigen3::Eigen)
|
target_link_libraries(Holesome Eigen3::Eigen)
|
||||||
|
@ -156,6 +161,8 @@ target_link_libraries(Math_00 Eigen3::Eigen)
|
||||||
|
|
||||||
target_link_libraries(Coordinates_00 Eigen3::Eigen)
|
target_link_libraries(Coordinates_00 Eigen3::Eigen)
|
||||||
|
|
||||||
|
target_link_libraries(Minimap_00 sfml-graphics sfml-audio)
|
||||||
|
|
||||||
# Assets
|
# Assets
|
||||||
add_custom_target(copy_assets
|
add_custom_target(copy_assets
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/assets ${CMAKE_CURRENT_BINARY_DIR}/assets
|
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/assets ${CMAKE_CURRENT_BINARY_DIR}/assets
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#define PLAYER_RADIUS_PER_LEVEL 0.25f
|
#define PLAYER_RADIUS_PER_LEVEL 0.25f
|
||||||
|
|
||||||
// World
|
// World
|
||||||
#define WORLD_GRAVITY b2Vec2(0.f, 0.f)
|
#define WORLD_GRAVITY b2Vec2(0.f, -9.8f)
|
||||||
#define SKY_HEIGHT_SCALE 2.f
|
#define SKY_HEIGHT_SCALE 2.f
|
||||||
|
|
||||||
// FPS
|
// FPS
|
||||||
|
|
|
@ -101,3 +101,11 @@ TranslatedCoordinates::TranslatedCoordinates(DiagonalWorldCoordinates diagonalWo
|
||||||
{
|
{
|
||||||
setDiagonal(diagonalWorldCoordinates);
|
setDiagonal(diagonalWorldCoordinates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TranslatedCoordinates::removeParent()
|
||||||
|
{
|
||||||
|
// Uncomment, if global position should be preserved
|
||||||
|
// auto globalWorldCoordinates = this->world();
|
||||||
|
// this->worldCoordinates = globalWorldCoordinates;
|
||||||
|
this->parent = nullptr;
|
||||||
|
}
|
||||||
|
|
|
@ -36,6 +36,8 @@ public:
|
||||||
|
|
||||||
void setParent(std::shared_ptr<TranslatedCoordinates> parent, WorldCoordinates offset = {0, 0});
|
void setParent(std::shared_ptr<TranslatedCoordinates> parent, WorldCoordinates offset = {0, 0});
|
||||||
|
|
||||||
|
void removeParent();
|
||||||
|
|
||||||
void setWorldOffset(WorldCoordinates offset);
|
void setWorldOffset(WorldCoordinates offset);
|
||||||
|
|
||||||
void setScreenOffset(IsometricCoordinates offset);
|
void setScreenOffset(IsometricCoordinates offset);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "collectable.hpp"
|
#include "collectable.hpp"
|
||||||
#include "../../sprites/versatile_sprite.hpp"
|
#include "../../sprites/versatile_sprite.hpp"
|
||||||
#include "../../config.h"
|
#include "../../config.h"
|
||||||
|
#include "../input/input_mapper.h"
|
||||||
|
|
||||||
Collectable::Collectable()
|
Collectable::Collectable()
|
||||||
{
|
{
|
||||||
|
@ -10,7 +11,9 @@ Collectable::Collectable()
|
||||||
|
|
||||||
void Collectable::setRotation(float angle)
|
void Collectable::setRotation(float angle)
|
||||||
{
|
{
|
||||||
|
// Assume that the sprite is the first child
|
||||||
|
auto sprite = std::dynamic_pointer_cast<VersatileSprite>(getChildren()[0]);
|
||||||
|
sprite->setRotation(angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Collectable::getDepth() const
|
float Collectable::getDepth() const
|
||||||
|
@ -29,3 +32,28 @@ void Collectable::setSprite(const std::string &spriteName)
|
||||||
// Set half size offset of coordinates
|
// Set half size offset of coordinates
|
||||||
coordinates->move(IsometricCoordinates(0, -sizeWidth / 2.f, 0));
|
coordinates->move(IsometricCoordinates(0, -sizeWidth / 2.f, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Collectable::update()
|
||||||
|
{
|
||||||
|
GameObject::update();
|
||||||
|
|
||||||
|
// TODO: Remove this
|
||||||
|
if (InputMapper::getInstance()->getInputIdentities().empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<InputIdentity> inputIdentity = InputMapper::getInstance()->getInputIdentity(
|
||||||
|
InputDeviceGroup::KEYBOARD_WASD);
|
||||||
|
|
||||||
|
// Rotate based on grow/shrink input
|
||||||
|
if (inputIdentity->isPerformingAction(GameAction::GROW))
|
||||||
|
{
|
||||||
|
angle += 10.f;
|
||||||
|
} else if (inputIdentity->isPerformingAction(GameAction::SHRINK))
|
||||||
|
{
|
||||||
|
angle -= 10.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
setRotation(angle);
|
||||||
|
}
|
||||||
|
|
|
@ -13,16 +13,20 @@ public:
|
||||||
|
|
||||||
void setRotation(float angle);
|
void setRotation(float angle);
|
||||||
|
|
||||||
|
void update() override;
|
||||||
|
|
||||||
float getDepth() const;
|
float getDepth() const;
|
||||||
|
|
||||||
int getId() const
|
int getId() const
|
||||||
{
|
{
|
||||||
return collectableId;
|
return collectableId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int collectableId = 0;
|
int collectableId = 0;
|
||||||
static inline int collectableCount = 0;
|
static inline int collectableCount = 0;
|
||||||
|
|
||||||
|
float angle = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -30,17 +30,18 @@ void CollectablesCollection::createEmpty(int maxDepth)
|
||||||
void CollectablesCollection::remove(const std::shared_ptr<Collectable> &collectable)
|
void CollectablesCollection::remove(const std::shared_ptr<Collectable> &collectable)
|
||||||
{
|
{
|
||||||
depthCollections[collectable->getDepth()]->remove(collectable);
|
depthCollections[collectable->getDepth()]->remove(collectable);
|
||||||
|
removeChild(collectable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectablesCollection::add(const std::shared_ptr<Collectable> &collectable)
|
void CollectablesCollection::add(const std::shared_ptr<Collectable> &collectable)
|
||||||
{
|
{
|
||||||
depthCollections[collectable->getDepth()]->add(collectable);
|
depthCollections[collectable->getDepth()]->add(collectable);
|
||||||
|
addDetachedChild(collectable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectablesCollection::update()
|
void CollectablesCollection::update()
|
||||||
{
|
{
|
||||||
updateCollectables();
|
GameObject::update();
|
||||||
|
|
||||||
// Move collectables to new depth collections if necessary
|
// Move collectables to new depth collections if necessary
|
||||||
|
|
||||||
|
@ -57,22 +58,27 @@ void CollectablesCollection::update()
|
||||||
for (auto &collectable: collectables)
|
for (auto &collectable: collectables)
|
||||||
{
|
{
|
||||||
int newDepth = std::floor(collectable->getDepth());
|
int newDepth = std::floor(collectable->getDepth());
|
||||||
if (newDepth != depth)
|
if (newDepth == depth)
|
||||||
{
|
{
|
||||||
if (newDepth < 0 || newDepth >= (int) depthCollections.size())
|
continue;
|
||||||
{
|
|
||||||
LOG(ERROR) << "Collectable " << collectable->getId() << " has invalid depth " << newDepth
|
|
||||||
<< "! Keeping it at previous depth " << depth << " ...";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
depthCollection->remove(collectable);
|
|
||||||
depthCollections[newDepth]->add(collectable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isValidDepth(newDepth))
|
||||||
|
{
|
||||||
|
LOG(ERROR) << "Collectable " << collectable->getId() << " has invalid depth " << newDepth
|
||||||
|
<< "! Keeping it at previous depth " << depth << " ...";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
depthCollection->remove(collectable);
|
||||||
|
depthCollections[newDepth]->add(collectable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CollectablesCollection::isValidDepth(int desiredDepth) const
|
||||||
|
{ return desiredDepth >= 0 && desiredDepth < (int) depthCollections.size(); }
|
||||||
|
|
||||||
void CollectablesCollection::draw(sf::RenderWindow *window)
|
void CollectablesCollection::draw(sf::RenderWindow *window)
|
||||||
{
|
{
|
||||||
// Render collectables in reverse order of depth
|
// Render collectables in reverse order of depth
|
||||||
|
@ -86,25 +92,3 @@ void CollectablesCollection::draw(sf::RenderWindow *window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectablesCollection::updateCollectables()
|
|
||||||
{
|
|
||||||
for (auto &[depth, depthCollection]: depthCollections)
|
|
||||||
{
|
|
||||||
for (auto &collectable: depthCollection->collectables)
|
|
||||||
{
|
|
||||||
collectable->update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<CollectablesDepthCollection> CollectablesCollection::getDepthCollection(int depth)
|
|
||||||
{
|
|
||||||
if (depthCollections.find(depth) == depthCollections.end())
|
|
||||||
{
|
|
||||||
LOG(ERROR) << "Depth collection for depth " << depth << " does not exist! Returning empty collection ...";
|
|
||||||
return std::make_shared<CollectablesDepthCollection>(depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
return depthCollections[depth];
|
|
||||||
}
|
|
||||||
|
|
|
@ -23,14 +23,12 @@ public:
|
||||||
void add(const std::shared_ptr<Collectable>& collectable);
|
void add(const std::shared_ptr<Collectable>& collectable);
|
||||||
void remove(const std::shared_ptr<Collectable>& collectable);
|
void remove(const std::shared_ptr<Collectable>& collectable);
|
||||||
|
|
||||||
std::shared_ptr<CollectablesDepthCollection> getDepthCollection(int depth);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static inline std::shared_ptr<CollectablesCollection> singletonInstance = nullptr;
|
static inline std::shared_ptr<CollectablesCollection> singletonInstance = nullptr;
|
||||||
|
|
||||||
std::map<int, std::shared_ptr<CollectablesDepthCollection>> depthCollections = {};
|
std::map<int, std::shared_ptr<CollectablesDepthCollection>> depthCollections = {};
|
||||||
|
|
||||||
void updateCollectables();
|
bool isValidDepth(int desiredDepth) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,14 @@ void Game::update()
|
||||||
}
|
}
|
||||||
|
|
||||||
InputMapper::getInstance()->updateIdentities();
|
InputMapper::getInstance()->updateIdentities();
|
||||||
|
|
||||||
|
for (const auto &gameObject: gameObjects)
|
||||||
|
{
|
||||||
|
if (gameObject->getActive())
|
||||||
|
{
|
||||||
|
gameObject->preRenderUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Game> Game::getInstance()
|
std::shared_ptr<Game> Game::getInstance()
|
||||||
|
|
|
@ -64,6 +64,7 @@ void GameObject::removeChild(const std::shared_ptr<GameObject> &child)
|
||||||
if (it != children.end())
|
if (it != children.end())
|
||||||
{
|
{
|
||||||
children.erase(it);
|
children.erase(it);
|
||||||
|
child->coordinates->removeParent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,3 +85,11 @@ void GameObject::addDetachedChild(const std::shared_ptr<GameObject> &child)
|
||||||
{
|
{
|
||||||
children.push_back(child);
|
children.push_back(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameObject::preRenderUpdate()
|
||||||
|
{
|
||||||
|
for (auto &child: children)
|
||||||
|
{
|
||||||
|
child->preRenderUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ public:
|
||||||
|
|
||||||
virtual void physicsUpdate();
|
virtual void physicsUpdate();
|
||||||
|
|
||||||
|
virtual void preRenderUpdate();
|
||||||
|
|
||||||
void setActive(bool active);
|
void setActive(bool active);
|
||||||
bool getActive() const { return isActive; }
|
bool getActive() const { return isActive; }
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ void CollectableSimulation::physicsUpdate()
|
||||||
{
|
{
|
||||||
updateGroundHole();
|
updateGroundHole();
|
||||||
|
|
||||||
// world->Step(FRAME_TIME.asSeconds(), COLLECTABLES_SIM_VELOCITY_ITERATIONS, COLLECTABLES_SIM_POSITION_ITERATIONS);
|
world->Step(FRAME_TIME.asSeconds(), COLLECTABLES_SIM_VELOCITY_ITERATIONS, COLLECTABLES_SIM_POSITION_ITERATIONS);
|
||||||
|
|
||||||
updateCollectable();
|
updateCollectable();
|
||||||
}
|
}
|
||||||
|
|
72
src/prototypes/minimap_00.cpp
Normal file
72
src/prototypes/minimap_00.cpp
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#include <SFML/Graphics/RenderWindow.hpp>
|
||||||
|
#include <SFML/Window/Event.hpp>
|
||||||
|
#include <SFML/Graphics/CircleShape.hpp>
|
||||||
|
#include <SFML/Graphics/RectangleShape.hpp>
|
||||||
|
|
||||||
|
void drawMainContent(sf::RenderWindow *window)
|
||||||
|
{
|
||||||
|
sf::RectangleShape rectangle(sf::Vector2f(800, 600));
|
||||||
|
rectangle.setFillColor(sf::Color::Green);
|
||||||
|
rectangle.setPosition(0, 0);
|
||||||
|
window->draw(rectangle);
|
||||||
|
|
||||||
|
sf::CircleShape circle(50);
|
||||||
|
circle.setFillColor(sf::Color::Red);
|
||||||
|
circle.setPosition(150, 150);
|
||||||
|
window->draw(circle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawMiniMapContent(sf::RenderWindow *window)
|
||||||
|
{
|
||||||
|
sf::RectangleShape rectangle(sf::Vector2f(196, 196));
|
||||||
|
rectangle.setFillColor(sf::Color::White);
|
||||||
|
rectangle.setPosition(2, 2);
|
||||||
|
rectangle.setOutlineColor(sf::Color::Black);
|
||||||
|
rectangle.setOutlineThickness(2);
|
||||||
|
window->draw(rectangle);
|
||||||
|
|
||||||
|
sf::CircleShape circle(20);
|
||||||
|
circle.setFillColor(sf::Color::Blue);
|
||||||
|
circle.setPosition(80, 80);
|
||||||
|
window->draw(circle);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
// Create simple window
|
||||||
|
sf::RenderWindow window(sf::VideoMode(800, 600), "Minimap example");
|
||||||
|
|
||||||
|
// Create minimap view
|
||||||
|
sf::View minimapView(sf::FloatRect(0, 0, 200, 200));
|
||||||
|
// Set viewport to the top right corner
|
||||||
|
minimapView.setViewport(sf::FloatRect(0.75f, 0, 0.25f, 0.25f));
|
||||||
|
|
||||||
|
while (window.isOpen()) {
|
||||||
|
window.clear(sf::Color::Black);
|
||||||
|
|
||||||
|
// Draw main content
|
||||||
|
auto fullView = window.getDefaultView();
|
||||||
|
window.setView(fullView);
|
||||||
|
|
||||||
|
drawMainContent(&window);
|
||||||
|
|
||||||
|
|
||||||
|
// Draw minimap
|
||||||
|
window.setView(minimapView);
|
||||||
|
|
||||||
|
drawMiniMapContent(&window);
|
||||||
|
|
||||||
|
|
||||||
|
// Finished
|
||||||
|
window.display();
|
||||||
|
|
||||||
|
// Handle close event
|
||||||
|
sf::Event event{};
|
||||||
|
while (window.pollEvent(event)) {
|
||||||
|
if (event.type == sf::Event::Closed) {
|
||||||
|
window.close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,12 @@
|
||||||
|
|
||||||
AnimatedSprite::AnimatedSprite(const std::vector<sf::Sprite> &sprites, const sf::Vector2f &size)
|
AnimatedSprite::AnimatedSprite(const std::vector<sf::Sprite> &sprites, const sf::Vector2f &size)
|
||||||
{
|
{
|
||||||
this->sprites = sprites;
|
// Create a copy of the sprites as pointers
|
||||||
|
for (auto &sprite: sprites)
|
||||||
|
{
|
||||||
|
this->sprites.push_back(std::make_shared<sf::Sprite>(sprite));
|
||||||
|
}
|
||||||
|
|
||||||
setSize(size);
|
setSize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,27 +30,50 @@ void AnimatedSprite::update()
|
||||||
void AnimatedSprite::draw(sf::RenderWindow *window)
|
void AnimatedSprite::draw(sf::RenderWindow *window)
|
||||||
{
|
{
|
||||||
auto currentSprite = sprites[currentFrame];
|
auto currentSprite = sprites[currentFrame];
|
||||||
currentSprite.setPosition(coordinates->isometric().toScreen());
|
currentSprite->setPosition(renderPosition);
|
||||||
|
|
||||||
window->draw(currentSprite);
|
window->draw(*currentSprite);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimatedSprite::setSize(const sf::Vector2f &size)
|
void AnimatedSprite::setSize(const sf::Vector2f &size)
|
||||||
{
|
{
|
||||||
for (auto &sprite: sprites)
|
for (auto &sprite: sprites)
|
||||||
{
|
{
|
||||||
sprite.setScale(size.x / sprite.getTextureRect().width, size.y / sprite.getTextureRect().height);
|
sprite->setScale(size.x / sprite->getTextureRect().width, size.y / sprite->getTextureRect().height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sf::Vector2f AnimatedSprite::getSize() const
|
sf::Vector2f AnimatedSprite::getSize() const
|
||||||
{
|
{
|
||||||
auto scale = sprites[currentFrame].getScale();
|
auto scale = sprites[currentFrame]->getScale();
|
||||||
auto textureSize = sprites[currentFrame].getTextureRect();
|
auto textureSize = sprites[currentFrame]->getTextureRect();
|
||||||
return {textureSize.width * scale.x, textureSize.height * scale.y};
|
return {textureSize.width * scale.x, textureSize.height * scale.y};
|
||||||
}
|
}
|
||||||
|
|
||||||
sf::Sprite AnimatedSprite::getSprite() const
|
sf::Sprite AnimatedSprite::getSprite() const
|
||||||
{
|
{
|
||||||
return sprites[currentFrame];
|
return *sprites[currentFrame];
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimatedSprite::setRotation(float angle)
|
||||||
|
{
|
||||||
|
this->angle = restrictAngle(angle);
|
||||||
|
|
||||||
|
for (auto &sprite: sprites)
|
||||||
|
{
|
||||||
|
sprite->setRotation(angle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimatedSprite::preRenderUpdate()
|
||||||
|
{
|
||||||
|
if (angle == 0)
|
||||||
|
{
|
||||||
|
renderPosition = coordinates->isometric().toScreen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto position = coordinates->isometric().toScreen();
|
||||||
|
|
||||||
|
renderPosition = calculateRotatedCornerPosition(position, angle);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,17 @@ public:
|
||||||
|
|
||||||
sf::Sprite getSprite() const override;
|
sf::Sprite getSprite() const override;
|
||||||
|
|
||||||
|
void preRenderUpdate() override;
|
||||||
|
|
||||||
|
void setRotation(float angle) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int currentFrame = 0;
|
int currentFrame = 0;
|
||||||
sf::Time timeSinceLastFrame = sf::Time::Zero;
|
sf::Time timeSinceLastFrame = sf::Time::Zero;
|
||||||
std::vector<sf::Sprite> sprites;
|
std::vector<std::shared_ptr<sf::Sprite>> sprites;
|
||||||
|
|
||||||
|
sf::Vector2f renderPosition;
|
||||||
|
float angle = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ SingleSprite::SingleSprite(const std::shared_ptr<sf::Texture> &texture, const sf
|
||||||
|
|
||||||
void SingleSprite::draw(sf::RenderWindow *window)
|
void SingleSprite::draw(sf::RenderWindow *window)
|
||||||
{
|
{
|
||||||
sprite.setPosition(coordinates->isometric().toScreen());
|
sprite.setPosition(renderPosition);
|
||||||
window->draw(sprite);
|
window->draw(sprite);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,3 +42,22 @@ sf::Sprite SingleSprite::getSprite() const
|
||||||
{
|
{
|
||||||
return sprite;
|
return sprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SingleSprite::setRotation(float angle)
|
||||||
|
{
|
||||||
|
this->angle = restrictAngle(angle);
|
||||||
|
sprite.setRotation(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleSprite::preRenderUpdate()
|
||||||
|
{
|
||||||
|
if (angle == 0)
|
||||||
|
{
|
||||||
|
renderPosition = coordinates->isometric().toScreen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto position = coordinates->isometric().toScreen();
|
||||||
|
|
||||||
|
renderPosition = calculateRotatedCornerPosition(position, angle);
|
||||||
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
class SingleSprite : public GameObject, public Sprite
|
class SingleSprite : public GameObject, public Sprite
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SingleSprite(const sf::Sprite &sprite, const sf::Vector2f &size = sf::Vector2f(0, 0));
|
explicit SingleSprite(const sf::Sprite &sprite, const sf::Vector2f &size = sf::Vector2f(0, 0));
|
||||||
|
|
||||||
SingleSprite(const std::shared_ptr<sf::Texture> &texture, const sf::Vector2f &size = sf::Vector2f(0, 0));
|
explicit SingleSprite(const std::shared_ptr<sf::Texture> &texture, const sf::Vector2f &size = sf::Vector2f(0, 0));
|
||||||
|
|
||||||
void draw(sf::RenderWindow *window) override;
|
void draw(sf::RenderWindow *window) override;
|
||||||
|
|
||||||
|
@ -21,8 +21,14 @@ public:
|
||||||
|
|
||||||
sf::Sprite getSprite() const override;
|
sf::Sprite getSprite() const override;
|
||||||
|
|
||||||
|
void setRotation(float angle) override;
|
||||||
|
|
||||||
|
void preRenderUpdate() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
sf::Sprite sprite;
|
sf::Sprite sprite;
|
||||||
|
float angle = 0;
|
||||||
|
sf::Vector2f renderPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <SFML/Graphics/Sprite.hpp>
|
#include <SFML/Graphics/Sprite.hpp>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
class Sprite
|
class Sprite
|
||||||
{
|
{
|
||||||
|
@ -23,6 +24,52 @@ public:
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Sprite::getSprite() not implemented");
|
throw std::runtime_error("Sprite::getSprite() not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotates a sprite around its center.
|
||||||
|
* @param angle [0, 360]
|
||||||
|
*/
|
||||||
|
virtual void setRotation(float angle)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Sprite::setRotation() not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static float restrictAngle(float angle)
|
||||||
|
{
|
||||||
|
float const MAX_ANGLE = 360.f;
|
||||||
|
angle = std::fmod(angle, MAX_ANGLE); // Get the remainder after dividing by 360
|
||||||
|
|
||||||
|
if (angle < 0.0f)
|
||||||
|
{
|
||||||
|
float numberOfTimes = std::ceil(std::abs(angle) / MAX_ANGLE);
|
||||||
|
angle += MAX_ANGLE * numberOfTimes; // Convert negative angles to positive
|
||||||
|
}
|
||||||
|
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] sf::Vector2f
|
||||||
|
calculateRotatedCornerPosition(sf::Vector2f spriteScreenPosition, float angle) const
|
||||||
|
{
|
||||||
|
angle = restrictAngle(angle);
|
||||||
|
|
||||||
|
auto spriteSize = getSize();
|
||||||
|
|
||||||
|
// Calculate the center point of the sprite
|
||||||
|
sf::Vector2f center(spriteScreenPosition.x + spriteSize.x / 2, spriteScreenPosition.y + spriteSize.y / 2);
|
||||||
|
|
||||||
|
// Convert the angle to radians
|
||||||
|
float angle_rad = angle * (M_PI / 180.0f);
|
||||||
|
|
||||||
|
// Calculate the rotated coordinates of the top left corner
|
||||||
|
float new_x = center.x + (spriteScreenPosition.x - center.x) * std::cos(angle_rad) -
|
||||||
|
(spriteScreenPosition.y - center.y) * std::sin(angle_rad);
|
||||||
|
float new_y = center.y + (spriteScreenPosition.x - center.x) * std::sin(angle_rad) +
|
||||||
|
(spriteScreenPosition.y - center.y) * std::cos(angle_rad);
|
||||||
|
|
||||||
|
return {new_x, new_y};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //HOLESOME_SPRITE_HPP
|
#endif //HOLESOME_SPRITE_HPP
|
||||||
|
|
|
@ -22,42 +22,34 @@ VersatileSprite::VersatileSprite(const std::string &name, sf::Vector2f size)
|
||||||
|
|
||||||
void VersatileSprite::setSize(const sf::Vector2f &size)
|
void VersatileSprite::setSize(const sf::Vector2f &size)
|
||||||
{
|
{
|
||||||
if (singleSprite != nullptr)
|
getUsedSpritePtr()->setSize(size);
|
||||||
{
|
|
||||||
singleSprite->setSize(size);
|
|
||||||
} else if (animatedSprite != nullptr)
|
|
||||||
{
|
|
||||||
animatedSprite->setSize(size);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
Sprite::setSize(size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sf::Vector2f VersatileSprite::getSize() const
|
sf::Vector2f VersatileSprite::getSize() const
|
||||||
{
|
{
|
||||||
if (singleSprite != nullptr)
|
return getUsedSpritePtr()->getSize();
|
||||||
{
|
|
||||||
return singleSprite->getSize();
|
|
||||||
} else if (animatedSprite != nullptr)
|
|
||||||
{
|
|
||||||
return animatedSprite->getSize();
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
return Sprite::getSize();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sf::Sprite VersatileSprite::getSprite() const
|
sf::Sprite VersatileSprite::getSprite() const
|
||||||
|
{
|
||||||
|
return getUsedSpritePtr()->getSprite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VersatileSprite::setRotation(float angle)
|
||||||
|
{
|
||||||
|
getUsedSpritePtr()->setRotation(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Sprite> VersatileSprite::getUsedSpritePtr() const
|
||||||
{
|
{
|
||||||
if (singleSprite != nullptr)
|
if (singleSprite != nullptr)
|
||||||
{
|
{
|
||||||
return singleSprite->getSprite();
|
return singleSprite;
|
||||||
} else if (animatedSprite != nullptr)
|
} else if (animatedSprite != nullptr)
|
||||||
{
|
{
|
||||||
return animatedSprite->getSprite();
|
return animatedSprite;
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
return {};
|
throw std::runtime_error("Versatile sprite has no sprite");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,15 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] sf::Vector2f getSize() const override;
|
[[nodiscard]] sf::Vector2f getSize() const override;
|
||||||
|
|
||||||
sf::Sprite getSprite() const override;
|
[[nodiscard]] sf::Sprite getSprite() const override;
|
||||||
|
|
||||||
|
void setRotation(float angle) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<SingleSprite> singleSprite = nullptr;
|
std::shared_ptr<SingleSprite> singleSprite = nullptr;
|
||||||
std::shared_ptr<AnimatedSprite> animatedSprite = nullptr;
|
std::shared_ptr<AnimatedSprite> animatedSprite = nullptr;
|
||||||
|
|
||||||
|
std::shared_ptr<Sprite> getUsedSpritePtr() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue