Перейти к контенту

Malandrinus

Жители
  • Число публикаций

    1 930
  • Регистрация

  • Последнее посещение

  • Дней в топе

    13
  • AMKoin

    160 [Подарить AMKoin]

Весь контент пользователя Malandrinus

  1. Malandrinus

    [SoC] Вопросы по SDK

    Writer, скриптом можно составить полный список вертексов. Как-то так: local gg = game_graph() local i = 0 while gg:valid_vertex_id(i) do local lvl = gg:vertex(i):level_id() -- здесь имеем номер вертекса i и номер его уровня lvl i = i + 1 end Ну а дальше дело техники. Здесь делается предположение, что валидные вертексы идут непрерывным списком и заканчиваются первым невалидным вертексом. Так ли всегда - точно не знаю, но скорее всего так. Как альтернативный вариант, можно разобрать game.graph и составить список вертексов заранее. Вроде как для этого даже есть готовый утиль.
  2. Другое название этой сетки - сетка навигации. Ещё название - AI-сетка, что по сути одно и тоже, поскольку весь AI так или иначе связан с перемещениями. Проще всего понять, на что похожа сетка уровня (граф навигации уровня), если эту сетку как-то визуализировать. Есть для этого тулза, созданная IG-2007. Но я попробую словами... В общем главное, что надо понять - это что неписи в онлайне перемещаются на самом деле не по тем поверхностям, что вы видите глазами, а по узлам сетки уровня. И перемещаться могут только там, где эта сетка есть. Т.е. фактически, сетка уровня - это не столько сетка, сколько попросту альтернативная невидимая поверхность, совмещённая с видимой. Наличие такой сетки радикально упрощает навигацию, делая её вообще говоря реализуемой. Узлы этой сетки - это такие маленькие прямоугольные площадки, размером как правило 0.7 игровых метра. Если смотреть в плане, то все эти площадки строго упорядочены, как клетки шахматной доски. По высоте (т.е. по оси Y) они отличаются, чтобы воспроизводить ландшафт. В том числе в одном месте по горизонтали (т.е. в плоскости XZ) могут находиться две и более таких площадок, но с разной высотой. Ну понятно в общем - это для этажей зданий и прочего в этом роде. В некоторых местах сетки нет. Во-первых, вся сетка вообще ограничена определённым параллелепипедом в пространстве. Это связано со способом её хранения, вне этого пространства сетки попросту не может быть. Некоторых узлов нет, и сетка образует дырки, соответствующие стенам, кустам, препятствиям вообще. Эти дырки в частности используются движком игры, чтобы искать для неписей укрытия. Видели функцию level.cover_in_direction? Нетрудно представить логику её работы: найти вертекс, закрытый дыркой в определённом направлении. Если в дырке на самом деле стоит куст или дерево, то это как бы укрытие. Впрочем, до конца этот вопрос я не исследовал и думаю, что на самом деле "дырки укрытия" как-то специально промаркированы. Узлов уровня много, можете прикинуть сами, но и так ясно, что на одном уровне таких вертексов может быть порядка миллиона. Глобальный или оффлайновый граф, как ясно из его названия, нужен для навигации в оффлайне. Он уже больше похож на граф. На уровень таких узлов может быть что-то около пары сотен. Навигация в оффлайне в том числе допускает и перемещение между уровнями. Непись, которому сказали идти на узел глобального графа, находящийся на другом уровне, сначала строит свой путь по локальной сетке уровня до подходящего геймвертекса, дойдя до него - переходит в оффлайн и дальше уже перемещается по глобальному графу на нужный уровень. Насчёт необходимости указывать эти значения при спавне. В принципе, геймвертекс - единственная привязка к уровню, поскольку координаты указываются локальные. Без геймвертекса будет неясно, на каком уровне создан объект. Левел вертекс однозначно нужен для неписей, иначе может возникнуть ситуация, что непись стоит невесть где и не знает, как дойти, куда ему там надо. Хотя в ряде случаев я согласен, указание левелвертекса выглядит избыточным. Например, для физических объектов, которые по сетке не ходят, а определяются физикой. Но зачем-то это движку надо. Я подозреваю, что в ряде случаев допустимы отклонения от точности указания этих параметров.
  3. Да есть он там! Кто сказал что нет? Даже в lua_help остался. Можно создать машину, хотя и не получается на ней поездить.
  4. Monnoroch, ну хорошо, не в большинстве случаев, а иногда (предыдущий пост поправил). Вроде бы nil в с точки зрения Lua должен быть не значением, а его отсутствием. Поэтому вызов функции fun(nil) вроде как эквивалентен вызову fun(), но так ли происходит всегда, я не знаю. Но такие случаи я точно встречал. К примеру вызов level.object_by_id(nil), как и без аргумента возвращает объект с нулевым идентификатором, т.е. актора. А вызов alife():object(nil) приводит к вылету. А вызов db.actor:object(nil), который вроде как должен вернуть объект по индексу, возвращает вместо этого число 0. В итоге, ситуация еще хуже, поскольку реакция функций игры на вызов без аргумента (или с nil) попросту непредсказуемая. Так что мой предыдущий спич полностью остаётся в силе =)
  5. Garry_Galler, избыточный код. Зачем так сложно? У тебя по коду на входе функции и так подразумевается серверный объект. Зачем по нему получать серверный ещё раз? Ничего другого не получишь, уж точно =) Этого достаточно: function get_parent_id(obj) if obj then return sobj.parent_id end end И потом, с точки зрения безопасности и надёжности проверка if obj then ... на самом деле ничего не даёт. Ну проверил, что объекта на самом деле нет, а дальше что? Функция в этом случае возвращает nil. Хуже всего, что этот nil в последствие приводит к непредсказуемым результатам (и не обязательно сразу к вылету). И в итоге ты получаешь отложенную ошибку, которая естественно приведёт к сбою, но совсем не там, где надо, не там где можно понять её источник. На самом деле эту проверку надо делать до вызова этой функции. Именно то место, где вызывается подобная функция - это место, где можно принять решение, что делать с этой ошибкой. Так что без всяких функций: if obj then --делать что-то с obj.parent_id else --обрабатывать ошибку end В современных языках программирования эти вопросы решаются с использованием механизма исключений: ошибка выявляется в одном месте, а механизм исключений переправляет её обработку в вышестоящий уровень, где имеются полномочия для принятия решения. В Lua исключений в явном виде нет (хотя некое подобие в виде pcall есть), так что придётся по старинке, самому выявлять нужное место обработки ошибки.
  6. dimos, Это «Отбойник» - автоматический дробовик из ЗП. Секция wpn_protecta в файле configs\weapons\w_protecta.ltx. Я думаю, что не получится сделать оружие, стреляющее двумя типами патронов. При выстреле из подствольника спавнится граната, обрабатывается её физика и т.д. Это не превратить в выстрел из дробовика. Ну по крайней мере я не знаю, как это сделать. W_GLAUNC - это вообще никак не поможет, поскольку это объект подствольника, а все аддоны - это просто инвентарные предметы. Они не стреляют, стреляют стволы. При надевании аддона сам объект аддона исчезает, а у ствола просто устанавливается соответствующий флажок.
  7. Это вопрос к моему ответу про ящики? Если так, то не знаю, что добавить. Мой ответ и так конкретней некуда. Или о чём другом речь? Чтобы определить, что предмет в ящике? А чем по parent_id не устраивает? Вообще, нетпакеты - это последний аргумент. По возможности лучше избегать. Какое самодостаточное наблюдение =)
  8. Real Wolf, parent_id предмета совпадает с id ящика.
  9. Разбираем систему клиентских классов. Сначала немного лирики. Довольно лирики. Далее представлена система клиентских классов. Используются те-же соглашения, что и раньше: конструктор не показан, если класс не добавляет своих методов, то показан одной строкой. Иерархия клиентских классов Сперва базовые интерфейсы. Их не так много, как у серверных. Все остальные классы имеют своим предком CGameObject Классы, производные от CGameObject Рисовать картинку с иерархией наследования смысла нет, поскольку и так всё довольно очевидно. За исключением малого числа классов остальные к CGameObject ничего не добавляют. Соответственно и описывать нечего. Названия классов более или менее говорят о том, для каких объектов они предназначены. Где мог, указал точные сигнатуры функций (CCar, CHelicopter и hanging_lamp). Для новых классов и методов по сравнению с ТЧ такой информации к сожалению нет.Несколько рассуждений на тему взаимодействия серверного и клиентского объектов. Попробуем рассуждать логически. Есть сервер, есть клиент. Это в общем случае разные компьютеры. Каким может быть взаимодействие объектов, находящихся по разную сторону сети? Могут ли они, к примеру, вызывать методы друг друга, или непосредственно читать данные своего "напарника"? Очевидно нет. Моё мнение такое, что их взаимодействие сводится в основном к посылке данных друг другу. При этом, данные посылаются "пачкой", т.е. обо всём состоянии сразу. То, что я наблюдал, выглядит так: при переходе в онлайн создаётся клиентский объект, серверный его инициализирует, посылая ему при создании нетпакет. Этот момент можно отловить в биндере клиентского класса. Все дальнейшие движения происходят от клиентского объекта к серверному. Это означает, что периодически клиентский затирает данные серверного. Точные моменты, когда это происходит, периодичность, а также механизм передачи данных мне лично понять пока не удалось. По крайней мере на серверной стороне ничего не видно, просто тихо меняются данные, и понять, когда это происходит, крайне непросто.Однако некоторые выводы таки можно сделать. Если взаимодействие классов примерно такое, как я здесь предположил, то совместимость классов определяется совместимостью бинарных данных, которые они друг другу посылают. Ну в общем классы должны друг другу соответствовать, хотя, как далее будет видно, это правило на практике (очень редко) нарушается. Если не забыли ещё, то зарегистрированные пары классов задаются идентификаторами класса. В файлах конфигурации (в секциях объектов) этим идентификаторам соответствует параметр class. В скриптах - это один из членов clsid. В движке уже зарегистрированы соответствующие пары классов, и вы можете добавлять свои пары с помощью класса object_factory. Я это всё описывал здесь.Далее я привожу таблицу соответствия классов. Для полноты картины в ней указаны классы, которые не экспортированы в Lua. Если класс не экспортирован, то это означает, что вам не удастся зарегистрировать на его основе свою пару классов. Однако это не мешает (теоретически) создавать объекты этого класса при условии, что имеется и известен его идентификатор.Приведённая таблица собрана из разных источников, в том числе основана на примерах регистрации классов из игры. Видно, что иногда клиентский класс использует в качестве "напарника" серверный, который является базовым для своего "родного" серверного. Видимо, это технически в норме вещей и так можно поступать и нам. Из этого ряда выбивается CZombie, для которого вроде бы родным должен быть серверный класс cse_alife_monster_zombie, а регистрируют его с cse_alife_monster_base. Это вроде как нарушает вышеописанную логику, поскольку cse_alife_monster_base не является базовым для cse_alife_monster_zombie. С другой стороны, он так зарегистрирован в самом движке. Возможно cse_alife_monster_zombie попросту не задействован и никакого отношения к CZombie на самом деле не имеет. Таблица соответствия серверных и клиентских классов
  10. 7.9, всё верно, только не 40 млн., а 4 мрд., точнее максимальное 32-х разрядное беззнаковое целое: 4294967295 что в миллисекундах составляет примерно 1193 часа, т.е. 49 дней. Это известная проблема: после примерно месяца игрового времени часть функций, связанных с использованием этого счётчика, начинают работать неправильно. Вроде как это даже лечили. Хотя если этот счётчик используется не только скриптами, но и движком, то всё не вылечить.
  11. Полагаю, красивая картинка никому не помешает. Вот вам диаграмма серверных классов с высоты птичьего полёта =) Обращаю внимание на нотацию записи: большая пустая стрелка означает отношение обобщения (или наследования иными словами) и направлена от унаследованного класса к базовому. Включение абстрактного интерфейса обозначается стрелкой с кружочком и указанием, какой интерфейс наследуется. Это сокращённая форма записи, позволяющая избавиться от нагромождения линий и лучше понять основную линию наследования.
  12. Введение Итак. Все игровые сущности (сталкеры, монстры, предметы и пр.) в общем случае существуют одновременно на двух сторонах - на сервере и на клиенте. На каждой стороне для этого есть соответствующие классы. На серверной - это классы, имя которых начинается на "cse". С некоторыми поправками можно считать, что все серверные классы происходят от cse_abstract. На клиентской стороне есть соответствующая иерархия классов. Можно считать, что все они происходят от CGameObject. На клиентской стороне есть ещё и game_object, который также является интерфейсом к клиентским объектам, причём основным интерфейсом. Его описание уже начато, но оно далеко от завершения. Классы как серверные, так и клиентские образуют иерархию наследования классов. На самом примитивном уровне можно считать, что наследование означает включение всех членов базового класса (т.е. свойств и методов) в производный класс. В свою очередь это означает, что нет необходимости описывать унаследованные члены для всех производных классов, достаточно это сделать один раз для базового. Кроме того я ввожу важное соглашение для описания классов: я не буду приводить полный список членов класса для каждого из них, а только список методов и свойств, которые добавляет конкретный класс сверх унаследованных. Поэтому, если по описанию в классе нет ничего, то смотрите на классы от которых он унаследован и смотрите список их методов и список методов их базовых классов и т.д. Я вынужден так сделать во-первых в силу ограничения форума, во вторых для убирания избыточности при описании. Я также буду пользоваться спойлерами: после описания класса будет идти спойлер с его производными классами, в них спойлеры с их производными и т.д. (наподобие матрёшки =) Начнём с серверных. Можно обратить внимание, что имеется некоторое количество базовых классов и интерфейсов, которые вообще не содержат никаких методов или свойств. Наследование от них не даёт конкретной информации о свойствах серверного класса, но парадоксальным образом может дать некоторые догадки о применимости методов game_object. Вот список этих базовых интерфейсов: Базовые интерфейсы Несколько предваряющих комментариев к дальнейшему материалу. Вся иерархия серверных классов
  13. Garry_Galler, зря ты длинные пути используешь для установки. Чревато необъяснимыми глюками. Не факт, что в этой ситуации как-то сказалось, но вообще лучше ставить в папку попроще. Диск значения не имеет, а путь лучше дать без выкрутасов типа такого "d:\games\shoc\"
  14. Monnoroch, у тебя запредельно медленное преобразование строк в числа. Учитывая, что это даже не строки, а одиночные символы (если я верно понял), то сконвертировать их в соответствующие числа можно на порядок быстрее, используя коды символов. Функции для этого есть.
  15. Второй способ с кучей if-ов порочный в принципе. И скорее всего будет намного медленней.
  16. Monnoroch, сумматор там стоит. Это такое электронное устройство. На входы подаёшь два двоичных числа, на выходных контактах через некоторое время получаешь результат. Посмотри здесь, если сильно интересно, как он устроен.
  17. Monnoroch, интерпретатор (здесь не компилятор, а именно интерпретатор) переводит высокоуровневую операцию в последовательность машинных кодов. В данном случае это заключается в вызове некой внутренней функции, которая собственно и выполнит сложение. Сама функция представляет собой последовательность инструкций процессора, которая делает примерно следующее: - Читает из стека аргументы в машинные регистры - выполняет собственно сложение. Обычно для этого есть инструкция типа "сложить значение из регистра А со значением из регистра Б и поместить обратно в А" - возвращает результат тем или иным образом в зависимости от соглашения о вызовах. Например через определённый регистр. Ну и всё. Эти инструкции выполняются уже непосредственно процессором. Ниже уровня для программиста нету =)
  18. Это зависит от типа переменной. Если встроенный арифметический тип, то тупо считает, вроде как приводя к более сложному типу из двух. Точное поведение зависит от реализации. Если это пользовательский тип, то при наличии метатаблицы с переопределённой арифметической операцией переправит действие этой операции (для vector или CTime к примеру). При отсутствии - будет ошибка "no such operator defined" Это всё в принципе заморочки Lua. В его документации всё и описано.
  19. Примерно столько же сколько и упакованные, поскольку как таковой упаковки там нет. Поставь плагин для Total Commander (ссылка в моей подписи) и узнаешь точно без необходимости распаковывать.
  20. Нельзя. Это часть API движка. Да не то чтобы альтернатив нет. Просто такой подход начинают использовать сразу, как только учатся писать простейшие циклы. Это всего-лишь самый простой подход. Но далеко не самый лучший. Почему не получится? В биндере каждого предмета по переходу в онлайн/оффлайн аккуратно регистрировать предмет в базе данных. Сразу сортировать объекты на неписей, монстров, предметы и т.д. Вообще-то что-то такое там и так сделано, ещё в оригинальной игре (таблица db), может стоит разобраться... В среднем это уменьшает время поиска всего-лишь в два раза. Хрен редьки не слаще =) Ты прочитал мой ответ в теме "справочник"? Тебе для каких целей надо определять состояние бега? Что можно, что нельзя сильно зависит от целей применения.
  21. Monnoroch, Да ладно, здесь ведь не просто 65 тыс. пересчитать. Здесь же надо с каждым что-то _сделать_. А это вам не плюнуть и растереть. Интерпретация кода скриптовым парсером, да вызовы движка, где десяток символов скрипта - это кто-там-знает-сколько-на-самом-деле операций. Так что линейный перебор - зло! Для экспериментов годится. Для реальных модов - фтопку.
  22. Garry_Galler, в ЗП появилась функция перебора предметов в ящике Gonarh, даже полсекунды как правило неприемлимо, поскольку чаще всего этот перебор надо делать регулярно. Представь себе регулярные полсекундные фризы! Более продуктивный, но более сложный подход: регистрация каждого объекта в общей базе данных при создании/переходе в онлайн.
  23. lekzd, можно попытаться по разнице между position и center. Это типа по наклону туловища =) Но там тоже нестабильные значения. Вообще-то по скорости не так уж и плохо работает. Если учесть, что апдейты идут нерегулярно и вычислять скорость по настоящему, т.е. учитывая параметр delta, то точность повышается. Также вроде как лучше вычислять скорость только с учётом координат xz, т.е. игнорировать вертикальную компоненту. Кроме того, можно ввести фильтрацию шума - усреднение по нескольким точкам. У меня усреднение по трём дало достаточно различимую картину: стоит, идёт, бежит. Но иногда таки бывают выбросы. По пяти точкам - различие практически идеальное. Хотя и появляется некоторая микроскопическая задержка, связанная с размазыванием переходов. Вопрос ещё, для чего это надо. Если скажем надо просто отобразить состояние бега на худе, то и так вполне сойдёт.
  24. По коду ошибки не видно. Может быть в таблице что не так, а её ты не показал. Зашли в личку весь набор.
  25. Darien, покажи код. Так не понять в чём дело.
×
×
  • Создать...