nikolaj.kormushkin Posted February 4, 2020 Share 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() для всех компонент уже отработали. Что я не верно понимаю, что приводит к такому результату? Link to comment
karpych11 Posted February 4, 2020 Share Posted February 4, 2020 Здравствуйте. При текущей реализации все компоненты инициализируются одновременно с AppWorldLogic. Поэтому нельзя вызывать старт миссии в AppWorldLogic::init(), т.к. не все компоненты завершили инициализацию. Можно активировать миссию в первом AppWorldLogic::update(): if (!tanks_overflow_logic->isActive()) tanks_overflow_logic->start(training_mode); Link to comment
nikolaj.kormushkin Posted February 5, 2020 Author Share 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 Link to comment
nikolaj.kormushkin Posted February 6, 2020 Author Share Posted February 6, 2020 Может есть какой-то механизм гарантирующий мне, что init()-ы всех custom-компонент уже отработали? Link to comment
karpych11 Posted February 6, 2020 Share 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; } В логе должна будет отобразиться ошибка. Проверьте, пожалуйста, этот момент. Также при необходимости можно убрать неиспользуемые параметры в логике. Но следует убедиться, что будут также исправлены все компоненты, которые были связаны с ними. Link to comment
nikolaj.kormushkin Posted February 11, 2020 Author Share 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-сервер)? Link to comment
silent Posted February 11, 2020 Share 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 Link to comment
nikolaj.kormushkin Posted February 11, 2020 Author Share Posted February 11, 2020 11 minutes ago, silent said: Николай, здравствуйте! Если дебаггер приказал долго жить, то можно отлаживаться по-старинке: выводя в лог сообщения или же просто комментировать код и смотреть в какой момент всё запустится снова. Можете прислать нам проект, но, боюсь, что посмотрим мы его только на следующей неделе. Спасибо! Спасибо, отправлю Вам чуть позже проект. Я ещё спросил, может-ли karpych11 передать свой рабочий проект, который он создал для анализа причины ошибок? Link to comment
silent Posted February 11, 2020 Share 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 Link to comment
karpych11 Posted February 17, 2020 Share 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(); ... ... Link to comment
nikolaj.kormushkin Posted February 17, 2020 Author Share 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 Link to comment
karpych11 Posted February 18, 2020 Share Posted February 18, 2020 Здравствуйте. В данном случае исходный код движка не понадобился. Проблемная компонента была найдена с помощью поочередного отключения логики миссии. Далее уже внутри компоненты отключалась логика линии, стрелки и таблички. Таким образом была выявлена проблема зависания. В AppWorldLogic::Update() начинался старт миссии, что приводило к включению подсказки. На этом моменте происходило зависание в движке, поэтому Вы не могли при отладке пройти дальше этого места. Реализацию линии можно посмотреть в демо Superposition. Скопируйте его как проект. Там есть ObjTooltip.h и ObjTooltip.cpp. Линия создается с помощью ObjectMeshDynamic. Вам потребуется только тот код, который работает с переменной obj_line. Link to comment
Recommended Posts