From c49d937e201ae28b49e7fb8ee750d2b9bf6d76f5 Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Sun, 9 Jul 2023 03:43:47 +0200 Subject: [PATCH] A bunch of progress on collectable occlusion --- CMakeLists.txt | 5 +- assets/collectables/bike.png | Bin 0 -> 849 bytes assets/collectables/rose.png | Bin 0 -> 517 bytes assets/collectables/rosebush.png | Bin 0 -> 578 bytes assets/collectables/small-tree.png | Bin 0 -> 835 bytes assets/collectables/stone.png | Bin 0 -> 617 bytes assets/collectables/tram.png | Bin 0 -> 967 bytes src/collectables.hpp | 2 +- src/config.h | 1 + src/sprites/configs/masked_sprite_config.hpp | 22 +++ src/sprites/masked_sprite.cpp | 144 +++++++++++++++++++ src/sprites/masked_sprite.hpp | 45 ++++++ src/sprites/masked_sprite_hole.hpp | 51 +++++++ src/sprites/sprite_factory.cpp | 43 ++++++ src/sprites/sprite_factory.hpp | 2 + src/sprites/sprite_sheet.cpp | 16 ++- src/sprites/sprite_sheet.hpp | 14 +- src/sprites/versatile_sprite.cpp | 37 ++--- src/sprites/versatile_sprite.hpp | 5 +- src/texture_config.h | 34 ++++- src/utilities/vector_utils.hpp | 9 ++ 21 files changed, 388 insertions(+), 42 deletions(-) create mode 100644 assets/collectables/bike.png create mode 100644 assets/collectables/rose.png create mode 100644 assets/collectables/rosebush.png create mode 100644 assets/collectables/small-tree.png create mode 100644 assets/collectables/stone.png create mode 100644 assets/collectables/tram.png create mode 100644 src/sprites/configs/masked_sprite_config.hpp create mode 100644 src/sprites/masked_sprite.cpp create mode 100644 src/sprites/masked_sprite.hpp create mode 100644 src/sprites/masked_sprite_hole.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d340689..eaae4c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,7 +126,10 @@ set(SOURCES src/game/physics/holes/ground/collectable_sim_ground.cpp src/game/physics/holes/ground/collectable_sim_ground.hpp src/game/physics/body_adapter.cpp - src/game/physics/body_adapter.hpp) + src/game/physics/body_adapter.hpp + src/sprites/masked_sprite.cpp + src/sprites/masked_sprite.hpp + src/sprites/configs/masked_sprite_config.hpp src/sprites/masked_sprite_hole.hpp) set(PHYSICS_00_SOURCES src/prototypes/physics_00.cpp) diff --git a/assets/collectables/bike.png b/assets/collectables/bike.png new file mode 100644 index 0000000000000000000000000000000000000000..96fdb1072ed88ebe4555bddcf4b50a71c39d2e39 GIT binary patch literal 849 zcmV-X1FrmuP)3yVoup)pEqEJh54#dylE&6_v# zX4K4hy*H9yer(QCDoYlxp0pu7ZkpC^r*{k4kujxaoAKMqu^;4_9x0XTE<$^Obg4) z=}f2AwBmi7mo&sPbF&gV#M`lm5GI75NyZpukbZ_`9;BBbeX^oLexQ&)!>Z(JG8=4Q zvOar^)p;$ zO8KitF!U*UuB3GyLemypT$i-y9$aoi+moT1YC)S)8{suZ8-@&bt+oYkHlfT#mZTa6TpGBgeD*R`T04C#(=_{ zb12tRN~SeN6Zg;%x?(9Mvsw!&B|{&;KG7yZ1jZBGr+kADC)H}`Jp%kGbB6Q0nxNIR z1vL`_05H>}0>46D-7@qZwM1&MTHivUvny_<$+VCoe+@POQ^+}o{NRn_n4Qsk?6ysl zKO0x2+qOKH*gS8x>Y@ObK1Xdp?~$wGoWmpkB0o)HjCqm9=snVSj}Qlxi8(gy)$)*^ zk?%u{5tn7jfM4%7e7|1R%4lz%XGkd(XG>zs{TL(megFA_%T|RDFoaO8wx$xb^URmW zNR_z%gKyGr2*L0c@Nc#D brosLJmBgJ|?*cy^00000NkvXXu0mjf()5Zb literal 0 HcmV?d00001 diff --git a/assets/collectables/rose.png b/assets/collectables/rose.png new file mode 100644 index 0000000000000000000000000000000000000000..5f86907ed6877f77f4247370244420a65234972f GIT binary patch literal 517 zcmV+g0{Z=lP)3yVoup)pEqEJh54#dylE&6_v# zX4K4hy*H9yer(QCDoYlxp0pu7ZkpC^r*{k4kujxaoAKMqu^;4_9x0XTE<$^Obg4) z=}f2AwBmi7mo&sPbF&gV#M`lm5GI75NyZpukbZ_`9;BBbeX^oLexQ&)!>Z(JG8=4Q zvOar^)p;$ zO8KitF!U*UuB3GyLemypT$i-y9$aoi+moT1YC)6(@*haJe3=9kmEHDOK%i{V|jK~52guMvM-vi3900000NkvXX Hu0mjfK?maM literal 0 HcmV?d00001 diff --git a/assets/collectables/rosebush.png b/assets/collectables/rosebush.png new file mode 100644 index 0000000000000000000000000000000000000000..a00326ddfbe23dcc512f2cc7b1fa1c561127321c GIT binary patch literal 578 zcmV-I0=@l-P)3yVoup)pEqEJh54#dylE&6_v# zX4K4hy*H9yer(QCDoYlxp0pu7ZkpC^r*{k4kujxaoAKMqu^;4_9x0XTE<$^Obg4) z=}f2AwBmi7mo&sPbF&gV#M`lm5GI75NyZpukbZ_`9;BBbeX^oLexQ&)!>Z(JG8=4Q zvOar^)p;$ zO8KitF!U*UuB3GyLemypT$i-y9$aoi+moT1YC)984xjoD5QT^qqlto^7gjzeh-gA zKD3w@0OnW*0IqX$EYso*Vgy|&Y0j~X5lpHsdj>1BTC#h#lEMzE7VpK>4=y}BxVnBG Q&;S4c07*qoM6N<$f>Rg%UjP6A literal 0 HcmV?d00001 diff --git a/assets/collectables/small-tree.png b/assets/collectables/small-tree.png new file mode 100644 index 0000000000000000000000000000000000000000..b54e231b2c29544389903179200aa0895fd3a56f GIT binary patch literal 835 zcmV-J1HAl+P)P001ip1^@s6jawtd0004jX+uL$b5ch_ zAW20-HZeIiHZ3wPF#rH4k#&*JD@0Kg$3KQ?3yVoup)pEqEJh54#dylE&6_v# zX4K4hy*H9yer(QCDoYlxp0pu7ZkpC^r*{k4kujxaoAKMqu^;4_9x0XTE<$^Obg4) z=}f2AwBmi7mo&sPbF&gV#M`lm5GI75NyZpukbZ_`9;BBbeX^oLexQ&)!>Z(JG8=4Q zvOar^)p;$ zO8KitF!U*UuB3GyLemypT$i-y9$aoi+moT1YC)FO5yTwy=^Y6V(W|Xt@l4uMOGCBu`>;vTNJwYC-1f zK@;_Gdu+yMqa9n=Jc-#28glo5R8#=UyHyi&eTUm4>MSD7_4izD$imlI)LJqdfD(D1 z*fynZq#{;hREAzRqX{x0lA8WHVL5#ukDWS;M37E9UfP*r$o<`I>-VyJ61V=JoLyqb z{`IYYNEclm#4LU<5MMb#e2ag1nOq@P$Ulbo9w|3yVoup)pEqEJh54#dylE&6_v# zX4K4hy*H9yer(QCDoYlxp0pu7ZkpC^r*{k4kujxaoAKMqu^;4_9x0XTE<$^Obg4) z=}f2AwBmi7mo&sPbF&gV#M`lm5GI75NyZpukbZ_`9;BBbeX^oLexQ&)!>Z(JG8=4Q zvOar^)p;$ zO8KitF!U*UuB3GyLemypT$i-y9$aoi+moT1YC)|;N)|1@HP5<(n-P}#TZxj zRaNSm{AzMthJjbtmDphcFtY;mIrC^+7MTFJpP!pSsw(2ZA`{2;T5O$jg%AKZc)|g1 z1{mk2_y8%TFJxv?gX7#3@OFA{hV?mbAF;o!-niEX$u~O>O{A|500000NkvXXu0mjf D*u@i9 literal 0 HcmV?d00001 diff --git a/assets/collectables/tram.png b/assets/collectables/tram.png new file mode 100644 index 0000000000000000000000000000000000000000..b2cbf78d953e4cf805517d3216aa98ec86095f37 GIT binary patch literal 967 zcmV;&133JNP)3yVoup)pEqEJh54#dylE&6_v# zX4K4hy*H9yer(QCDoYlxp0pu7ZkpC^r*{k4kujxaoAKMqu^;4_9x0XTE<$^Obg4) z=}f2AwBmi7mo&sPbF&gV#M`lm5GI75NyZpukbZ_`9;BBbeX^oLexQ&)!>Z(JG8=4Q zvOar^)p;$ zO8KitF!U*UuB3GyLemypT$i-y9$aoi+moT1YC)>1U)1UX=hn@t;5eo>;d1=#52obrq<>BF+mwke$gi^IbWaZlc9jbt;?)1K( zS8YNP1szFr$s|z06}5^u{1G)-k-~a2>qF(*ZAAdeld3)A}_C>(%1*_ z9P&tXRHB(NFYTE^GX#vdU9dY`dn;egYh`Duwo~?H1FA2bl$GbXG-ah}svNu;G|RG1 zJ6V}cU#>v%B}tP0l+`DOszcK{uKTh9$(MG_X$=9RvxcBhrGiHTR*)%E7Gt=A*C|W2 zeh|~rjJ@+UGIhdsjJePsYhT9C?@#s5-;cj( zEV5wEfcd7W0WIoaL}OA`Q`v1%u7W2;m;uWr0y2L7|!Am+Ax~9VPWl`zYz?qx(bu%@9IFI(5d{ p;Q|C-oCTt7sLU{(Itcjm const all_collectables = { - {"box", CollectableConfig("numbers")} + {"box", CollectableConfig("rosebush")} }; #endif //HOLESOME_COLLECTABLES_HPP diff --git a/src/config.h b/src/config.h index fde27a4..2092052 100644 --- a/src/config.h +++ b/src/config.h @@ -31,6 +31,7 @@ #define ISOMETRIC_SKEW (16.f/32.f) #define MOVEMENT_SKEW sf::Vector2f(1.f, 1/ISOMETRIC_SKEW/2.f) #define WORLD_TO_ISO_SCALE 50.0f // 50.f, don't change. Rather adjust the zoom of the camera +#define MASKED_HOLE_BORDER_TRANSITION_SIZE 0.2f // Tracking view defaults #define DEF_TV_IS_ABSOLUTE_FREE_MOVE_THRESHOLD false diff --git a/src/sprites/configs/masked_sprite_config.hpp b/src/sprites/configs/masked_sprite_config.hpp new file mode 100644 index 0000000..86112dc --- /dev/null +++ b/src/sprites/configs/masked_sprite_config.hpp @@ -0,0 +1,22 @@ +#ifndef HOLESOME_MASKED_SPRITE_CONFIG_HPP +#define HOLESOME_MASKED_SPRITE_CONFIG_HPP + +#include +#include +#include +#include "sprite_config.hpp" + +struct MaskedSpriteConfig +{ + SpriteConfig spriteConfig; + + MaskedSpriteConfig(std::string sheetName, int sheetIndex, sf::Vector2f size = sf::Vector2f(0, 0)) + : spriteConfig(std::move(sheetName), sheetIndex, size) + {} + + explicit MaskedSpriteConfig(std::string textureName, sf::Vector2f size = sf::Vector2f(0, 0)) + : spriteConfig(std::move(textureName), size) + {} +}; + +#endif //HOLESOME_MASKED_SPRITE_CONFIG_HPP diff --git a/src/sprites/masked_sprite.cpp b/src/sprites/masked_sprite.cpp new file mode 100644 index 0000000..e522d25 --- /dev/null +++ b/src/sprites/masked_sprite.cpp @@ -0,0 +1,144 @@ +#include "masked_sprite.hpp" +#include "../config.h" +#include "../logging/easylogging++.h" +#include "../game/player/player_collection.hpp" + +MaskedSprite::MaskedSprite(const std::shared_ptr &sheetTexture, sf::IntRect textureRext, sf::Vector2f size) + : texture(sheetTexture), textureRect(textureRext) +{ + image = std::make_shared(sheetTexture->copyToImage()); + sprite = std::make_shared(*texture, textureRect); + setSize(size); +} + +MaskedSprite::MaskedSprite(const std::shared_ptr &texture, sf::Vector2f size) + : texture(texture) +{ + image = std::make_shared(texture->copyToImage()); + textureRect = sf::IntRect(0, 0, texture->getSize().x, texture->getSize().y); + sprite = std::make_shared(*texture, textureRect); + setSize(size); +} + +void MaskedSprite::preRenderUpdate() +{ + GameObject::preRenderUpdate(); + + // TODO: Did anything change? Is update of masked sprite necessary? + + if (angle == 0) + { + renderPosition = coordinates->isometric().toScreen(); + } else + { + auto position = coordinates->isometric().toScreen(); + renderPosition = calculateRotatedCornerPosition(position, angle); + } + sprite->setPosition(renderPosition); + sprite->setRotation(angle); + + updateFreshMaskedSprite(); +} + +void MaskedSprite::draw(sf::RenderWindow *window) +{ + GameObject::draw(window); + + window->draw(*sprite); +} + +void MaskedSprite::updateFreshMaskedSprite() +{ + // TODO: Calculate depth per pixel + auto maskedImage = std::make_shared(); + maskedImage->create(textureRect.width, textureRect.height, sf::Color::Transparent); + +// todo: or use sf::RenderTexture? + + maskedImage->copy(*image, 0, 0); + + // Calculate world coordinates for each pixel + auto topLeftCorner = sf::Vector2f(coordinates->diagonalWorld().horizontal, coordinates->diagonalWorld().vertical); + auto xAxis = rotateVectorByAngle(sf::Vector2f(1, 0), angle); + auto yAxis = rotateVectorByAngle(sf::Vector2f(0, -1), angle); + float xFactorPerPixel = sprite->getScale().x / WORLD_TO_ISO_SCALE; + float yFactorPerPixel = sprite->getScale().y / WORLD_TO_ISO_SCALE; + for (int yOffset = 0; yOffset < textureRect.height; yOffset++) + { + for (int xOffset = 0; xOffset < textureRect.width; xOffset++) + { + int y = yOffset + textureRect.top; + int x = xOffset + textureRect.left; + + auto pixelPosition = + topLeftCorner + xAxis * (xOffset * xFactorPerPixel) + yAxis * (yOffset * yFactorPerPixel); + if (pixelPosition.y >= 0) + { + continue; + } + + auto pixelColor = image->getPixel(x, y); + auto newPixelColor = calculateNewPixelColor(pixelColor, pixelPosition); + maskedImage->setPixel(x, y, newPixelColor); + } + } + + maskedTexture = std::make_shared(); + maskedTexture->loadFromImage(*maskedImage); + + sprite->setTexture(*maskedTexture); + sprite->setTextureRect(sf::IntRect(0, 0, textureRect.width, textureRect.height)); +} + +void MaskedSprite::setSize(const sf::Vector2f &size) +{ + if (size == sf::Vector2f(0, 0)) + { + return; + } + + sprite->setScale(size.x / textureRect.width, size.y / textureRect.height); +} + +sf::Vector2f MaskedSprite::getSize() const +{ + return {textureRect.width * sprite->getScale().x, textureRect.height * sprite->getScale().y}; +} + +sf::Sprite MaskedSprite::getSprite() const +{ + return *sprite; +} + +void MaskedSprite::setRotation(float angle) +{ + this->angle = restrictAngle(angle); + sprite->setRotation(angle); +} + +sf::Color MaskedSprite::calculateNewPixelColor(sf::Color currentColor, sf::Vector2 position) +{ + if (position.y >= 0) + { + // Pixel is above ground + return currentColor; + } + + // Change color based on height and hole + // Cut off pixels that are hidden in the ground, beyond the hole + float depth = coordinates->diagonalWorld().depth; + auto pixelCoordinates = TranslatedCoordinates(DiagonalWorldCoordinates(position.x, position.y, depth)); + + std::vector holeAlphaFactors{}; + auto players = PlayerCollection::getInstance()->getPlayers(); + for (auto &player: players) + { + auto holeMask = MaskedSpriteHole(player); + float holeAlphaFactor = holeMask.isPointInHoleFactor(pixelCoordinates); + holeAlphaFactors.push_back(holeAlphaFactor); + } + + float biggestHoleAlphaFactor = *std::max_element(holeAlphaFactors.begin(), holeAlphaFactors.end()); + currentColor.a *= biggestHoleAlphaFactor; + return currentColor; +} diff --git a/src/sprites/masked_sprite.hpp b/src/sprites/masked_sprite.hpp new file mode 100644 index 0000000..fa0b649 --- /dev/null +++ b/src/sprites/masked_sprite.hpp @@ -0,0 +1,45 @@ +#ifndef HOLESOME_MASKED_SPRITE_HPP +#define HOLESOME_MASKED_SPRITE_HPP + + +#include +#include "../game/game_object.h" +#include "versatile_sprite.hpp" +#include "masked_sprite_hole.hpp" + +class MaskedSprite : public Sprite, public GameObject +{ +public: + explicit MaskedSprite(const std::shared_ptr& texture, sf::Vector2f size = sf::Vector2f(0, 0)); + + MaskedSprite(const std::shared_ptr &sheetTexture, sf::IntRect textureRext, + sf::Vector2f size = sf::Vector2f(0, 0)); + + void preRenderUpdate() override; + + void draw(sf::RenderWindow *window) override; + + void setSize(const sf::Vector2f &size) override; + + sf::Vector2f getSize() const override; + + sf::Sprite getSprite() const override; + + void setRotation(float angle) override; + +private: + std::shared_ptr sprite; + std::shared_ptr maskedTexture; + std::shared_ptr texture; + std::shared_ptr image; + sf::IntRect textureRect; + float angle = 0; + sf::Vector2f renderPosition; + + void updateFreshMaskedSprite(); + + sf::Color calculateNewPixelColor(sf::Color currentColor, sf::Vector2 position); +}; + + +#endif //HOLESOME_MASKED_SPRITE_HPP diff --git a/src/sprites/masked_sprite_hole.hpp b/src/sprites/masked_sprite_hole.hpp new file mode 100644 index 0000000..28cb359 --- /dev/null +++ b/src/sprites/masked_sprite_hole.hpp @@ -0,0 +1,51 @@ +#ifndef HOLESOME_MASKED_SPRITE_HOLE_HPP +#define HOLESOME_MASKED_SPRITE_HOLE_HPP + +#include +#include "../coordinates/translated_coordinates.h" +#include "../game/player/player.hpp" + +struct MaskedSpriteHole +{ + sf::Vector2f center = sf::Vector2f(0, 0); + float majorRadiusSquared = 0; + float minorRadiusSquared = 0; + + MaskedSpriteHole() = default; + + MaskedSpriteHole(const std::shared_ptr &player) + { + auto iso = player->coordinates->isometric(); + this->center = sf::Vector2f(iso.x, iso.y); + + float radius = player->getWorldRadius() * WORLD_TO_ISO_SCALE * sqrt(2); + this->majorRadiusSquared = pow(radius, 2); + this->minorRadiusSquared = pow(radius * ISOMETRIC_SKEW, 2); + } + + float isPointInHoleFactor(TranslatedCoordinates point) const + { + if (majorRadiusSquared == 0 || minorRadiusSquared == 0) + return 1.f; + + // Calculate the value of the ellipse equation + auto iso = point.isometric(); + auto relativeCoordinates = center - sf::Vector2f(iso.x, iso.y); + float L = pow(relativeCoordinates.x, 2) / majorRadiusSquared + pow(relativeCoordinates.y, 2) / minorRadiusSquared; + + // Compare the value of L to 1 + float lowerBound = 1 - MASKED_HOLE_BORDER_TRANSITION_SIZE / 2.f; + float upperBound = 1 + MASKED_HOLE_BORDER_TRANSITION_SIZE / 2.f; + if (L <= lowerBound) + // Inside + return 1.f; + else if (L < upperBound) + // Linear transition for anti-aliasing + return (upperBound - L) / MASKED_HOLE_BORDER_TRANSITION_SIZE; + else + // Outside + return 0.f; + } +}; + +#endif //HOLESOME_MASKED_SPRITE_HOLE_HPP diff --git a/src/sprites/sprite_factory.cpp b/src/sprites/sprite_factory.cpp index 63fb81b..32db7eb 100644 --- a/src/sprites/sprite_factory.cpp +++ b/src/sprites/sprite_factory.cpp @@ -1,6 +1,7 @@ #include "sprite_factory.hpp" #include "../texture_config.h" #include "texture_manager.hpp" +#include "masked_sprite.hpp" std::shared_ptr SpriteFactory::createSingleSprite(const std::string &name, sf::Vector2f size) { @@ -136,3 +137,45 @@ std::shared_ptr SpriteFactory::createTileMap(TileMapConfig config) return std::make_shared(tileSet, config.tiles); } + +std::shared_ptr SpriteFactory::createMaskedSprite(const std::string &name, sf::Vector2f size) +{ + // Get sprite config + auto masked_sprite_config = all_masked_sprites.find(name); + + if (masked_sprite_config == all_masked_sprites.end()) + { + LOG(ERROR) << "MaskedSprite " << name << " not found. Could not create masked sprite."; + return nullptr; + } + + // Construct sprite + auto spriteConfig = masked_sprite_config->second.spriteConfig; + + // Construct simply from texture + if (!spriteConfig.isFromSheet) + { + auto texture = TextureManager::getInstance()->getTexture(spriteConfig.resourceName); + if (texture == nullptr) + { + LOG(ERROR) << "Texture " << spriteConfig.resourceName << " not found. Could not create masked sprite."; + return nullptr; + } + + LOG(INFO) << "Creating masked sprite from texture " << spriteConfig.resourceName; + return std::make_shared(texture, size); + } + + // Construct from sheet + auto sheet = createSheet(spriteConfig.resourceName); + if (sheet == nullptr) + { + LOG(ERROR) << "Sheet " << spriteConfig.resourceName << " not found. Could not create masked sprite."; + return nullptr; + } + + LOG(INFO) << "Creating single masked from sheet " << spriteConfig.resourceName; + auto texture = sheet->getTexture(); + auto rect = sheet->getTextureRect(spriteConfig.sheetIndex); + return std::make_shared(texture, rect, size); +} diff --git a/src/sprites/sprite_factory.hpp b/src/sprites/sprite_factory.hpp index e3dec4e..ebc3b3b 100644 --- a/src/sprites/sprite_factory.hpp +++ b/src/sprites/sprite_factory.hpp @@ -11,11 +11,13 @@ #include "tiling/tilemap.hpp" #include "tiling/tileset.hpp" #include "tiling/tilemap_config.hpp" +#include "masked_sprite.hpp" class SpriteFactory { public: static std::shared_ptr createSingleSprite(const std::string& name, sf::Vector2f size = sf::Vector2f(0, 0)); + static std::shared_ptr createMaskedSprite(const std::string& name, sf::Vector2f size = sf::Vector2f(0, 0)); static std::shared_ptr createAnimatedSprite(const std::string& name, sf::Vector2f size = sf::Vector2f(0, 0)); static std::shared_ptr createSheet(const std::string& name); static std::shared_ptr createTileSet(const std::string &name); diff --git a/src/sprites/sprite_sheet.cpp b/src/sprites/sprite_sheet.cpp index a044761..9ea6774 100644 --- a/src/sprites/sprite_sheet.cpp +++ b/src/sprites/sprite_sheet.cpp @@ -1,7 +1,7 @@ #include "sprite_sheet.hpp" #include "single_sprite.hpp" -SpriteSheet::SpriteSheet(const std::shared_ptr& texture, int columns, int rows) +SpriteSheet::SpriteSheet(const std::shared_ptr &texture, int columns, int rows) { this->texture = texture; this->columns = columns; @@ -17,12 +17,15 @@ SpriteSheet::SpriteSheet(const std::shared_ptr& texture, int column { sf::Sprite sprite; sprite.setTexture(*texture); - sprite.setTextureRect(sf::IntRect(x * spriteWidth, y * spriteHeight, spriteWidth, spriteHeight)); + sprite.setTextureRect(getRect(spriteWidth, spriteHeight, y, x)); sprites.push_back(sprite); } } } +sf::Rect SpriteSheet::getRect(int spriteWidth, int spriteHeight, int row, int column) const +{ return sf::IntRect(column * spriteWidth, row * spriteHeight, spriteWidth, spriteHeight); } + std::shared_ptr SpriteSheet::getSprite(int sequenceIndex) const { if (sequenceIndex < 0 || sequenceIndex >= sprites.size()) @@ -66,3 +69,12 @@ std::shared_ptr SpriteSheet::getTexture() const { return texture; } + +sf::IntRect SpriteSheet::getTextureRect(int sequenceIndex) const +{ + int column = sequenceIndex % columns; + int row = sequenceIndex / columns; + int spriteWidth = texture->getSize().x / columns; + int spriteHeight = texture->getSize().y / rows; + return getRect(spriteWidth, spriteHeight, row, column); +} diff --git a/src/sprites/sprite_sheet.hpp b/src/sprites/sprite_sheet.hpp index 02959b2..cf8a017 100644 --- a/src/sprites/sprite_sheet.hpp +++ b/src/sprites/sprite_sheet.hpp @@ -13,20 +13,24 @@ class SpriteSheet public: SpriteSheet(const std::shared_ptr& texture, int columns, int rows); - std::shared_ptr getSprite(int sequenceIndex) const; + [[nodiscard]] std::shared_ptr getSprite(int sequenceIndex) const; - std::shared_ptr getAnimation(int startingSequenceIndex, int numberOfFrames) const; + [[nodiscard]] std::shared_ptr getAnimation(int startingSequenceIndex, int numberOfFrames) const; - std::shared_ptr getTexture() const; + [[nodiscard]] std::shared_ptr getTexture() const; - int getColumns() const; - int getRows() const; + [[nodiscard]] sf::IntRect getTextureRect(int sequenceIndex) const; + + [[nodiscard]] int getColumns() const; + [[nodiscard]] int getRows() const; private: int columns; int rows; std::shared_ptr texture; std::vector sprites; + + [[nodiscard]] sf::Rect getRect(int spriteWidth, int spriteHeight, int row, int column) const; }; diff --git a/src/sprites/versatile_sprite.cpp b/src/sprites/versatile_sprite.cpp index 5aa21d9..df7a276 100644 --- a/src/sprites/versatile_sprite.cpp +++ b/src/sprites/versatile_sprite.cpp @@ -7,12 +7,19 @@ VersatileSprite::VersatileSprite(const std::string &name, sf::Vector2f size) // Try to find in sprites if (all_sprites.find(name) != all_sprites.end()) { - singleSprite = SpriteFactory::createSingleSprite(name, size); - addChild(singleSprite); + auto spriteObject = SpriteFactory::createSingleSprite(name, size); + addChild(spriteObject); + sprite = spriteObject; } else if (all_animations.find(name) != all_animations.end()) { - animatedSprite = SpriteFactory::createAnimatedSprite(name, size); - addChild(animatedSprite); + auto spriteObject = SpriteFactory::createAnimatedSprite(name, size); + addChild(spriteObject); + sprite = spriteObject; + } else if (all_masked_sprites.find(name) != all_masked_sprites.end()) + { + auto spriteObject = SpriteFactory::createMaskedSprite(name, size); + addChild(spriteObject); + sprite = spriteObject; } else { LOG(ERROR) << "Sprite " << name << " not found. Could not create versatile sprite."; @@ -22,34 +29,20 @@ VersatileSprite::VersatileSprite(const std::string &name, sf::Vector2f size) void VersatileSprite::setSize(const sf::Vector2f &size) { - getUsedSpritePtr()->setSize(size); + sprite->setSize(size); } sf::Vector2f VersatileSprite::getSize() const { - return getUsedSpritePtr()->getSize(); + return sprite->getSize(); } sf::Sprite VersatileSprite::getSprite() const { - return getUsedSpritePtr()->getSprite(); + return sprite->getSprite(); } void VersatileSprite::setRotation(float angle) { - getUsedSpritePtr()->setRotation(angle); -} - -std::shared_ptr VersatileSprite::getUsedSpritePtr() const -{ - if (singleSprite != nullptr) - { - return singleSprite; - } else if (animatedSprite != nullptr) - { - return animatedSprite; - } else - { - throw std::runtime_error("Versatile sprite has no sprite"); - } + sprite->setRotation(angle); } diff --git a/src/sprites/versatile_sprite.hpp b/src/sprites/versatile_sprite.hpp index c5444ae..f25ea81 100644 --- a/src/sprites/versatile_sprite.hpp +++ b/src/sprites/versatile_sprite.hpp @@ -23,10 +23,7 @@ public: void setRotation(float angle) override; private: - std::shared_ptr singleSprite = nullptr; - std::shared_ptr animatedSprite = nullptr; - - std::shared_ptr getUsedSpritePtr() const; + std::shared_ptr sprite = nullptr; }; diff --git a/src/texture_config.h b/src/texture_config.h index e6878ec..0b8ee3a 100644 --- a/src/texture_config.h +++ b/src/texture_config.h @@ -7,6 +7,7 @@ #include "sprites/configs/sheet_config.hpp" #include "sprites/configs/sprite_config.hpp" #include "sprites/tiling/tileset_config.hpp" +#include "sprites/configs/masked_sprite_config.hpp" #define PLAYER_SKIN "hole" @@ -15,13 +16,19 @@ * The key is the name of the texture, the value is the path to the texture. */ std::map const all_textures = { - {"numbers", "assets/numbers.png"}, - {"64", "assets/64.png"}, - {"edge", "assets/edge.png"}, - {"ring", "assets/ring.png"}, - {"grasses", "assets/grass_plus.png"}, - {"hole", "assets/hole.png"}, - {"iso-tiles", "assets/isometric-tiles.png"} + {"numbers", "assets/numbers.png"}, + {"64", "assets/64.png"}, + {"edge", "assets/edge.png"}, + {"ring", "assets/ring.png"}, + {"grasses", "assets/grass_plus.png"}, + {"hole", "assets/hole.png"}, + {"bike", "assets/collectables/bike.png"}, + {"rose", "assets/collectables/rose.png"}, + {"rosebush", "assets/collectables/rosebush.png"}, + {"small-tree", "assets/collectables/small-tree.png"}, + {"stone", "assets/collectables/stone.png"}, + {"tram", "assets/collectables/tram.png"}, + {"iso-tiles", "assets/isometric-tiles.png"} }; /** @@ -53,6 +60,19 @@ std::map const all_sprites = { {"hole", SpriteConfig("hole")} }; +/** + * All masked sprites used in the game. + * The key is the name of the MaskedSprite, the value is the MaskedSprite config. + */ +std::map const all_masked_sprites = { + {"bike", MaskedSpriteConfig("bike")}, + {"rose", MaskedSpriteConfig("rose")}, + {"rosebush", MaskedSpriteConfig("rosebush")}, + {"stone", MaskedSpriteConfig("stone")}, + {"tram", MaskedSpriteConfig("tram")}, + {"small-tree", MaskedSpriteConfig("small-tree")} +}; + /** * All tilesets used in the game. * The key is the name of the tileset, the value is the tileset config. diff --git a/src/utilities/vector_utils.hpp b/src/utilities/vector_utils.hpp index 2b1df8d..b1c13ad 100644 --- a/src/utilities/vector_utils.hpp +++ b/src/utilities/vector_utils.hpp @@ -35,4 +35,13 @@ T sum(sf::Vector2 v) return v.x + v.y; } +template +sf::Vector2 rotateVectorByAngle(sf::Vector2 v, float angle) +{ + float radians = -angle * M_PI / 180; + auto x = v.x * std::cos(radians) - v.y * std::sin(radians); + auto y = v.x * std::sin(radians) + v.y * std::cos(radians); + return sf::Vector2(x, y); +} + #endif //HOLESOME_VECTOR_UTILS_HPP