diff --git a/cocos/2d/CCFastTMXLayer.cpp b/cocos/2d/CCFastTMXLayer.cpp index 5a38a1a9dd..2fa888b37d 100644 --- a/cocos/2d/CCFastTMXLayer.cpp +++ b/cocos/2d/CCFastTMXLayer.cpp @@ -93,6 +93,8 @@ bool FastTMXLayer::initWithTilesetInfo(TMXTilesetInfo *tilesetInfo, TMXLayerInfo // mapInfo _mapTileSize = mapInfo->getTileSize(); _layerOrientation = mapInfo->getOrientation(); + _staggerAxis = mapInfo->getStaggerAxis(); + _staggerIndex = mapInfo->getStaggerIndex(); // offset (after layer orientation is set); Vec2 offset = this->calculateLayerOffset(layerInfo->_offset); @@ -321,6 +323,57 @@ void FastTMXLayer::setupTiles() _screenTileCount = (int)(_screenGridSize.width * _screenGridSize.height); + if (!_tileSet->_animationInfo.empty()) { + /// FastTMXLayer: anim support + for (int y = 0; y < _layerSize.height; y++) + { + for (int x = 0; x < _layerSize.width; x++) + { + int newX = x; + // fix correct render ordering in Hexagonal maps when stagger axis == x + if (_staggerAxis == TMXStaggerAxis_X && _layerOrientation == TMXOrientationHex) + { + if (_staggerIndex == TMXStaggerIndex_Odd) + { + if (x >= _layerSize.width / 2) + newX = (x - std::ceil(_layerSize.width / 2)) * 2 + 1; + else + newX = x * 2; + } + else { + // TMXStaggerIndex_Even + if (x >= static_cast(_layerSize.width / 2)) + newX = (x - static_cast(_layerSize.width / 2)) * 2; + else + newX = x * 2 + 1; + } + } + + int pos = static_cast(newX + _layerSize.width * y); + int gid = _tiles[pos]; + + // gid are stored in little endian. + // if host is big endian, then swap + //if( o == CFByteOrderBigEndian ) + // gid = CFSwapInt32( gid ); + /* We support little endian.*/ + + // FIXME:: gid == 0 --> empty tile + if (gid != 0) + { + if (_tileSet->_animationInfo.find(gid) != _tileSet->_animationInfo.end()) + { + _animTileCoord[gid].push_back(Vec2(newX, y)); + } + } + } + } + + if (hasTileAnimation()) + { + _tileAnimManager = new TMXTileAnimManager(this); + } + } } Mat4 FastTMXLayer::tileToNodeTransform() @@ -910,5 +963,97 @@ std::string FastTMXLayer::getDescription() const return StringUtils::format("", _tag, (int)_mapTileSize.width, (int)_mapTileSize.height); } +TMXTileAnimManager::TMXTileAnimManager(FastTMXLayer* layer) +{ + _layer = layer; + for (const auto& p : *_layer->getAnimTileCoord()) + { + for (auto tilePos : p.second) + { + _tasks.pushBack(TMXTileAnimTask::create(_layer, _layer->getTileSet()->_animationInfo.at(p.first), tilePos)); + } + } +} + +TMXTileAnimManager* TMXTileAnimManager::create(FastTMXLayer* layer) +{ + TMXTileAnimManager* ret = new (std::nothrow) TMXTileAnimManager(layer); + if (ret) + { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; +} + +void TMXTileAnimManager::startAll() +{ + if (_started || _tasks.empty()) + return; + _started = true; + for (auto& task : _tasks) + { + task->start(); + } +} + +void TMXTileAnimManager::stopAll() +{ + if (!_started) + return; + _started = false; + for (auto& task : _tasks) + { + task->stop(); + } +} + +TMXTileAnimTask::TMXTileAnimTask(FastTMXLayer* layer, TMXTileAnimInfo* animation, const Vec2& tilePos) +{ + _layer = layer; + _animation = animation; + _frameCount = static_cast(_animation->_frames.size()); + _tilePosition = tilePos; + std::stringstream ss; + ss << "TickAnimOnTilePos(" << _tilePosition.x << "," << _tilePosition.y << ")"; + _key = ss.str(); +} + +void TMXTileAnimTask::tickAndScheduleNext(float dt) +{ + setCurrFrame(); + _layer->getParent()->scheduleOnce(CC_CALLBACK_1(TMXTileAnimTask::tickAndScheduleNext, this), _animation->_frames[_currentFrame]._duration / 1000.0f, _key); +} + +void TMXTileAnimTask::start() +{ + _isRunning = true; + tickAndScheduleNext(0.0f); +} + +void TMXTileAnimTask::stop() +{ + _isRunning = false; + _layer->getParent()->unschedule(_key); +} + +void TMXTileAnimTask::setCurrFrame() +{ + _layer->setTileGID(_animation->_frames[_currentFrame]._tileID, _tilePosition); + _currentFrame = (_currentFrame + 1) % _frameCount; +} + +TMXTileAnimTask* TMXTileAnimTask::create(FastTMXLayer* layer, TMXTileAnimInfo* animation, const Vec2& tilePos) +{ + TMXTileAnimTask* ret = new (std::nothrow) TMXTileAnimTask(layer, animation, tilePos); + if (ret) + { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; +} NS_CC_END diff --git a/cocos/2d/CCFastTMXLayer.h b/cocos/2d/CCFastTMXLayer.h index e3071c6706..0e9f59fedb 100644 --- a/cocos/2d/CCFastTMXLayer.h +++ b/cocos/2d/CCFastTMXLayer.h @@ -37,6 +37,7 @@ NS_CC_BEGIN class TMXMapInfo; class TMXLayerInfo; class TMXTilesetInfo; +class TMXTileAnimManager; class Texture2D; class Sprite; @@ -275,6 +276,21 @@ public: virtual void draw(Renderer *renderer, const Mat4& transform, uint32_t flags) override; void removeChild(Node* child, bool cleanup = true) override; + /** Map from gid of animated tile to its instance. + * + * @return Map from gid of animated tile to its instance. + */ + const std::unordered_map>* getAnimTileCoord() { + return &_animTileCoord; + } + + bool hasTileAnimation() const { + return !_animTileCoord.empty(); + } + + TMXTileAnimManager* getTileAnimManager() const { + return _tileAnimManager; + } protected: virtual void setOpacity(uint8_t opacity) override; @@ -315,9 +331,16 @@ protected: TMXTilesetInfo* _tileSet = nullptr; /** Layer orientation, which is the same as the map orientation */ int _layerOrientation = FAST_TMX_ORIENTATION_ORTHO; + int _staggerAxis = TMXStaggerAxis_Y; + int _staggerIndex = TMXStaggerIndex_Even; /** properties from the layer. They can be added using Tiled */ ValueMap _properties; + /** map from gid of animated tile to its instance. Also useful for optimization*/ + std::unordered_map> _animTileCoord; + /** pointer to the tile animation manager of this layer */ + TMXTileAnimManager* _tileAnimManager = nullptr; + Texture2D *_texture = nullptr; /** container for sprite children. map > */ @@ -356,6 +379,69 @@ protected: backend::UniformLocation _alphaValueLocation; }; +/** @brief TMXTileAnimTask represents the frame-tick task of an animated tile. + * It is a assistant class for TMXTileAnimTicker. + */ +class CC_DLL TMXTileAnimTask : public Ref +{ +public: + TMXTileAnimTask(FastTMXLayer* layer, TMXTileAnimInfo* animation, const Vec2& tilePos); + static TMXTileAnimTask* create(FastTMXLayer* layer, TMXTileAnimInfo* animation, const Vec2& tilePos); + /** start the animation task */ + void start(); + /** stop the animation task */ + void stop(); + bool isRunning() const { + return _isRunning; + } + +protected: + /** set texture of tile to current frame */ + void setCurrFrame(); + /** tick to next frame and schedule next tick */ + void tickAndScheduleNext(float dt); + + bool _isRunning = false; + /** key of schedule task for specific animated tile */ + std::string _key; + FastTMXLayer* _layer = nullptr; + /** position of the animated tile */ + Vec2 _tilePosition; + /** AnimationInfo on this tile */ + TMXTileAnimInfo* _animation = nullptr; + /** Index of the frame that should be drawn currently */ + uint32_t _currentFrame = 0; + uint32_t _frameCount = 0; +}; + +/** @brief TMXTileAnimManager controls all tile animation of a layer. + */ +class CC_DLL TMXTileAnimManager : public Ref +{ +public: + static TMXTileAnimManager* create(FastTMXLayer* layer); + explicit TMXTileAnimManager(FastTMXLayer* layer); + + /** start all tile animations */ + void startAll(); + /** stop all tile animations */ + void stopAll(); + + /** get vector of tasks */ + const Vector& getTasks() const { + return _tasks; + } + +protected: + bool _started = false; + /** vector contains all tasks of this layer */ + Vector _tasks; + FastTMXLayer* _layer = nullptr; +}; + +// @API compatible +typedef FastTMXLayer TMXLayer; + // end of tilemap_parallax_nodes group /// @} NS_CC_END diff --git a/cocos/2d/CCFastTMXTiledMap.cpp b/cocos/2d/CCFastTMXTiledMap.cpp index e7e39c1791..dc78858ff9 100644 --- a/cocos/2d/CCFastTMXTiledMap.cpp +++ b/cocos/2d/CCFastTMXTiledMap.cpp @@ -257,4 +257,22 @@ std::string FastTMXTiledMap::getDescription() const return StringUtils::format("(_children.size())); } +void FastTMXTiledMap::setTileAnimEnabled(bool enabled) +{ + for (auto& child : _children) + { + FastTMXLayer* layer = dynamic_cast(child); + if (layer) + { + if (layer->hasTileAnimation()) + { + if (enabled) + layer->getTileAnimManager()->startAll(); + else + layer->getTileAnimManager()->stopAll(); + } + } + } +} + NS_CC_END diff --git a/cocos/2d/CCFastTMXTiledMap.h b/cocos/2d/CCFastTMXTiledMap.h index 9b344dd8f6..c6116b1be7 100644 --- a/cocos/2d/CCFastTMXTiledMap.h +++ b/cocos/2d/CCFastTMXTiledMap.h @@ -201,6 +201,10 @@ public: virtual std::string getDescription() const override; + /** Set all tile animations enabled or not. + * animations are not enabled by default + */ + void setTileAnimEnabled(bool enabled); protected: /** * @js ctor @@ -243,6 +247,8 @@ private: // end of tilemap_parallax_nodes group /** @} */ - + +// @API compatible +typedef FastTMXTiledMap TMXTiledMap; NS_CC_END diff --git a/cocos/2d/CCTMXXMLParser.cpp b/cocos/2d/CCTMXXMLParser.cpp index 984ae3929d..9a483ed451 100644 --- a/cocos/2d/CCTMXXMLParser.cpp +++ b/cocos/2d/CCTMXXMLParser.cpp @@ -665,6 +665,19 @@ void TMXMapInfo::startElement(void* /*ctx*/, const char *name, const char **atts dict["polylinePoints"] = Value(pointsArray); } } + else if (elementName == "animation") + { + TMXTilesetInfo* info = tmxMapInfo->getTilesets().back(); + info->_animationInfo.insert(tmxMapInfo->getParentGID(), TMXTileAnimInfo::create(tmxMapInfo->getParentGID())); + tmxMapInfo->setParentElement(TMXPropertyAnimation); + } + else if (elementName == "frame") + { + TMXTilesetInfo* info = tmxMapInfo->getTilesets().back(); + auto animInfo = info->_animationInfo.at(tmxMapInfo->getParentGID()); + // calculate gid of frame + animInfo->_frames.emplace_back(TMXTileAnimFrame(info->_firstGid + attributeDict["tileid"].asInt(), attributeDict["duration"].asFloat())); + } } void TMXMapInfo::endElement(void* /*ctx*/, const char *name) @@ -785,6 +798,10 @@ void TMXMapInfo::endElement(void* /*ctx*/, const char *name) { _recordFirstGID = true; } + else if (elementName == "animation") + { + tmxMapInfo->setParentElement(TMXPropertyNone); + } } void TMXMapInfo::textHandler(void* /*ctx*/, const char *ch, size_t len) @@ -800,4 +817,28 @@ void TMXMapInfo::textHandler(void* /*ctx*/, const char *ch, size_t len) } } +TMXTileAnimFrame::TMXTileAnimFrame(uint32_t tileID, float duration) + : _tileID(tileID) + , _duration(duration) +{ +} + +TMXTileAnimInfo::TMXTileAnimInfo(uint32_t tileID) + : _tileID(tileID) +{ +} + +TMXTileAnimInfo* TMXTileAnimInfo::create(uint32_t tileID) +{ + TMXTileAnimInfo* ret = new (std::nothrow) TMXTileAnimInfo(tileID); + if (ret) + { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; +} + + NS_CC_END diff --git a/cocos/2d/CCTMXXMLParser.h b/cocos/2d/CCTMXXMLParser.h index 33c237a007..57db337281 100644 --- a/cocos/2d/CCTMXXMLParser.h +++ b/cocos/2d/CCTMXXMLParser.h @@ -35,6 +35,7 @@ THE SOFTWARE. #include "math/CCGeometry.h" #include "platform/CCSAXParser.h" #include "base/CCVector.h" +#include "base/CCMap.h" #include "base/CCValue.h" #include "2d/CCTMXObjectGroup.h" // needed for Vector for binding @@ -72,7 +73,8 @@ enum { TMXPropertyLayer, TMXPropertyObjectGroup, TMXPropertyObject, - TMXPropertyTile + TMXPropertyTile, + TMXPropertyAnimation }; typedef enum TMXTileFlags_ { @@ -125,6 +127,35 @@ enum TMXStaggerIndex_Even, }; +/** @brief TMXTileAnimFrame contains the information about the frame of a animated tile like: +- Frame gid +- duration of this frame + +This information is obtained from the TMX file. +*/ +struct CC_DLL TMXTileAnimFrame +{ + TMXTileAnimFrame(uint32_t tileID, float duration); + /** gid of the frame */ + uint32_t _tileID = 0; + /** duration of the frame */ + float _duration = 0.0f; +}; + +/** @brief TMXTileAnimInfo contains the information about the animated tile like: +- Animated Tile gid +- frames the animated tile contains + +This information is obtained from the TMX file. +*/ +struct CC_DLL TMXTileAnimInfo : public Ref +{ + static TMXTileAnimInfo* create(uint32_t tileID); + explicit TMXTileAnimInfo(uint32_t tileID); + uint32_t _tileID = 0; + std::vector _frames; +}; + // Bits on the far end of the 32-bit global tile ID (GID's) are used for tile flags /** @brief TMXLayerInfo contains the information about the layers like: @@ -185,7 +216,8 @@ public: //! size in pixels of the image Size _imageSize; std::string _originSourceImage; - + //! map from gid of animated tile to its animation info + Map _animationInfo; public: /** * @js ctor diff --git a/tests/cpp-tests/Classes/TileMapTest/TileMapTest2.cpp b/tests/cpp-tests/Classes/TileMapTest/TileMapTest2.cpp index 19b731abc3..4cf393973f 100644 --- a/tests/cpp-tests/Classes/TileMapTest/TileMapTest2.cpp +++ b/tests/cpp-tests/Classes/TileMapTest/TileMapTest2.cpp @@ -66,6 +66,7 @@ FastTileMapTests::FastTileMapTests() ADD_TEST_CASE(TMXBug987New); ADD_TEST_CASE(TMXBug787New); ADD_TEST_CASE(TMXGIDObjectsTestNew); + ADD_TEST_CASE(TileAnimTestNew); } TileDemoNew::TileDemoNew() @@ -1386,3 +1387,35 @@ std::string TMXGIDObjectsTestNew::subtitle() const { return "Tiles are created from an object group"; } + +//------------------------------------------------------------------ +// +// TileAnimTestNew +// +//------------------------------------------------------------------ +TileAnimTestNew::TileAnimTestNew() +{ + + map = FastTMXTiledMap::create("TileMaps/tile_animation_test.tmx"); + addChild(map, 0, kTagTileMap); + + auto listener = EventListenerTouchAllAtOnce::create(); + listener->onTouchesBegan = CC_CALLBACK_2(TileAnimTestNew::onTouchBegan, this); + _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this); + + Size CC_UNUSED s = map->getContentSize(); + CCLOG("ContentSize: %f, %f", s.width, s.height); + + map->setTileAnimEnabled(_animStarted); +} + +std::string TileAnimTestNew::title() const +{ + return "Tile animation test. Click to toggle the animation"; +} + +void TileAnimTestNew::onTouchBegan(const std::vector& touches, cocos2d::Event* event) +{ + _animStarted = !_animStarted; + map->setTileAnimEnabled(_animStarted); +} diff --git a/tests/cpp-tests/Classes/TileMapTest/TileMapTest2.h b/tests/cpp-tests/Classes/TileMapTest/TileMapTest2.h index 8fcb918064..c27459e753 100644 --- a/tests/cpp-tests/Classes/TileMapTest/TileMapTest2.h +++ b/tests/cpp-tests/Classes/TileMapTest/TileMapTest2.h @@ -334,4 +334,16 @@ public: virtual std::string subtitle() const override; }; +class TileAnimTestNew : public TileDemoNew +{ +public: + CREATE_FUNC(TileAnimTestNew); + TileAnimTestNew(); + virtual std::string title() const override; + + cocos2d::FastTMXTiledMap* map; + bool _animStarted = true; + void onTouchBegan(const std::vector& touches, cocos2d::Event* event); +}; + #endif