diff --git a/.gitmodules b/.gitmodules index bfa520c..6e8ab27 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,3 +14,6 @@ path = extern/SDL_ttf url = https://github.com/libsdl-org/SDL_ttf.git branch = release-2.22.x +[submodule "extern/tmxlite"] + path = extern/tmxlite + url = https://github.com/fallahn/tmxlite.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 5149005..602370b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,10 +17,13 @@ set(SDL2TTF_VENDORED ON) set(SDL2_SOURCE_DIR “${ENGINE_SOURCE_DIR}/extern/SDL”) +set(TMXLITE_STATIC_LIB TRUE) + add_subdirectory(extern/SDL EXCLUDE_FROM_ALL) add_subdirectory(extern/SDL_image EXCLUDE_FROM_ALL) add_subdirectory(extern/SDL_mixer EXCLUDE_FROM_ALL) add_subdirectory(extern/SDL_ttf EXCLUDE_FROM_ALL) +add_subdirectory(extern/tmxlite/tmxlite EXCLUDE_FROM_ALL) file(GLOB_RECURSE SOURCES ${ENGINE_SOURCE_DIR}/src/*.cpp) add_library(${PROJECT_NAME} ${SOURCES}) @@ -33,6 +36,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC # should be private when all SDL fu SDL2_image::SDL2_image-static SDL2_mixer::SDL2_mixer-static SDL2_ttf::SDL2_ttf-static + tmxlite ) if(CMAKE_BUILD_TYPE MATCHES "Debug") diff --git a/extern/tmxlite b/extern/tmxlite new file mode 160000 index 0000000..fcef1a2 --- /dev/null +++ b/extern/tmxlite @@ -0,0 +1 @@ +Subproject commit fcef1a28ade8406e290d5fd168a8950e6996844f diff --git a/include/Map.h b/include/Map.h index 4c1bc3b..92b8179 100644 --- a/include/Map.h +++ b/include/Map.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -10,8 +11,6 @@ public: Map() = default; ~Map() = default; - // code comment below is a test for doxygen - do not remove - /*! * * \brief @@ -21,6 +20,17 @@ public: * \return Boolean for success * */ + [[deprecated("ID based text files are not supported anymore, use .txm maps instead")]] static void loadMap(const char* path, int sizeX, int sizeY, GameInternal* game, const std::map>* textureDict /* backreference */); + [[deprecated]] static void addTile(unsigned long id, int x, int y, GameInternal* game, const std::map>* textureDict); + + /*! + * \brief Loads a .tmx map + * \param path Path to the `.tmx` map file + * + */ + static void loadMapTmx(const char* path); +private: + static void addTile(float x, float y, const tmx::Vector2u& mapTileSize, int u, int v, int zIndex, const char* texturePath); }; diff --git a/include/SpriteComponent.h b/include/SpriteComponent.h index b1f3a32..0676549 100644 --- a/include/SpriteComponent.h +++ b/include/SpriteComponent.h @@ -31,8 +31,12 @@ private: uint8_t speed = 100; bool flipped = false; + int textureXOffset; + int textureYOffset; + public: SpriteComponent(const char* path, int zIndex); + SpriteComponent(const char* path, int xOffset, int yOffset, int zIndex); SpriteComponent( const char* path, bool isAnimated, diff --git a/src/Map.cpp b/src/Map.cpp index 072398f..7204be2 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -1,14 +1,30 @@ #include "Map.h" +#include #include +#include #include #include +#include #include +#include + +#include +#include + +#include +#include +#include +#include +#include #include "Constants.h" #include "GameInternal.h" -#include "SDL_error.h" +#include "SpriteComponent.h" +#include "TextureManager.h" #include "TileComponent.h" +#include "VEGO.h" +#include "tmxlite/Types.hpp" void Map::loadMap(const char* path, int sizeX, int sizeY, GameInternal* game, const std::map>* textureDict /* backreference */) { @@ -36,7 +52,7 @@ void Map::loadMap(const char* path, int sizeX, int sizeY, GameInternal* game, co if (x != sizeX) { SDL_SetError("Error loading map: specified x size doesn't match map file!"); std::cout << "ERROR: Map couldnt be loaded! " << SDL_GetError() << std::endl; - SDL_ClearError(); + SDL_ClearError(); } x = 0; y++; @@ -63,4 +79,93 @@ void Map::addTile(unsigned long id, int x, int y, GameInternal* game, const std: if(tile.getComponent().hasCollision()) tile.addComponent("tile"/*tile.getComponent().getName().data()*/); tile.addGroup((size_t)Entity::GroupLabel::MAPTILES); +} + +void Map::loadMapTmx(const char* path) +{ + tmx::Map map; + if (!map.load(path)) { + // TODO: log to console + } + + const std::vector& tileSets = map.getTilesets(); + + const std::vector& mapLayers = map.getLayers(); + const auto mapSize = map.getTileCount(); + const auto mapTileSize = map.getTileSize(); + + std::vector texturePaths = {}; + + for (auto tileSet : tileSets) { + texturePaths.emplace_back(tileSet.getImagePath()); + } + + for (auto& layer : mapLayers) { + + if (layer->getType() == tmx::Layer::Type::Tile) { + auto& tileLayer = layer->getLayerAs(); + + int zIndex = 0; + + const std::vector& properties = layer->getProperties(); + auto zIndexIterator = std::find_if(properties.begin(), properties.end(), [](const tmx::Property& property) { + return property.getName() == "zIndex"; + }); + + if (zIndexIterator != properties.end() && std::is_nothrow_convertiblegetType()), int>::value) { + zIndex = zIndexIterator->getIntValue(); + } + + const auto& tiles = tileLayer.getTiles(); + + for (auto i = 0u; i < tileSets.size(); i++) { + auto tilesetTexture = VEGO_Game().textureManager->loadTexture(texturePaths.at(i).c_str()); + tmx::Vector2i textureSize; + SDL_QueryTexture(tilesetTexture, nullptr, nullptr, &(textureSize.x), &(textureSize.y)); + + const auto tileCountX = textureSize.x / mapTileSize.x; + const auto tileCountY = textureSize.y / mapTileSize.y; + + for (auto idx = 0ul; idx < mapSize.x * mapSize.y; idx++) { + + if (idx >= tiles.size() || tiles[idx].ID < tileSets.at(i).getFirstGID() + || tiles[idx].ID >= (tileSets.at(i).getFirstGID() + tileSets.at(i).getTileCount())) { + continue; + } + + const auto x = idx % mapSize.x; + const auto y = idx / mapSize.x; + + auto idIndex = (tiles[idx].ID - tileSets.at(i).getFirstGID()); + + int u = idIndex % tileCountX; + int v = idIndex / tileCountY; + u *= mapTileSize.x; //TODO we should be using the tile set size, as this may be different from the map's grid size + v *= mapTileSize.y; + + //normalise the UV + u /= textureSize.x; + v /= textureSize.y; + + //vert pos + const float tilePosX = static_cast(x) * mapTileSize.x; + const float tilePosY = (static_cast(y) * mapTileSize.y); + + Map::addTile(tilePosX, tilePosY, mapTileSize, u, v, zIndex, texturePaths.at(i).c_str()); + } + } + if (layer->getType() == tmx::Layer::Type::Object) { + // spawn objects + continue; + } + } + } +} + +void Map::addTile(float x, float y, const tmx::Vector2u& mapTileSize, int u, int v, int zIndex, const char* texturePath) +{ + auto& tile(VEGO_Game().manager.addEntity()); + + tile.addComponent(x, y, mapTileSize.x, mapTileSize.y, 1); + tile.addComponent(texturePath, v, u, zIndex); // why does uv need to be reversed? } \ No newline at end of file diff --git a/src/SpriteComponent.cpp b/src/SpriteComponent.cpp index 6c1963d..ae27ea7 100644 --- a/src/SpriteComponent.cpp +++ b/src/SpriteComponent.cpp @@ -15,7 +15,12 @@ #include "Manager.h" #include "VEGO.h" -SpriteComponent::SpriteComponent(const char* path, int zIndex) : RenderObject(zIndex, VEGO_Game().renderManager) +SpriteComponent::SpriteComponent(const char* path, int zIndex) : RenderObject(zIndex, VEGO_Game().renderManager), textureXOffset(0), textureYOffset(0) +{ + this->texturePath = path; +} + +SpriteComponent::SpriteComponent(const char* path, int xOffset, int yOffset, int zIndex) : RenderObject(zIndex, VEGO_Game().renderManager), textureXOffset(xOffset), textureYOffset(yOffset) { this->texturePath = path; } @@ -25,7 +30,7 @@ SpriteComponent::SpriteComponent( bool isAnimated, std::map>* animationMap, std::string defaultAnimation, - int zIndex) : RenderObject(zIndex, VEGO_Game().renderManager) + int zIndex) : RenderObject(zIndex, VEGO_Game().renderManager), textureXOffset(0), textureYOffset(0) { animated = isAnimated; @@ -49,20 +54,22 @@ void SpriteComponent::init() this->transform = &entity->getComponent(); - this->srcRect.x = this->srcRect.y = 0; this->srcRect.w = transform->width; this->srcRect.h = transform->height; + this->srcRect.x = this->textureXOffset * this->srcRect.w; + this->srcRect.y = this->textureYOffset * this->srcRect.h;; this->update(); } void SpriteComponent::update() { + // This code is not compatible for animated tiles if (animated) { srcRect.x = srcRect.w * static_cast((SDL_GetTicks() / speed) % frames); - } - srcRect.y = animationIndex * transform->height; + srcRect.y = animationIndex * transform->height; + } this->destRect.x = this->transform->position.x; this->destRect.y = this->transform->position.y;