Winsor 178 Опубликовано 1 Ноября 2016 (изменено) добавление, например, на выстрел в Вашем примере - это всего лишь вызов в нужном месте что нибудь типа псевдокод вызова калбека на выстрел из класса оружия auto source=this; if (m_Owner==Actor()) { source=Actor(); } source->callback(типкалбекахит)(кто_стрелял, из_чего, чем); теперь в классе актора, или сталкера (да, нужен скриптовый класс биндера "script_binding= ", иначе у чего будет движок вызывать эти методы при их наличии?) регистрируем этот калбек через set_callback в одном вызове (ну и в одном очищаем) - чаще всего init/net_destroy. все, дальше обрабатываем так как надо. никаких load/save/update не требуется. еще один пример - реализация калбека на хит по объекту. движком генерируем вызов этого калбека из CGameObject (прародителя усех объектов в луа). в самих скриптах только в объектах (биндере), например, бочки делаем обработку этого калбека - и все, обрабатываются только бочки. И, да, само вот это вот "а на актора или еще куда Само собой, иногда приходиться идти на хитрость - есть окно инвентаря - вот куда ему добавить перехват калбека , если по умолчанию, ни сам класс этого окна не экспортируется в игру (это полбеды), ни в игре нету дочернего класса от CUIInventoryWnd, чтобы на нем добавить обработчики (как это например сделано для главного меню). Но со вторым вариантом возиться долго и не интересно. проще всегда делать Actor()->callback(калбек_из_окна)(окно_каллбека, другие параметры) и в биндере актора перехватить это калбек. все, чесно и прозрачно. Придумывать единый механизм, отличный от Actor() для таких целей - ну не знаю, не уверен что это окупиться. Кто уже довольно неплохо разгреб, в каких файлах функциях что лежит - может поделитесь Поделиться чем? в движке примерно 4к файлов *.cpp. Сформулируйте вопрос... Изменено 4 Января 2017 пользователем Kondr48 Немного подправил цитаты. 1 Поделиться этим сообщением Ссылка на сообщение
Winsor 178 Опубликовано 27 Января 2017 Поговорим немного о диалогах в ТЧ 1.0007. Наверное, большинство знают что диалоги (как, например, и артикли в энциклопедии) являются объектами, которые загружаются один раз за всю сессию игры (на основании паттерна Singleton) в структуру SPhraseDialogData : CSharedResource. т.е. если мы разрабатываем диалог, то для проверки того что именно мы написали необходимо выйти и зайти в игру полностью. Само собой это сводит на нет полностью работу с диалогами, создаваемыми скриптами (через експорт функций класса CPhraseDialog). Но часто хочется оживить Зону и разнообразить диалоги. 1) расширяем чуть чуть экспорт CPhraseDialog Добавляем в экспорт CPhraseDialog функцию SetPriority для изменения приоритета нашего диалога. Теперь он выглядит вот так: class_<CPhraseDialog>("CPhraseDialog") .def("AddPhrase", &CPhraseDialog::AddPhrase_script ) .def("SetPriority", &CPhraseDialog::SetPriority), Этим мы можем при создании диалогов помещать начало нашего графа , например, в самый верх или них списка начальных фраз. 2) В связи с тем что 80-90% диалогов все таки статические (им хватает для жизни preconditions) - добавляем параметр, который позволит управлять, обновлять ли диалог каждый раз, или только при первой загрузке в структуру struct SPhraseDialogData : CSharedResource добавляем поле-признак, например, bool b_bForceReload. в класс CPhraseDialog два метода public: void SetDialogForceReload(bool value=false) {data()->b_bForceReload=value;} bool GetDialogForceReload() {return data()->b_bForceReload;} CPhraseDialog::load_shared после SetCaption, например, добавляем SetDialogForceReload(pXML->ReadAttribInt(dialog_node, "force_reload", 0)==1? true:false); т.е. при наличии в конфиге диалога параметра force_reload="1" он помечается как перезагружаемый <dialog id="test_dynamic" force_reload="1"> <init_func>subtest.gen_dialog1</init_func> </dialog> 3) Добавляем в класс окна диалогов признак, который указывает, что окно в данный момент инициализируется. Это нужно в связи с тем что CPhraseDialogManager::AddAvailableDialog может вызываться из других мест, не только из инициализатора окна диалога. позволяет избежать, например, перестроения диалога, когда он уже начат с кем то из НПС. в класс CUITalkWnd добавляем protected: bool m_bInitState; public: bool GetInitState() const {return m_bInitState;} и инициализацию m_bInitState=false в конструктор класса. в методе void CUITalkWnd::InitTalkDialog() в начале пишемm_bInitState=true;и аналогично в концеm_bInitState=false;Теперь мы имеем признак того что CUITalkWnd инизиализирует графы диалогов. 4) Изменяем метод загрузки данных для конкретного диалога с использованием методов CSharedResource void CPhraseDialog::Load(shared_str dialog_id) { m_DialogId = dialog_id; bool need_load=inherited_shared::start_load_shared(m_DialogId); //делаем инициализацию SPhraseDialogData* при первом вызове. если синглтон не был создан ранее - он создается, иначе возвращается созданный //результат функции - это флаг, указывающий, требуется ли загрузка данных при первичной инициализации. if (need_load) //структура создается первый раз, ее надо просто загрузить, также таким образом можно избежать двойного вызова метода загрузки inherited_shared::load_shared(m_DialogId, nullptr); else if (GetDialogForceReload()) //структура уже создавалась и загружалась ранее { //но установлен флаг для принудительной перезагрузки CUIGameSP* ui_sp = smart_cast<CUIGameSP*>(HUD().GetUI()->UIGame()); if (ui_sp && ui_sp->TalkMenu->GetInitState()) //перезагружаем данные только тогда, когда идет инициализация окна диалога { data()->SetLoad(false);//для структуры SPhraseDialogData устанавливаем флаг принудительной загрузки inherited_shared::load_shared(m_DialogId, nullptr); //обновляем данные //inherited_shared::finish_load_shared вызывать не обязательно, так как load_shared уже выполняет его функционал. } } } Все. теперь при каждом открытии окна диалога у Вас будет перезагружаться скрипт subtest.gen_dialog1, который Вам позволит генерировать случайные фразы, например, через math.random Также, параметр force_reload можно использовать и для перезагрузки на моменте отладки и построения диалогов из конфига xml, но надо учитывать, что данный метод не перезагружает скрипты, которые используются в precondition, строковые константы в других xml файлах и подобные внешние ресурсы. 3 Поделиться этим сообщением Ссылка на сообщение
Winsor 178 Опубликовано 27 Января 2017 (изменено) Почему муторный? у меня граф перестраивается только для тех диалогов, которым нужно. Все диалоги перестраивать каждый раз - лишняя нагрузка. И он перестраивается полностью. Мы с Вами пошли просто разными путями. Вы, насколько я понял - пытались скриптом генерировать сам граф, я же генерацию графа оставил на движок, только сделал механизм перезагрузки. Как по мне - оба варианта имеют право на жизнь. Мой пост можно воспринимать как иллюстрацию - как победить shared_data.Она используется не только для диалогов. Изменено 27 Января 2017 пользователем Winsor 1 Поделиться этим сообщением Ссылка на сообщение
Winsor 178 Опубликовано 27 Января 2017 @Карлан, было бы неплохо, если бы Вы поделились частью исходников для изучения, как подобный механизм реализован у Вас, а именно CPhraseDialog::Load, CPhraseDialog::load_shared. xr_3da\xrGame\xml_str_id_loader.cpp тоже очень хотелось бы изучить Спасибо. 1 1 Поделиться этим сообщением Ссылка на сообщение
Winsor 178 Опубликовано 2 Февраля 2017 (изменено) В продолжении темы борьбы с диалогами - необходимо в выше приведенном xr_3da\xrGame\PhraseDialog.cpp void CPhraseDialog::Load(shared_str dialog_id) заменить на такой: void CPhraseDialog::Load(shared_str dialog_id) { m_DialogId = dialog_id; bool need_load=inherited_shared::start_load_shared(m_DialogId); //начинаем загрузку if (need_load) //свеже созданное inherited_shared::load_shared(m_DialogId, nullptr); else if (GetDialogForceReload()) //уже создавалось раньше { CUIGameSP* ui_sp = smart_cast<CUIGameSP*>(HUD().GetUI()->UIGame()); if (ui_sp && ui_sp->TalkMenu->GetInitState()) //читать только если идет инициализация окна диалога. в других Load - загружать наново не надо { ITEM_DATA item_data = *id_to_index::GetById(m_DialogId); //перечитаем xml часть диалога std::string file_name=item_data._xml->m_xml_file_name; const size_t sidx = file_name.rfind('\\'); if (std::string::npos != sidx) file_name=file_name.substr(sidx+1,file_name.length()); item_data._xml->ClearInternal(); //почистим внутреннюю структуру xml парсера item_data._xml->Init(CONFIG_PATH, GAME_PATH, file_name.c_str());//заново запустим парсинг xml с диска data()->SetLoad(false); inherited_shared::load_shared(m_DialogId, nullptr); //перестроим граф диалогов на основании новой структуры xml } } } это позволит при установленном вышеописанном параметре для диалога заново перечитывать и распарсивать эго xml. Также , для меня чрезвычайно полезной оказалась немного переделанная функция дампа структуры xml, взятая с официальной документации TinyXML. Добавляем в в начало: #include <locale> #include <iostream> #include <sstream> и куда-нибудь, можно в конец: #pragma region dump xml from tinyxml docs const unsigned int NUM_INDENTS_PER_SPACE=2; const char * getIndent( unsigned int numIndents ) { static const char * pINDENT=" + "; static const unsigned int LENGTH=strlen( pINDENT ); unsigned int n=numIndents*NUM_INDENTS_PER_SPACE; if ( n > LENGTH ) n = LENGTH; return &pINDENT[ LENGTH-n ]; } // same as getIndent but no "+" at the end const char * getIndentAlt( unsigned int numIndents ) { static const char * pINDENT=" "; static const unsigned int LENGTH=strlen( pINDENT ); unsigned int n=numIndents*NUM_INDENTS_PER_SPACE; if ( n > LENGTH ) n = LENGTH; return &pINDENT[ LENGTH-n ]; } std::string dump_attributes(TiXmlElement* pElement, unsigned int indent) { if ( !pElement ) return ""; std::string result=""; TiXmlAttribute* pAttrib=pElement->FirstAttribute(); // const char* pIndent=getIndentAlt(indent); while (pAttrib) { std::stringstream ss; ss << pAttrib->Name() << ": value=["<<pAttrib->Value()<<"]"; pAttrib=pAttrib->Next(); result+=ss.str()+(result.length()>0? " " : ""); } return result; } void dumpTree(XML_NODE* startNode, unsigned int indent = 0) { TiXmlNode* pChild; TiXmlText* pText; int t = startNode->Type(); LPCSTR indentStr= getIndent(indent); switch ( t ) { case TiXmlNode::DOCUMENT: Msg( "Document" ); break; case TiXmlNode::ELEMENT: { std::string attrInfo=dump_attributes(startNode->ToElement(), indent+1); if (attrInfo.length()==0) Msg( "%sElement [%s]", indentStr,startNode->Value()); else Msg( "%sElement [%s] attributes:(%s)", indentStr,startNode->Value() ,attrInfo.c_str()); } break; case TiXmlNode::COMMENT: Msg( "%sComment: [%s]",indentStr, startNode->Value()); break; case TiXmlNode::UNKNOWN: Msg( "%sUnknown" ,indentStr); break; case TiXmlNode::TEXT: pText = startNode->ToText(); Msg( "%sText: [%s]", indentStr,pText->Value() ); break; case TiXmlNode::DECLARATION: Msg( "%sDeclaration",indentStr ); break; default: break; } //printf( "\n" ); for ( pChild = startNode->FirstChild(); pChild != nullptr; pChild = pChild->NextSibling()) dumpTree( pChild, indent+1 ); } #pragma endregion void CXml::dump() { Msg("--- start dump content for xml object based on %s ---",this->m_xml_file_name); dumpTree(GetRoot()); Msg("--- end dump content for xml object based on %s ---",this->m_xml_file_name); } В описание класса CXml добавляем в public: void dump(); Теперь, вызвав, например, в выше приведенном коде item_data._xml->dump(); в консоли мы увидим структуру распарсенного xml файла. Изменено 3 Февраля 2017 пользователем Kondr48 объединил посты 1 Поделиться этим сообщением Ссылка на сообщение