Jump to content

Очерёдность вызова методов init() у AppWorldLogic и custom-компонента


photo

Recommended Posts

Добрый день, сотрудники компании 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":

image.png.53a79db6a23454f280de9cb2af6bb2cb.png

Из которого видно, что узел "tanks_overflow" имеет дочерний узел "mission" со свойством "mission" и следующими параметрами параметрами следующего вида:

image.png.7ff6385fe28137e5e7c862627ca4d0bd.png

, где параметр "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;
}

Вопрос:

  1. Разве метод AppWorldLogic::init() отрабатывает позже метода init() у custom-компонента? Ведь я провожу инициализацию системы компонент в классе AppSystemLogic::init() и ожидаю, что при вызове AppWorldLogic методы init() для всех компонент уже отработали. Что я не верно понимаю, что приводит к такому результату?
     
Link to comment

Здравствуйте.

При текущей реализации все компоненты инициализируются одновременно с AppWorldLogic. Поэтому нельзя вызывать старт миссии в AppWorldLogic::init(), т.к. не все компоненты завершили инициализацию. Можно активировать миссию в первом AppWorldLogic::update():

if (!tanks_overflow_logic->isActive())
    tanks_overflow_logic->start(training_mode);

 

Link to comment

Спасибо за совет, но даже при такой реализации, вызывая tanks_overflow_logic->start() из AppWorldLogic::update(), я так и не попадаю перед этим в LogicController::init() для инициализации используемой в start() переменной "mission". Т.о. у меня уже начинает выполняться AppWorldLogic::update(), а init-компонента ещё не отработал. 

Edited by nikolaj.kormushkin
Link to comment

Может есть какой-то механизм гарантирующий мне, что init()-ы всех  custom-компонент уже отработали?

Link to comment

Здравствуйте.

Я создал указанную Вами структуру миссии. Полностью повторил 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
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 (так, что не происходит никакой реакции на нажатие мыши или клавиатуры), после чего появляется следующая ошибка:

image.thumb.png.dc655c1784f8f9e89ac0fe7b05a88371.png

К сожалению при переходе через F10 (в MS Studio 2017) дальше метода update() выполнение передаётся на скрытые методы движка и я не имею возможности отладить процесс зависания:
  image.png.d2429828060e4a3aced83a8f8f43c359.png

Вопросы:

  1. Как можно отладить причину зависания в данной ситуации? Можно-ли отправить Вам проект для диагностики? Возьмётесь-ли вы диагностировать проблему?
  2. Можно попросить Вас выслать мне проект который Вы создали для диагностики первоначальной проблемы (если да, то загрузите его пожалуйста на Ваш ftp-сервер)?
Link to comment

Николай, здравствуйте!

Если дебаггер приказал долго жить, то можно отлаживаться по-старинке: выводя в лог сообщения или же просто комментировать код и смотреть в какой момент всё запустится снова.

Можете прислать нам проект, но, боюсь, что посмотрим мы его только на следующей неделе.

Спасибо!

How to submit a good bug report
---
FTP server for test scenes and user uploads:

Link to comment
11 minutes ago, silent said:

Николай, здравствуйте!

Если дебаггер приказал долго жить, то можно отлаживаться по-старинке: выводя в лог сообщения или же просто комментировать код и смотреть в какой момент всё запустится снова.

Можете прислать нам проект, но, боюсь, что посмотрим мы его только на следующей неделе.

Спасибо!

Спасибо, отправлю Вам чуть позже проект. Я ещё спросил, может-ли karpych11 передать свой рабочий проект, который он создал для анализа причины ошибок?

Link to comment
Quote

Я ещё спросил, может-ли karpych11 передать свой рабочий проект, который он создал для анализа причины ошибок?

К сожалению, он был удалён, так что присылать особо нечего.

How to submit a good bug report
---
FTP server for test scenes and user uploads:

Link to comment

Здравствуйте.

Проблема оказалась связана с компонентой 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
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();
	...
	...

 

Огромное спасибо за диагностику проблемы.

  1. Скажите пожалуйста, я мог-бы провести диагностику своими силами или для диагностики Вам потребовались исходные коды движка? Если да, то какова методология действий при такой ситуации? Меня ограничило то, что я не смог в отладке (через F10) пройти дальше AppWorldLogic::update(), как показано на скрине выше - выполнение по стэку вело на методы в исходнике enginecallbacks.cpp движка. Может у Вас есть советы, что следует проделать дополнительно в таких ситуациях для отладки? 
  2. А как ещё можно реализовать отображение связи между объектом и сообщением подсказки помимо WorldSplineGraph, при этом иметь изящную(изогнутую, не прямую) линию?
Edited by nikolaj.kormushkin
Link to comment

Здравствуйте.

В данном случае исходный код движка не понадобился. Проблемная компонента была найдена с помощью поочередного отключения логики миссии. Далее уже внутри компоненты отключалась логика линии, стрелки и таблички. Таким образом была выявлена проблема зависания. В AppWorldLogic::Update() начинался старт миссии, что приводило к включению подсказки. На этом моменте происходило зависание в движке, поэтому Вы не могли при отладке пройти дальше этого места.

Реализацию линии можно посмотреть в демо Superposition. Скопируйте его как проект. Там есть ObjTooltip.h и ObjTooltip.cpp. Линия создается с помощью ObjectMeshDynamic. Вам потребуется только тот код, который работает с переменной obj_line.

Link to comment
×
×
  • Create New...