From 67b64d782a7581770cc3f5ef39f0f4b165acf384 Mon Sep 17 00:00:00 2001 From: pflat <3509142-pflat@users.noreply.gitlab.com> Date: Fri, 8 Mar 2024 16:08:13 +0000 Subject: [PATCH] Add support for animeted tiles in tmx files --- .../LuaScript/pkgs/Urho2D/TileMapDefs2D.pkg | 14 +++++ .../LuaScript/pkgs/Urho2D/TileMapLayer2D.pkg | 2 + .../LuaScript/pkgs/Urho2D/TmxFile2D.pkg | 45 +++++++++++++++- Source/Urho3D/Urho2D/TileMap2D.cpp | 19 +++++++ Source/Urho3D/Urho2D/TileMap2D.h | 8 +++ Source/Urho3D/Urho2D/TileMapDefs2D.cpp | 53 +++++++++++++++++++ Source/Urho3D/Urho2D/TileMapDefs2D.h | 37 +++++++++++++ Source/Urho3D/Urho2D/TileMapLayer2D.cpp | 34 ++++++++++++ Source/Urho3D/Urho2D/TileMapLayer2D.h | 3 ++ Source/Urho3D/Urho2D/TmxFile2D.cpp | 25 +++++++++ Source/Urho3D/Urho2D/TmxFile2D.h | 8 +++ 11 files changed, 246 insertions(+), 2 deletions(-) diff --git a/Source/Urho3D/LuaScript/pkgs/Urho2D/TileMapDefs2D.pkg b/Source/Urho3D/LuaScript/pkgs/Urho2D/TileMapDefs2D.pkg index 64d2e56b88..f834f8e8e3 100644 --- a/Source/Urho3D/LuaScript/pkgs/Urho2D/TileMapDefs2D.pkg +++ b/Source/Urho3D/LuaScript/pkgs/Urho2D/TileMapDefs2D.pkg @@ -23,6 +23,12 @@ struct TileMapInfo2D tolua_readonly tolua_property__get_set float mapHeight; }; +struct TileFrameInfo2D +{ + unsigned gid_ @ gid; + unsigned duration_ @ duration; +}; + enum TileMapLayerType2D { LT_TILE_LAYER, @@ -47,6 +53,13 @@ class PropertySet2D const String GetProperty(const String name) const; }; +class FrameSet2D +{ + void UpdateTimer(float timeStep); + unsigned GetCurrentFrameGid() const; + unsigned GetNumFrames() const; +}; + static const unsigned FLIP_HORIZONTAL; static const unsigned FLIP_VERTICAL; static const unsigned FLIP_DIAGONAL; @@ -62,6 +75,7 @@ class Tile2D Sprite2D* GetSprite() const; bool HasProperty(const String name) const; const String GetProperty(const String name) const; + bool IsAnimated() const; tolua_readonly tolua_property__get_set unsigned gid; tolua_readonly tolua_property__get_set Sprite2D* sprite; diff --git a/Source/Urho3D/LuaScript/pkgs/Urho2D/TileMapLayer2D.pkg b/Source/Urho3D/LuaScript/pkgs/Urho2D/TileMapLayer2D.pkg index dd5abfd7c9..58889fcbb3 100644 --- a/Source/Urho3D/LuaScript/pkgs/Urho2D/TileMapLayer2D.pkg +++ b/Source/Urho3D/LuaScript/pkgs/Urho2D/TileMapLayer2D.pkg @@ -5,6 +5,8 @@ class TileMapLayer2D : Component void SetDrawOrder(int drawOrder); void SetVisible(bool visible); + void UpdateAnimations(); + int GetDrawOrder() const; bool IsVisible() const; bool HasProperty(const String name) const; diff --git a/Source/Urho3D/LuaScript/pkgs/Urho2D/TmxFile2D.pkg b/Source/Urho3D/LuaScript/pkgs/Urho2D/TmxFile2D.pkg index 95d521a11f..019faa655b 100644 --- a/Source/Urho3D/LuaScript/pkgs/Urho2D/TmxFile2D.pkg +++ b/Source/Urho3D/LuaScript/pkgs/Urho2D/TmxFile2D.pkg @@ -1,11 +1,52 @@ $#include "Urho2D/TmxFile2D.h" -// FIXME: complete the bindings for the rest of the class methods +class TmxLayer2D +{ + TmxFile2D* GetTmxFile() const; + TileMapLayerType2D GetType() const; + const String GetName() const; + int GetWidth() const; + int GetHeight() const; + bool IsVisible() const; + bool HasProperty(const String name) const; + const String GetProperty(const String name) const; +} + +class TmxTileLayer2D : TmxLayer2D +{ + Tile2D* GetTile(int x, int y) const; +} + +class TmxObjectGroup2D : TmxLayer2D +{ + unsigned GetNumObjects() const; + TileMapObject2D* GetObject(unsigned index) const; +} + +class TmxImageLayer2D : TmxLayer2D +{ + const Vector2 GetPosition() const; + const String GetSource() const; + Sprite2D* GetSprite() const; +} + class TmxFile2D : Resource { - void SetSpriteTextureEdgeOffset(float offset); + bool SetInfo(Orientation2D orientation, int width, int height, float tileWidth, float tileHeight); + void AddLayer(unsigned index, TmxLayer2D *layer); + void AddLayer(TmxLayer2D* layer); + const TileMapInfo2D GetInfo() const; + Sprite2D* GetTileSprite(unsigned gid) const; + PropertySet2D* GetTilePropertySet(unsigned gid) const; + FrameSet2D* GetTileFrameSet(unsigned gid) const; + + void UpdateAnimationTimers(float timeStep); + unsigned GetNumLayers() const; + const TmxLayer2D* GetLayer(unsigned index) const; + void SetSpriteTextureEdgeOffset(float offset); float GetSpriteTextureEdgeOffset() const; + tolua_readonly tolua_property__get_set TileMapInfo2D info; tolua_property__get_set float spriteTextureEdgeOffset @ edgeOffset; }; diff --git a/Source/Urho3D/Urho2D/TileMap2D.cpp b/Source/Urho3D/Urho2D/TileMap2D.cpp index 8d768ba4d6..74a9938b63 100644 --- a/Source/Urho3D/Urho2D/TileMap2D.cpp +++ b/Source/Urho3D/Urho2D/TileMap2D.cpp @@ -27,6 +27,7 @@ #include "../Resource/ResourceCache.h" #include "../Scene/Node.h" #include "../Scene/Scene.h" +#include "../Scene/SceneEvents.h" #include "../Urho2D/TileMap2D.h" #include "../Urho2D/TileMapLayer2D.h" #include "../Urho2D/TmxFile2D.h" @@ -55,6 +56,13 @@ void TileMap2D::RegisterObject(Context* context) AM_DEFAULT); } +void TileMap2D::OnNodeSet(Node* node) +{ + Scene* scene = GetScene(); + if (scene) + SubscribeToEvent(scene, E_SCENEUPDATE , URHO3D_HANDLER(TileMap2D, HandleSceneUpdate)); +} + // Transform vector from node-local space to global space static Vector2 TransformNode2D(const Matrix3x4& transform, Vector2 local) { @@ -191,4 +199,15 @@ Vector > TileMap2D::GetTileCollisionShapes(unsigned g return tmxFile_ ? tmxFile_->GetTileCollisionShapes(gid) : shapes; } +void TileMap2D::HandleSceneUpdate(StringHash eventType, VariantMap& eventData) +{ + using namespace SceneUpdate; + + float timeStep = eventData[P_TIMESTEP].GetFloat(); + tmxFile_->UpdateAnimationTimers(timeStep); + + for (unsigned l = 0; l < layers_.Size(); ++l) + layers_[l]->UpdateAnimations(); +} + } diff --git a/Source/Urho3D/Urho2D/TileMap2D.h b/Source/Urho3D/Urho2D/TileMap2D.h index daed4515da..1ce68f5107 100644 --- a/Source/Urho3D/Urho2D/TileMap2D.h +++ b/Source/Urho3D/Urho2D/TileMap2D.h @@ -45,6 +45,9 @@ class URHO3D_API TileMap2D : public Component /// @nobind static void RegisterObject(Context* context); + /// Handle node being assigned. + void OnNodeSet(Node* node) override; + /// Visualize the component as debug geometry. void DrawDebugGeometry(DebugRenderer* debug, bool depthTest) override; @@ -79,6 +82,11 @@ class URHO3D_API TileMap2D : public Component ResourceRef GetTmxFileAttr() const; /// Vector > GetTileCollisionShapes(unsigned gid) const; + +private: + /// Handle scene update. + void HandleSceneUpdate(StringHash eventType, VariantMap& eventData); + private: /// Tmx file. SharedPtr tmxFile_; diff --git a/Source/Urho3D/Urho2D/TileMapDefs2D.cpp b/Source/Urho3D/Urho2D/TileMapDefs2D.cpp index 1072f60736..70b0c64357 100644 --- a/Source/Urho3D/Urho2D/TileMapDefs2D.cpp +++ b/Source/Urho3D/Urho2D/TileMapDefs2D.cpp @@ -160,6 +160,51 @@ const String& PropertySet2D::GetProperty(const String& name) const return i->second_; } +FrameSet2D::FrameSet2D() = default; + +FrameSet2D::~FrameSet2D() = default; + +void FrameSet2D::Load(const XMLElement& element) +{ + assert(element.GetName() == "animation"); + for (XMLElement frameElem = element.GetChild("frame"); frameElem; frameElem = frameElem.GetNext("frame")) + { + SharedPtr info(new TileFrameInfo2D()); + info->gid_ = frameElem.GetUInt("tileid") + 1; + info->duration_ = frameElem.GetUInt("duration"); + frames_.Push(info); + + lapTime_ += info->duration_; + } + +} + +void FrameSet2D::UpdateTimer(float timeStep) +{ + timeElapsed_ += timeStep * 1000.0f; + if (timeElapsed_ > lapTime_) + timeElapsed_ -= lapTime_; +} + +unsigned FrameSet2D::GetCurrentFrameGid() const +{ + unsigned minInterval = 0; + unsigned maxInterval = 0; + for (unsigned i = 0; i < frames_.Size(); ++i) + { + minInterval = maxInterval; + maxInterval += frames_[i]->duration_; + if (timeElapsed_ >= static_cast(minInterval) && timeElapsed_ < static_cast(maxInterval)) + return frames_[i]->gid_; + } + return frames_[0]->gid_; +} + +unsigned FrameSet2D::GetNumFrames() const +{ + return frames_.Size(); +} + Tile2D::Tile2D() : gid_(0) { @@ -185,6 +230,14 @@ const String& Tile2D::GetProperty(const String& name) const return propertySet_->GetProperty(name); } +bool Tile2D::IsAnimated() const +{ + if (!frameSet_) + return false; + else + return true; +} + TileMapObject2D::TileMapObject2D() = default; unsigned TileMapObject2D::GetNumPoints() const diff --git a/Source/Urho3D/Urho2D/TileMapDefs2D.h b/Source/Urho3D/Urho2D/TileMapDefs2D.h index 6568ad44d7..1f5f31ef5e 100644 --- a/Source/Urho3D/Urho2D/TileMapDefs2D.h +++ b/Source/Urho3D/Urho2D/TileMapDefs2D.h @@ -74,6 +74,14 @@ struct URHO3D_API TileMapInfo2D bool PositionToTileIndex(int& x, int& y, const Vector2& position) const; }; +struct URHO3D_API TileFrameInfo2D : public RefCounted +{ + /// Gid; + unsigned gid_; + /// Duration. + unsigned duration_; +}; + /// Tile map layer type. enum TileMapLayerType2D { @@ -123,6 +131,31 @@ class URHO3D_API PropertySet2D : public RefCounted HashMap nameToValueMapping_; }; +/// Frame set. +class URHO3D_API FrameSet2D : public RefCounted +{ +public: + FrameSet2D(); + ~FrameSet2D() override; + + /// Load from XML element. + void Load(const XMLElement& element); + /// Update animation timer. + void UpdateTimer(float timeStep); + /// Get current tile gid of the animation. + unsigned GetCurrentFrameGid() const; + /// Get number of frames. + unsigned GetNumFrames() const; + +protected: + /// Animation frame infos. + Vector > frames_; + /// Time elapsed in the animation (circular timer). + float timeElapsed_{0.0f}; + /// Time it takes to complete one animation. + unsigned lapTime_{0}; +}; + /// Tile flipping flags. static const unsigned FLIP_HORIZONTAL = 0x80000000u; static const unsigned FLIP_VERTICAL = 0x40000000u; @@ -157,6 +190,8 @@ class URHO3D_API Tile2D : public RefCounted bool HasProperty(const String& name) const; /// Return property. const String& GetProperty(const String& name) const; + /// Checks if tile has animation frames. + bool IsAnimated() const; private: friend class TmxTileLayer2D; @@ -167,6 +202,8 @@ class URHO3D_API Tile2D : public RefCounted SharedPtr sprite_; /// Property set. SharedPtr propertySet_; + /// Frame set. + SharedPtr frameSet_; }; /// Tile map object. diff --git a/Source/Urho3D/Urho2D/TileMapLayer2D.cpp b/Source/Urho3D/Urho2D/TileMapLayer2D.cpp index 04e74df110..c2c57f5a92 100644 --- a/Source/Urho3D/Urho2D/TileMapLayer2D.cpp +++ b/Source/Urho3D/Urho2D/TileMapLayer2D.cpp @@ -246,6 +246,40 @@ void TileMapLayer2D::SetVisible(bool visible) } } +void TileMapLayer2D::UpdateAnimations() +{ + if (GetLayerType() != LT_TILE_LAYER) + return; + + TmxFile2D* tmxFile = GetTileMap()->GetTmxFile(); + for (int y = 0; y < GetHeight(); y++) + { + for (int x = 0; x < GetWidth(); x++) + { + Tile2D* tile = GetTile(x, y); + if (!tile) + continue; + + if (tile->IsAnimated()) + { + Node* node = GetTileNode(x, y); + if (node) + { + unsigned curGid = tile->GetGid(); + FrameSet2D* frameSet = tmxFile->GetTileFrameSet(curGid); + unsigned newGid = frameSet->GetCurrentFrameGid(); + if (curGid != newGid) + { + Sprite2D* sprite = tmxFile->GetTileSprite(newGid); + auto* spriteComponent = node->GetComponent(); + spriteComponent->SetSprite(sprite); + } + } + } + } + } +} + TileMap2D* TileMapLayer2D::GetTileMap() const { return tileMap_; diff --git a/Source/Urho3D/Urho2D/TileMapLayer2D.h b/Source/Urho3D/Urho2D/TileMapLayer2D.h index 2afa0c2762..c7cafe5642 100644 --- a/Source/Urho3D/Urho2D/TileMapLayer2D.h +++ b/Source/Urho3D/Urho2D/TileMapLayer2D.h @@ -66,6 +66,9 @@ class URHO3D_API TileMapLayer2D : public Component /// @property void SetVisible(bool visible); + /// For tile layers, update animated tile sprites. + void UpdateAnimations(); + /// Return tile map. TileMap2D* GetTileMap() const; diff --git a/Source/Urho3D/Urho2D/TmxFile2D.cpp b/Source/Urho3D/Urho2D/TmxFile2D.cpp index c0e3c48229..7ca21a1220 100644 --- a/Source/Urho3D/Urho2D/TmxFile2D.cpp +++ b/Source/Urho3D/Urho2D/TmxFile2D.cpp @@ -149,6 +149,7 @@ bool TmxTileLayer2D::Load(const XMLElement& element, const TileMapInfo2D& info) tile->gid_ = gid; tile->sprite_ = tmxFile_->GetTileSprite(gid & ~FLIP_ALL); tile->propertySet_ = tmxFile_->GetTilePropertySet(gid & ~FLIP_ALL); + tile->frameSet_ = tmxFile_->GetTileFrameSet(gid & ~FLIP_ALL); tiles_[y * width_ + x] = tile; } @@ -173,6 +174,7 @@ bool TmxTileLayer2D::Load(const XMLElement& element, const TileMapInfo2D& info) tile->gid_ = gid; tile->sprite_ = tmxFile_->GetTileSprite(gid & ~FLIP_ALL); tile->propertySet_ = tmxFile_->GetTilePropertySet(gid & ~FLIP_ALL); + tile->frameSet_ = tmxFile_->GetTileFrameSet(gid & ~FLIP_ALL); tiles_[y * width_ + x] = tile; } ++currentIndex; @@ -203,6 +205,7 @@ bool TmxTileLayer2D::Load(const XMLElement& element, const TileMapInfo2D& info) tile->gid_ = gid; tile->sprite_ = tmxFile_->GetTileSprite(gid & ~FLIP_ALL); tile->propertySet_ = tmxFile_->GetTilePropertySet(gid & ~FLIP_ALL); + tile->frameSet_ = tmxFile_->GetTileFrameSet(gid & ~FLIP_ALL); tiles_[y * width_ + x] = tile; } currentIndex += 4; @@ -579,6 +582,22 @@ PropertySet2D* TmxFile2D::GetTilePropertySet(unsigned gid) const return i->second_; } +FrameSet2D* TmxFile2D::GetTileFrameSet(unsigned gid) const +{ + HashMap >::ConstIterator i = gidToFrameSetMapping_.Find(gid); + if (i == gidToFrameSetMapping_.End()) + return nullptr; + return i->second_; +} + +void TmxFile2D::UpdateAnimationTimers(float timeStep) +{ + for (auto i = gidToFrameSetMapping_.Begin(); i != gidToFrameSetMapping_.End(); ++i) + { + i->second_->UpdateTimer(timeStep); + } +} + const TmxLayer2D* TmxFile2D::GetLayer(unsigned index) const { if (index >= layers_.Size()) @@ -738,6 +757,12 @@ bool TmxFile2D::LoadTileSet(const XMLElement& element) propertySet->Load(tileElem.GetChild("properties")); gidToPropertySetMapping_[gid] = propertySet; } + if (tileElem.HasChild("animation")) + { + SharedPtr frameSet(new FrameSet2D()); + frameSet->Load(tileElem.GetChild("animation")); + gidToFrameSetMapping_[gid] = frameSet; + } } if (!isSingleTileSet) diff --git a/Source/Urho3D/Urho2D/TmxFile2D.h b/Source/Urho3D/Urho2D/TmxFile2D.h index 0f7656e5dd..1b386ab9a4 100644 --- a/Source/Urho3D/Urho2D/TmxFile2D.h +++ b/Source/Urho3D/Urho2D/TmxFile2D.h @@ -193,6 +193,12 @@ class URHO3D_API TmxFile2D : public Resource /// Return tile property set by gid, if not exist return 0. PropertySet2D* GetTilePropertySet(unsigned gid) const; + /// Return tile frame set by gid, if not exist return 0. + FrameSet2D* GetTileFrameSet(unsigned gid) const; + + /// Updates FrameSet's timers, in all tile sets. + void UpdateAnimationTimers(float timeStep); + /// Return number of layers. unsigned GetNumLayers() const { return layers_.Size(); } @@ -223,6 +229,8 @@ class URHO3D_API TmxFile2D : public Resource HashMap > gidToSpriteMapping_; /// Gid to tile property set mapping. HashMap > gidToPropertySetMapping_; + /// Gid to tile frame set mapping. + HashMap > gidToFrameSetMapping_; /// Gid to tile collision shape mapping. HashMap > > gidToCollisionShapeMapping_; /// Layers.