From 82c44b966f72753bc2d733c26f0da01d18d89f13 Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Tue, 10 Feb 2026 03:21:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(node):=20=E4=BC=98=E5=8C=96=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E7=AE=A1=E7=90=86=E5=92=8C=E7=A9=BA=E9=97=B4=E7=B4=A2?= =?UTF-8?q?=E5=BC=95=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 SpatialHash 中增加节点存在性检查,避免重复插入 - 为 Node 类添加批量添加子节点方法 addChildren - 使用哈希表优化子节点和动作的查找性能 - 重构成员变量布局以优化内存使用 - 改进世界变换计算方式,防止栈溢出 - 增强渲染命令收集功能,支持递归子节点 --- Extra2D/include/extra2d/scene/node.h | 87 +++++++++----- Extra2D/src/scene/node.cpp | 172 ++++++++++++++++++++++----- Extra2D/src/spatial/spatial_hash.cpp | 18 ++- 3 files changed, 217 insertions(+), 60 deletions(-) diff --git a/Extra2D/include/extra2d/scene/node.h b/Extra2D/include/extra2d/scene/node.h index 4c76683..b09cf79 100644 --- a/Extra2D/include/extra2d/scene/node.h +++ b/Extra2D/include/extra2d/scene/node.h @@ -30,6 +30,13 @@ public: // 层级管理 // ------------------------------------------------------------------------ void addChild(Ptr child); + + /** + * @brief 批量添加子节点 + * @param children 子节点列表 + */ + void addChildren(std::vector> &&children); + void removeChild(Ptr child); void removeChildByName(const std::string &name); void removeFromParent(); @@ -161,42 +168,62 @@ protected: float getOpacityRef() { return opacity_; } private: - // 层级 - WeakPtr parent_; - std::vector> children_; - bool childrenOrderDirty_ = false; + // ========================================================================== + // 成员变量按类型大小降序排列,减少内存对齐填充 + // 64位系统对齐:std::string(32) > glm::mat4(64) > std::vector(24) > + // double(8) > float(4) > int(4) > bool(1) + // ========================================================================== - // 变换 - Vec2 position_ = Vec2::Zero(); - float rotation_ = 0.0f; - Vec2 scale_ = Vec2(1.0f, 1.0f); - Vec2 anchor_ = Vec2(0.5f, 0.5f); - Vec2 skew_ = Vec2::Zero(); - float opacity_ = 1.0f; - bool visible_ = true; - int zOrder_ = 0; + // 1. 大块内存(64字节) + mutable glm::mat4 localTransform_; // 64 bytes + mutable glm::mat4 worldTransform_; // 64 bytes - // 缓存 - mutable bool transformDirty_ = true; - mutable bool worldTransformDirty_ = true; // 世界变换独立的脏标记 - mutable glm::mat4 localTransform_; - mutable glm::mat4 worldTransform_; + // 2. 字符串和容器(24-32字节) + std::string name_; // 32 bytes + std::vector> children_; // 24 bytes - // 元数据 - std::string name_; - int tag_ = -1; + // 3. 子节点索引(加速查找) + std::unordered_map> nameIndex_; // 56 bytes + std::unordered_map> tagIndex_; // 56 bytes - // 状态 - bool running_ = false; - Scene *scene_ = nullptr; - bool spatialIndexed_ = true; // 是否参与空间索引 - Rect lastSpatialBounds_; // 上一次的空间索引边界(用于检测变化) + // 4. 动作系统(使用 unordered_map 加速 tag 查找) + std::unordered_map> actionByTag_; // 56 bytes + std::vector> actions_; // 24 bytes(无 tag 的 Action) - // 动作 - std::vector> actions_; + // 5. 事件分发器 + EventDispatcher eventDispatcher_; // 大小取决于实现 - // 事件 - EventDispatcher eventDispatcher_; + // 6. 父节点引用 + WeakPtr parent_; // 16 bytes + + // 7. 变换属性(按访问频率分组) + Vec2 position_ = Vec2::Zero(); // 8 bytes + Vec2 scale_ = Vec2(1.0f, 1.0f); // 8 bytes + Vec2 anchor_ = Vec2(0.5f, 0.5f); // 8 bytes + Vec2 skew_ = Vec2::Zero(); // 8 bytes + + // 8. 边界框(用于空间索引) + Rect lastSpatialBounds_; // 16 bytes + + // 9. 浮点属性 + float rotation_ = 0.0f; // 4 bytes + float opacity_ = 1.0f; // 4 bytes + + // 10. 整数属性 + int zOrder_ = 0; // 4 bytes + int tag_ = -1; // 4 bytes + + // 11. 场景指针 + Scene *scene_ = nullptr; // 8 bytes + + // 12. 布尔标志(打包在一起) + mutable bool transformDirty_ = true; // 1 byte + mutable bool worldTransformDirty_ = true; // 1 byte + bool childrenOrderDirty_ = false; // 1 byte + bool visible_ = true; // 1 byte + bool running_ = false; // 1 byte + bool spatialIndexed_ = true; // 1 byte + // 填充 2 bytes 到 8 字节对齐 }; } // namespace extra2d diff --git a/Extra2D/src/scene/node.cpp b/Extra2D/src/scene/node.cpp index 490cb61..912729c 100644 --- a/Extra2D/src/scene/node.cpp +++ b/Extra2D/src/scene/node.cpp @@ -25,6 +25,14 @@ void Node::addChild(Ptr child) { children_.push_back(child); childrenOrderDirty_ = true; + // 更新索引 + if (!child->getName().empty()) { + nameIndex_[child->getName()] = child; + } + if (child->getTag() != -1) { + tagIndex_[child->getTag()] = child; + } + if (running_) { child->onEnter(); if (scene_) { @@ -33,16 +41,63 @@ void Node::addChild(Ptr child) { } } +void Node::addChildren(std::vector> &&children) { + // 预留空间,避免多次扩容 + size_t newSize = children_.size() + children.size(); + if (newSize > children_.capacity()) { + children_.reserve(newSize); + } + + for (auto &child : children) { + if (!child || child.get() == this) { + continue; + } + + child->removeFromParent(); + child->parent_ = weak_from_this(); + children_.push_back(child); + + // 更新索引 + if (!child->getName().empty()) { + nameIndex_[child->getName()] = child; + } + if (child->getTag() != -1) { + tagIndex_[child->getTag()] = child; + } + + if (running_) { + child->onEnter(); + if (scene_) { + child->onAttachToScene(scene_); + } + } + } + + if (!children.empty()) { + childrenOrderDirty_ = true; + } +} + void Node::removeChild(Ptr child) { if (!child) return; auto it = std::find(children_.begin(), children_.end(), child); if (it != children_.end()) { + // 始终从空间索引中移除(无论 running_ 状态) + // 这确保节点被正确清理 + (*it)->onDetachFromScene(); + if (running_) { - (*it)->onDetachFromScene(); (*it)->onExit(); } + // 从索引中移除 + if (!(*it)->getName().empty()) { + nameIndex_.erase((*it)->getName()); + } + if ((*it)->getTag() != -1) { + tagIndex_.erase((*it)->getTag()); + } (*it)->parent_.reset(); children_.erase(it); } @@ -80,22 +135,24 @@ void Node::removeAllChildren() { child->parent_.reset(); } children_.clear(); + nameIndex_.clear(); + tagIndex_.clear(); } Ptr Node::getChildByName(const std::string &name) const { - for (const auto &child : children_) { - if (child->getName() == name) { - return child; - } + // 使用哈希索引,O(1) 查找 + auto it = nameIndex_.find(name); + if (it != nameIndex_.end()) { + return it->second.lock(); } return nullptr; } Ptr Node::getChildByTag(int tag) const { - for (const auto &child : children_) { - if (child->getTag() == tag) { - return child; - } + // 使用哈希索引,O(1) 查找 + auto it = tagIndex_.find(tag); + if (it != tagIndex_.end()) { + return it->second.lock(); } return nullptr; } @@ -197,12 +254,26 @@ glm::mat4 Node::getLocalTransform() const { glm::mat4 Node::getWorldTransform() const { if (worldTransformDirty_) { - worldTransform_ = getLocalTransform(); - - auto p = parent_.lock(); - if (p) { - worldTransform_ = p->getWorldTransform() * worldTransform_; + // 迭代计算世界变换,避免深层级时的栈溢出 + // 收集父节点链 + std::vector nodeChain; + const Node *current = this; + while (current) { + nodeChain.push_back(current); + auto p = current->parent_.lock(); + current = p.get(); + // 限制最大深度,防止异常循环 + if (nodeChain.size() > 1000) { + break; + } } + + // 从根节点开始计算 + glm::mat4 transform = glm::mat4(1.0f); + for (auto it = nodeChain.rbegin(); it != nodeChain.rend(); ++it) { + transform = transform * (*it)->getLocalTransform(); + } + worldTransform_ = transform; worldTransformDirty_ = false; } return worldTransform_; @@ -313,33 +384,71 @@ void Node::updateSpatialIndex() { } void Node::runAction(Ptr action) { - if (action) { - action->start(this); - actions_.push_back(action); + if (!action) { + return; } + + action->start(this); + + int tag = action->getTag(); + if (tag != -1) { + // 有 tag 的 Action 存入哈希表,O(1) 查找 + // 如果已存在相同 tag 的 Action,先停止它 + auto it = actionByTag_.find(tag); + if (it != actionByTag_.end()) { + // 从 vector 中移除旧的 Action + auto oldAction = it->second; + auto vecIt = std::find(actions_.begin(), actions_.end(), oldAction); + if (vecIt != actions_.end()) { + actions_.erase(vecIt); + } + } + actionByTag_[tag] = action; + } + + actions_.push_back(action); } -void Node::stopAllActions() { actions_.clear(); } +void Node::stopAllActions() { + actions_.clear(); + actionByTag_.clear(); +} void Node::stopAction(Ptr action) { + if (!action) { + return; + } + + // 从 vector 中移除 auto it = std::find(actions_.begin(), actions_.end(), action); if (it != actions_.end()) { + // 如果有 tag,从哈希表中也移除 + int tag = action->getTag(); + if (tag != -1) { + actionByTag_.erase(tag); + } actions_.erase(it); } } void Node::stopActionByTag(int tag) { - auto it = std::remove_if( - actions_.begin(), actions_.end(), - [tag](const Ptr &action) { return action->getTag() == tag; }); - actions_.erase(it, actions_.end()); + auto it = actionByTag_.find(tag); + if (it != actionByTag_.end()) { + auto action = it->second; + // 从 vector 中移除 + auto vecIt = std::find(actions_.begin(), actions_.end(), action); + if (vecIt != actions_.end()) { + actions_.erase(vecIt); + } + actionByTag_.erase(it); + } } Ptr Node::getActionByTag(int tag) const { - for (const auto &action : actions_) { - if (action->getTag() == tag) { - return action; - } + // O(1) 哈希查找 + auto it = actionByTag_.find(tag); + if (it != actionByTag_.end()) { + return it->second; } return nullptr; } @@ -389,13 +498,20 @@ void Node::sortChildren() { void Node::collectRenderCommands(std::vector &commands, int parentZOrder) { - // 暂时最小化实现以测试 if (!visible_) return; - // 不排序,不递归,只生成当前节点的命令 + // 计算累积 Z 序 int accumulatedZOrder = parentZOrder + zOrder_; + + // 生成当前节点的渲染命令 generateRenderCommand(commands, accumulatedZOrder); + + // 递归收集子节点的渲染命令 + // 注意:这里假设子节点已经按 Z 序排序 + for (auto &child : children_) { + child->collectRenderCommands(commands, accumulatedZOrder); + } } } // namespace extra2d diff --git a/Extra2D/src/spatial/spatial_hash.cpp b/Extra2D/src/spatial/spatial_hash.cpp index d9dfed8..463707f 100644 --- a/Extra2D/src/spatial/spatial_hash.cpp +++ b/Extra2D/src/spatial/spatial_hash.cpp @@ -86,9 +86,17 @@ void SpatialHash::insert(Node *node, const Rect &bounds) { if (!node) return; + // 检查节点是否已存在,如果存在则先移除 + auto it = objectBounds_.find(node); + if (it != objectBounds_.end()) { + removeFromCells(node, it->second); + it->second = bounds; + } else { + objectBounds_[node] = bounds; + objectCount_++; + } + insertIntoCells(node, bounds); - objectBounds_[node] = bounds; - objectCount_++; } void SpatialHash::remove(Node *node) { @@ -100,6 +108,12 @@ void SpatialHash::remove(Node *node) { removeFromCells(node, it->second); objectBounds_.erase(it); objectCount_--; + } else { + // 节点不在 objectBounds_ 中,但可能还在 grid_ 的某些单元格中 + // 需要遍历所有单元格来移除 + for (auto &[cellKey, cell] : grid_) { + cell.remove(node); + } } }