nikolaj.kormushkin Posted February 4, 2020 Posted February 4, 2020 Добрый день, сотрудники компании Unigine. Использую custom-компонент TanksOverflowLogic: class TanksOverflowLogic : public InteractiveMode { public: COMPONENT(TanksOverflowLogic, InteractiveMode); PROP_NAME("tanks_overflow_logic"); PROP_AUTOSAVE(1); private: void on_start(int is_training_mode) override; void on_stop() override; }; Компонент InteractiveMode, подобный реализованному в проекте "oiler_refinery_sample" (приведу в урезанном варианте, для наглядности поста): class InteractiveMode : public LogicController { public: COMPONENT(InteractiveMode, LogicController); PROP_PARAM(Node, inventory); PROP_PARAM(Node, checklist); PROP_PARAM(Node, task_helpers); PROP_PARAM(Node, interactive_borders); protected: void on_init() override; ... }; И компонент LogicController, так-же полностью взят из проекта "oiler_refinery_sample" (приведу в урезанном варианте, для наглядности поста): class LogicController : public ComponentBase { public: COMPONENT(LogicController, ComponentBase); COMPONENT_INIT(init, 3); PROP_PARAM(Node, player_spawn_node) PROP_PARAM(Node, start_point); PROP_PARAM(Node, floor_node); // logic control methods void start(int is_training_mode); void stop(); protected: virtual void on_init() {}; virtual void on_start(int is_training_mode) {}; virtual void on_stop() {}; ... }; Соответственно, имя такую иерархию зависимостей между компонентами я хочу при инициализации мира, запустить режим TanksOverflowLogic. Для этого я создал узел "tanks_overflow" (NodeDummy) в редакторе, повесил на него свойство "tanks_overflow_logic", навешал параметры (inventory, checklist, task_helpers и т.д.). Класс AppWorldLogic имеет внутреннюю переменную-указатель "tanks_overflow_logic" и метод "setTanksOverflowInteractiveMode()": class AppWorldLogic : public Unigine::WorldLogic { public: AppWorldLogic(); virtual ~AppWorldLogic(); virtual int init(); ... private: ... TanksOverflowLogic *tanks_overflow_logic; public: void setTanksOverflowInteractiveMode(int training_mode); }; В методе "AppWorldLogic::init()" я инициализирую указатель "tanks_overflow_logic" и вызываю метод "setTanksOverflowInteractiveMode(1)" (многоточие символизирует часть кода, которая не важна в данный момент). lo: void AppWorldLogic::setTanksOverflowInteractiveMode(int training_mode) { ... NodePtr logic_layer = Editor::get()->getNodeByName("logic_layer"); if (!logic_layer) { Log::error("APP WORLD LOGIC ERROR: logic layer not found\n"); return 0; } tanks_overflow_logic = ComponentSystem::get()->getComponentInChildren<TanksOverflowLogic>(logic_layer); ... setTanksOverflowInteractiveMode(1); } При этому дерево дочерних узлов у узла "logic_layer" имеет вид представленный в файле "logic_layer.png": Из которого видно, что узел "tanks_overflow" имеет дочерний узел "mission" со свойством "mission" и следующими параметрами параметрами следующего вида: , где параметр "Function" = "all" и "Hierarchy Based" = "true", что говорит о том, что компонентом должны отслеживаться состояния всех дочерних узлов (в данном случае, единственного - "open_the_valve"). Метод "AppWorldLogic::setTanksOverflowInteractiveMode(int training_mode)" имеет вид: void AppWorldLogic::setTanksOverflowInteractiveMode(int training_mode) { ... tanks_overflow_logic->start(training_mode); } и метод LogicController::start(): void LogicController::start(int is_training_mode) { ... mission_handler_id = mission->setOnStateChangedListener(std::bind(&LogicController::on_mission_state_changed_handler, this, std::placeholders::_1, std::placeholders::_2)); ... } В этой строке я получаю NULL-исключение, для внутренней переменной mission. Инициализация переменно mission происходит в методе "LogicController::init()" следующим образом: mission = getComponentInChildren<Mission>(node); , но отладка показывает, что я не попадаю в LogicController::init(), т.е. инициализация компонента LogicLayer ещё не произошла, а я пытаюсь вызвать его метод "start()". При этом в AppSystemLogic:init() делаю инициализацию системы компонентов: int AppSystemLogic::init() { ComponentSystem::get()->initialize(); return 1; } Вопрос: Разве метод AppWorldLogic::init() отрабатывает позже метода init() у custom-компонента? Ведь я провожу инициализацию системы компонент в классе AppSystemLogic::init() и ожидаю, что при вызове AppWorldLogic методы init() для всех компонент уже отработали. Что я не верно понимаю, что приводит к такому результату?
karpych11 Posted February 4, 2020 Posted February 4, 2020 Здравствуйте. При текущей реализации все компоненты инициализируются одновременно с AppWorldLogic. Поэтому нельзя вызывать старт миссии в AppWorldLogic::init(), т.к. не все компоненты завершили инициализацию. Можно активировать миссию в первом AppWorldLogic::update(): if (!tanks_overflow_logic->isActive()) tanks_overflow_logic->start(training_mode);
nikolaj.kormushkin Posted February 5, 2020 Author Posted February 5, 2020 (edited) Спасибо за совет, но даже при такой реализации, вызывая tanks_overflow_logic->start() из AppWorldLogic::update(), я так и не попадаю перед этим в LogicController::init() для инициализации используемой в start() переменной "mission". Т.о. у меня уже начинает выполняться AppWorldLogic::update(), а init-компонента ещё не отработал. Edited February 5, 2020 by nikolaj.kormushkin
nikolaj.kormushkin Posted February 6, 2020 Author Posted February 6, 2020 Может есть какой-то механизм гарантирующий мне, что init()-ы всех custom-компонент уже отработали?
karpych11 Posted February 6, 2020 Posted February 6, 2020 Здравствуйте. Я создал указанную Вами структуру миссии. Полностью повторил tanks_overflow_logic, mission и open_the_valve. Но в параметры tanks_overflow_logic добавил Floor Node, Task Helpers и Interactive Borders, которые в Вашем примере не были указаны. Для теста эти параметры аналогичны тем, которые указаны в generator_logic. После этих изменений данная миссия стала корректно запускаться. При текущей реализации данные параметры являются необходимыми. В противном случае LogicController не сможет корректно инициализироваться, т.к. в функции init() есть проверка: if (!floor_node) { #if DEBUG Log::error("Logic Controller error: floor_node is null, please check %s\n", node->getName()); #endif return; } В логе должна будет отобразиться ошибка. Проверьте, пожалуйста, этот момент. Также при необходимости можно убрать неиспользуемые параметры в логике. Но следует убедиться, что будут также исправлены все компоненты, которые были связаны с ними.
nikolaj.kormushkin Posted February 11, 2020 Author Posted February 11, 2020 On 2/6/2020 at 3:22 PM, karpych11 said: Здравствуйте. Я создал указанную Вами структуру миссии. Полностью повторил tanks_overflow_logic, mission и open_the_valve. Но в параметры tanks_overflow_logic добавил Floor Node, Task Helpers и Interactive Borders, которые в Вашем примере не были указаны... Добрый день, спасибо, что указали на мою невнимательность в коде метода "LogicController::init()". Действительно, точку останова я поставил дальше этих return-ов и судил по ней, что я не попадаю в метод init() custom-компонента. Но к сожалению после исправления данных недочётов я наткнулся на проблему которую не знаю как отладить. После завершения метода AppWorldLogic::update() происходит зависание системы минут на 10 (так, что не происходит никакой реакции на нажатие мыши или клавиатуры), после чего появляется следующая ошибка: К сожалению при переходе через F10 (в MS Studio 2017) дальше метода update() выполнение передаётся на скрытые методы движка и я не имею возможности отладить процесс зависания: Вопросы: Как можно отладить причину зависания в данной ситуации? Можно-ли отправить Вам проект для диагностики? Возьмётесь-ли вы диагностировать проблему? Можно попросить Вас выслать мне проект который Вы создали для диагностики первоначальной проблемы (если да, то загрузите его пожалуйста на Ваш ftp-сервер)?
silent Posted February 11, 2020 Posted February 11, 2020 Николай, здравствуйте! Если дебаггер приказал долго жить, то можно отлаживаться по-старинке: выводя в лог сообщения или же просто комментировать код и смотреть в какой момент всё запустится снова. Можете прислать нам проект, но, боюсь, что посмотрим мы его только на следующей неделе. Спасибо! How to submit a good bug report --- FTP server for test scenes and user uploads: ftp://files.unigine.com user: upload password: 6xYkd6vLYWjpW6SN
nikolaj.kormushkin Posted February 11, 2020 Author Posted February 11, 2020 11 minutes ago, silent said: Николай, здравствуйте! Если дебаггер приказал долго жить, то можно отлаживаться по-старинке: выводя в лог сообщения или же просто комментировать код и смотреть в какой момент всё запустится снова. Можете прислать нам проект, но, боюсь, что посмотрим мы его только на следующей неделе. Спасибо! Спасибо, отправлю Вам чуть позже проект. Я ещё спросил, может-ли karpych11 передать свой рабочий проект, который он создал для анализа причины ошибок?
silent Posted February 11, 2020 Posted February 11, 2020 Quote Я ещё спросил, может-ли karpych11 передать свой рабочий проект, который он создал для анализа причины ошибок? К сожалению, он был удалён, так что присылать особо нечего. How to submit a good bug report --- FTP server for test scenes and user uploads: ftp://files.unigine.com user: upload password: 6xYkd6vLYWjpW6SN
karpych11 Posted February 17, 2020 Posted February 17, 2020 Здравствуйте. Проблема оказалась связана с компонентой tooltip, которая используется в подсказках к заданиям. В ней линия от предмета к надписи отображается с помощью WorldSplineGraph. Его перестроение происходит при вызове connector->rebuild(1). Если этот метод вызывается в первых кадрах при запуске, то происходит зависание приложения. Для решения этой проблемы желательно отказаться от использования данного объекта, и создавать соединяющую линию самому. Также возможно сделать ожидание нескольких кадров в компоненте следующим образом: в Tooltip.h добавить int wait_frames = 5; в Tooltip.cpp добавить: void Tooltip::update() { if (!current_state) return; if (wait_frames != 0) wait_frames--; ... ... if (segment) { if (wait_frames == 0) { Mat4 connector_iwt = connector->getIWorldTransform(); ... ...
nikolaj.kormushkin Posted February 17, 2020 Author Posted February 17, 2020 (edited) 19 hours ago, karpych11 said: Здравствуйте. Проблема оказалась связана с компонентой tooltip, которая используется в подсказках к заданиям. В ней линия от предмета к надписи отображается с помощью WorldSplineGraph. Его перестроение происходит при вызове connector->rebuild(1). Если этот метод вызывается в первых кадрах при запуске, то происходит зависание приложения. Для решения этой проблемы желательно отказаться от использования данного объекта, и создавать соединяющую линию самому. Также возможно сделать ожидание нескольких кадров в компоненте следующим образом: в Tooltip.h добавить int wait_frames = 5; в Tooltip.cpp добавить: void Tooltip::update() { if (!current_state) return; if (wait_frames != 0) wait_frames--; ... ... if (segment) { if (wait_frames == 0) { Mat4 connector_iwt = connector->getIWorldTransform(); ... ... Огромное спасибо за диагностику проблемы. Скажите пожалуйста, я мог-бы провести диагностику своими силами или для диагностики Вам потребовались исходные коды движка? Если да, то какова методология действий при такой ситуации? Меня ограничило то, что я не смог в отладке (через F10) пройти дальше AppWorldLogic::update(), как показано на скрине выше - выполнение по стэку вело на методы в исходнике enginecallbacks.cpp движка. Может у Вас есть советы, что следует проделать дополнительно в таких ситуациях для отладки? А как ещё можно реализовать отображение связи между объектом и сообщением подсказки помимо WorldSplineGraph, при этом иметь изящную(изогнутую, не прямую) линию? Edited February 18, 2020 by nikolaj.kormushkin
karpych11 Posted February 18, 2020 Posted February 18, 2020 Здравствуйте. В данном случае исходный код движка не понадобился. Проблемная компонента была найдена с помощью поочередного отключения логики миссии. Далее уже внутри компоненты отключалась логика линии, стрелки и таблички. Таким образом была выявлена проблема зависания. В AppWorldLogic::Update() начинался старт миссии, что приводило к включению подсказки. На этом моменте происходило зависание в движке, поэтому Вы не могли при отладке пройти дальше этого места. Реализацию линии можно посмотреть в демо Superposition. Скопируйте его как проект. Там есть ObjTooltip.h и ObjTooltip.cpp. Линия создается с помощью ObjectMeshDynamic. Вам потребуется только тот код, который работает с переменной obj_line.
Recommended Posts