Это классический тип задач, в которых вы используете рекурсию. Рекурсия сама по себе может быть сложной задачей, но особенно при работе с крошечным xml - не самым дружелюбным API.
ШАГ 1: A find
Helper
Давайте сделаем его более дружелюбным. На всех уровнях мы захотим посетить все элементы «сущности». Давайте сделаем удобный помощник для использования Tiny XML, чтобы получить эти:
auto find(TiXmlElement const* node, char const* name) {
std::vector<TiXmlElement const*> found;
for (
auto el = node->FirstChildElement(name);
el;
el = el->NextSiblingElement(name)
)
{
found.push_back(el);
}
return found;
}
Пока все хорошо, теперь мы можем более легко запрашивать узлы с определенным именем.
ШАГ 2: Разбор отдельной сущности
Давайте на секунду забудем об этом помощнике и предположим, что у нас уже выбран узел «сущность». Теперь мы хотим преобразовать его в сущность (имя, спрайт и т. Д. c.) И вернуть вновь созданную сущность:
Entity* parse_entity(TiXmlElement const* node) {
Entity* entity = g_manager.createEntity(node->Attribute("name"));
// todo transforms, sprite info etc.
return entity;
}
ШАГ 3: Построить дерево
Это звучит как сложная часть. Но на самом деле, используя наш помощник find
сверху, все, что нам нужно, это:
void parse_sub_entities(TiXmlElement const* node, Entity* parent = nullptr) {
for (auto el : find(node, "entity")) {
auto entity = parse_entity(el);
if (parent && entity) {
entity->addParent(parent);
parent->addChild(entity);
}
parse_sub_entities(el, entity);
}
}
Все дочерние узлы анализируются с использованием parse_entity
из шага 2, и только если у нас есть родительский узел, мы добавить отношения.
Чтобы завершить sh все это, мы рекурсивно в дочерние сущности, на этот раз передавая текущую сущность как родительскую.
Полная демонстрация
** Not Live On Coliru ** ¹
#include <tinyxml.h>
#include <vector>
#include <list>
#include <iostream>
#include <iomanip>
namespace { // helper functions for XML searching
auto find(TiXmlElement const* node, char const* name) {
std::vector<TiXmlElement const*> found;
for (
auto el = node->FirstChildElement(name);
el;
el = el->NextSiblingElement(name)
)
{
found.push_back(el);
}
return found;
}
}
auto scene = R"(<scene>
<entity name="Parent Entity">
<transform posx="500" posy="100" scalex="1" scaley="1" rotation="0"/>
<sprite image="Assets/Sprites/Avatar2.png"/>
<entity name="Child Entity">
<transform posx="0" posy="0" scalex="1" scaley="1" rotation="0"/>
<sprite image="crimson-logo.png"/>
</entity>
</entity>
</scene>)";
struct Entity {
std::string _name;
Entity* _parent = nullptr;
std::vector<Entity*> _children;
explicit Entity(std::string name = "unnamed") : _name(std::move(name)) {}
void addChild(Entity* e) { _children.push_back(e); }
void addParent(Entity* e) {
assert(!_parent || _parent == e);
_parent = e;
}
};
struct Mgr {
std::list<Entity> _entities;
Entity* createEntity(std::string name) {
return &_entities.emplace_back(name);
};
} g_manager;
Entity* parse_entity(TiXmlElement const* node) {
Entity* entity = g_manager.createEntity(node->Attribute("name"));
// todo transforms, sprite info etc.
return entity;
}
void parse_sub_entities(TiXmlElement const* node, Entity* parent = nullptr) {
for (auto el : find(node, "entity")) {
auto entity = parse_entity(el);
if (parent && entity) {
entity->addParent(parent);
parent->addChild(entity);
}
parse_sub_entities(el, entity);
}
}
int main() {
TiXmlDocument doc;
doc.Parse(scene);
parse_sub_entities(doc.RootElement());
std::cout << "Manager has " << g_manager._entities.size() << " entities\n";
for (auto& e: g_manager._entities) {
std::cout << "==== Entity: " << std::quoted(e._name) << "\n";
if (e._parent)
std::cout << " - has parent " << std::quoted(e._parent->_name) << "\n";
for (auto child : e._children)
std::cout << " - has child " << std::quoted(child->_name) << "\n";
}
}
Распечатывает:
Manager has 2 entities
==== Entity: "Parent Entity"
- has child "Child Entity"
==== Entity: "Child Entity"
- has parent "Parent Entity"
¹ no tiny xml установлен на любом онлайн компилятор, который я знаю