diff --git a/CMakeLists.txt b/CMakeLists.txt index 913b59d..fec1ebb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,9 @@ set(COORDINATES_00_SOURCES src/coordinates/translated_coordinates.h src/coordinates/translated_coordinates.cpp) +set(MINIMAP_00_SOURCES + src/prototypes/minimap_00.cpp) + # Add an executable target add_executable(Holesome ${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(Minimap_00 ${MINIMAP_00_SOURCES}) + # Link SFML and other libraries to your executable target target_link_libraries(Holesome sfml-graphics sfml-audio) 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(Minimap_00 sfml-graphics sfml-audio) + # Assets add_custom_target(copy_assets COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/assets ${CMAKE_CURRENT_BINARY_DIR}/assets diff --git a/src/config.h b/src/config.h index ee7abe6..ed180d7 100644 --- a/src/config.h +++ b/src/config.h @@ -13,7 +13,7 @@ #define PLAYER_RADIUS_PER_LEVEL 0.25f // World -#define WORLD_GRAVITY b2Vec2(0.f, 0.f) +#define WORLD_GRAVITY b2Vec2(0.f, -9.8f) #define SKY_HEIGHT_SCALE 2.f // FPS diff --git a/src/coordinates/translated_coordinates.cpp b/src/coordinates/translated_coordinates.cpp index 56d1af2..93933c6 100644 --- a/src/coordinates/translated_coordinates.cpp +++ b/src/coordinates/translated_coordinates.cpp @@ -101,3 +101,11 @@ TranslatedCoordinates::TranslatedCoordinates(DiagonalWorldCoordinates diagonalWo { setDiagonal(diagonalWorldCoordinates); } + +void TranslatedCoordinates::removeParent() +{ + // Uncomment, if global position should be preserved +// auto globalWorldCoordinates = this->world(); +// this->worldCoordinates = globalWorldCoordinates; + this->parent = nullptr; +} diff --git a/src/coordinates/translated_coordinates.h b/src/coordinates/translated_coordinates.h index 11e9e49..75734fb 100644 --- a/src/coordinates/translated_coordinates.h +++ b/src/coordinates/translated_coordinates.h @@ -36,6 +36,8 @@ public: void setParent(std::shared_ptr parent, WorldCoordinates offset = {0, 0}); + void removeParent(); + void setWorldOffset(WorldCoordinates offset); void setScreenOffset(IsometricCoordinates offset); diff --git a/src/game/collectables/collectable.cpp b/src/game/collectables/collectable.cpp index 57e8c27..c2bba11 100644 --- a/src/game/collectables/collectable.cpp +++ b/src/game/collectables/collectable.cpp @@ -1,6 +1,7 @@ #include "collectable.hpp" #include "../../sprites/versatile_sprite.hpp" #include "../../config.h" +#include "../input/input_mapper.h" Collectable::Collectable() { @@ -10,7 +11,9 @@ Collectable::Collectable() void Collectable::setRotation(float angle) { - + // Assume that the sprite is the first child + auto sprite = std::dynamic_pointer_cast(getChildren()[0]); + sprite->setRotation(angle); } float Collectable::getDepth() const @@ -29,3 +32,28 @@ void Collectable::setSprite(const std::string &spriteName) // Set half size offset of coordinates 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 = 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); +} diff --git a/src/game/collectables/collectable.hpp b/src/game/collectables/collectable.hpp index 98e6d10..972b36d 100644 --- a/src/game/collectables/collectable.hpp +++ b/src/game/collectables/collectable.hpp @@ -13,16 +13,20 @@ public: void setRotation(float angle); + void update() override; + float getDepth() const; int getId() const { return collectableId; } - private: int collectableId = 0; static inline int collectableCount = 0; + + float angle = 0; + }; diff --git a/src/game/collectables/collection/collectables_collection.cpp b/src/game/collectables/collection/collectables_collection.cpp index 9bfe36c..1d076c8 100644 --- a/src/game/collectables/collection/collectables_collection.cpp +++ b/src/game/collectables/collection/collectables_collection.cpp @@ -30,17 +30,18 @@ void CollectablesCollection::createEmpty(int maxDepth) void CollectablesCollection::remove(const std::shared_ptr &collectable) { depthCollections[collectable->getDepth()]->remove(collectable); - + removeChild(collectable); } void CollectablesCollection::add(const std::shared_ptr &collectable) { depthCollections[collectable->getDepth()]->add(collectable); + addDetachedChild(collectable); } void CollectablesCollection::update() { - updateCollectables(); + GameObject::update(); // Move collectables to new depth collections if necessary @@ -57,22 +58,27 @@ void CollectablesCollection::update() for (auto &collectable: collectables) { int newDepth = std::floor(collectable->getDepth()); - if (newDepth != depth) + if (newDepth == depth) { - if (newDepth < 0 || newDepth >= (int) depthCollections.size()) - { - LOG(ERROR) << "Collectable " << collectable->getId() << " has invalid depth " << newDepth - << "! Keeping it at previous depth " << depth << " ..."; - continue; - } - - depthCollection->remove(collectable); - depthCollections[newDepth]->add(collectable); + continue; } + + 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) { // 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 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(depth); - } - - return depthCollections[depth]; -} diff --git a/src/game/collectables/collection/collectables_collection.hpp b/src/game/collectables/collection/collectables_collection.hpp index 0aad6f4..a01bf5b 100644 --- a/src/game/collectables/collection/collectables_collection.hpp +++ b/src/game/collectables/collection/collectables_collection.hpp @@ -23,14 +23,12 @@ public: void add(const std::shared_ptr& collectable); void remove(const std::shared_ptr& collectable); - std::shared_ptr getDepthCollection(int depth); - private: static inline std::shared_ptr singletonInstance = nullptr; std::map> depthCollections = {}; - void updateCollectables(); + bool isValidDepth(int desiredDepth) const; }; diff --git a/src/game/game.cpp b/src/game/game.cpp index 581cd51..e74cc52 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -99,6 +99,14 @@ void Game::update() } InputMapper::getInstance()->updateIdentities(); + + for (const auto &gameObject: gameObjects) + { + if (gameObject->getActive()) + { + gameObject->preRenderUpdate(); + } + } } std::shared_ptr Game::getInstance() diff --git a/src/game/game_object.cpp b/src/game/game_object.cpp index 07bda62..8e268fe 100644 --- a/src/game/game_object.cpp +++ b/src/game/game_object.cpp @@ -64,6 +64,7 @@ void GameObject::removeChild(const std::shared_ptr &child) if (it != children.end()) { children.erase(it); + child->coordinates->removeParent(); } } @@ -84,3 +85,11 @@ void GameObject::addDetachedChild(const std::shared_ptr &child) { children.push_back(child); } + +void GameObject::preRenderUpdate() +{ + for (auto &child: children) + { + child->preRenderUpdate(); + } +} diff --git a/src/game/game_object.h b/src/game/game_object.h index 456092e..719865e 100644 --- a/src/game/game_object.h +++ b/src/game/game_object.h @@ -20,6 +20,8 @@ public: virtual void physicsUpdate(); + virtual void preRenderUpdate(); + void setActive(bool active); bool getActive() const { return isActive; } diff --git a/src/game/physics/holes/collectable_simulation.cpp b/src/game/physics/holes/collectable_simulation.cpp index 9584f97..46ed060 100644 --- a/src/game/physics/holes/collectable_simulation.cpp +++ b/src/game/physics/holes/collectable_simulation.cpp @@ -20,7 +20,7 @@ void CollectableSimulation::physicsUpdate() { 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(); } diff --git a/src/prototypes/minimap_00.cpp b/src/prototypes/minimap_00.cpp new file mode 100644 index 0000000..a649279 --- /dev/null +++ b/src/prototypes/minimap_00.cpp @@ -0,0 +1,72 @@ +#include +#include +#include +#include + +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; + } + } + } +} \ No newline at end of file diff --git a/src/sprites/animated_sprite.cpp b/src/sprites/animated_sprite.cpp index f64eb96..597d708 100644 --- a/src/sprites/animated_sprite.cpp +++ b/src/sprites/animated_sprite.cpp @@ -4,7 +4,12 @@ AnimatedSprite::AnimatedSprite(const std::vector &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(sprite)); + } + setSize(size); } @@ -25,27 +30,50 @@ void AnimatedSprite::update() void AnimatedSprite::draw(sf::RenderWindow *window) { 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) { 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 { - auto scale = sprites[currentFrame].getScale(); - auto textureSize = sprites[currentFrame].getTextureRect(); + auto scale = sprites[currentFrame]->getScale(); + auto textureSize = sprites[currentFrame]->getTextureRect(); return {textureSize.width * scale.x, textureSize.height * scale.y}; } 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); } diff --git a/src/sprites/animated_sprite.hpp b/src/sprites/animated_sprite.hpp index adccf23..5baf4aa 100644 --- a/src/sprites/animated_sprite.hpp +++ b/src/sprites/animated_sprite.hpp @@ -23,10 +23,17 @@ public: sf::Sprite getSprite() const override; + void preRenderUpdate() override; + + void setRotation(float angle) override; + private: int currentFrame = 0; sf::Time timeSinceLastFrame = sf::Time::Zero; - std::vector sprites; + std::vector> sprites; + + sf::Vector2f renderPosition; + float angle = 0; }; diff --git a/src/sprites/single_sprite.cpp b/src/sprites/single_sprite.cpp index 62405b8..4f70e29 100644 --- a/src/sprites/single_sprite.cpp +++ b/src/sprites/single_sprite.cpp @@ -17,7 +17,7 @@ SingleSprite::SingleSprite(const std::shared_ptr &texture, const sf void SingleSprite::draw(sf::RenderWindow *window) { - sprite.setPosition(coordinates->isometric().toScreen()); + sprite.setPosition(renderPosition); window->draw(sprite); } @@ -42,3 +42,22 @@ sf::Sprite SingleSprite::getSprite() const { 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); +} diff --git a/src/sprites/single_sprite.hpp b/src/sprites/single_sprite.hpp index 0ced242..41a69db 100644 --- a/src/sprites/single_sprite.hpp +++ b/src/sprites/single_sprite.hpp @@ -9,9 +9,9 @@ class SingleSprite : public GameObject, public Sprite { 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 &texture, const sf::Vector2f &size = sf::Vector2f(0, 0)); + explicit SingleSprite(const std::shared_ptr &texture, const sf::Vector2f &size = sf::Vector2f(0, 0)); void draw(sf::RenderWindow *window) override; @@ -21,8 +21,14 @@ public: sf::Sprite getSprite() const override; + void setRotation(float angle) override; + + void preRenderUpdate() override; + private: sf::Sprite sprite; + float angle = 0; + sf::Vector2f renderPosition; }; diff --git a/src/sprites/sprite.hpp b/src/sprites/sprite.hpp index f709b3d..67ffa1c 100644 --- a/src/sprites/sprite.hpp +++ b/src/sprites/sprite.hpp @@ -5,6 +5,7 @@ #include #include #include +#include class Sprite { @@ -23,6 +24,52 @@ public: { 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 diff --git a/src/sprites/versatile_sprite.cpp b/src/sprites/versatile_sprite.cpp index cacc7dd..5aa21d9 100644 --- a/src/sprites/versatile_sprite.cpp +++ b/src/sprites/versatile_sprite.cpp @@ -22,42 +22,34 @@ VersatileSprite::VersatileSprite(const std::string &name, sf::Vector2f size) void VersatileSprite::setSize(const sf::Vector2f &size) { - if (singleSprite != nullptr) - { - singleSprite->setSize(size); - } else if (animatedSprite != nullptr) - { - animatedSprite->setSize(size); - } else - { - Sprite::setSize(size); - } + getUsedSpritePtr()->setSize(size); } sf::Vector2f VersatileSprite::getSize() const { - if (singleSprite != nullptr) - { - return singleSprite->getSize(); - } else if (animatedSprite != nullptr) - { - return animatedSprite->getSize(); - } else - { - return Sprite::getSize(); - } + return getUsedSpritePtr()->getSize(); } sf::Sprite VersatileSprite::getSprite() const +{ + return getUsedSpritePtr()->getSprite(); +} + +void VersatileSprite::setRotation(float angle) +{ + getUsedSpritePtr()->setRotation(angle); +} + +std::shared_ptr VersatileSprite::getUsedSpritePtr() const { if (singleSprite != nullptr) { - return singleSprite->getSprite(); + return singleSprite; } else if (animatedSprite != nullptr) { - return animatedSprite->getSprite(); + return animatedSprite; } else { - return {}; + throw std::runtime_error("Versatile sprite has no sprite"); } } diff --git a/src/sprites/versatile_sprite.hpp b/src/sprites/versatile_sprite.hpp index ad863e2..c5444ae 100644 --- a/src/sprites/versatile_sprite.hpp +++ b/src/sprites/versatile_sprite.hpp @@ -18,11 +18,15 @@ public: [[nodiscard]] sf::Vector2f getSize() const override; - sf::Sprite getSprite() const override; + [[nodiscard]] sf::Sprite getSprite() const override; + + void setRotation(float angle) override; private: std::shared_ptr singleSprite = nullptr; std::shared_ptr animatedSprite = nullptr; + + std::shared_ptr getUsedSpritePtr() const; };