Pull to refresh

Comments 39

Спасибо за статью и за ваш труд, отличная работа.

Если позволите, вопрос по внутренности реплеев:

1. Правильно ли я понимаю, что в файле реплея содержатся ничто иное, как последовательность действий каждого из игроков, которые передаются интерпретатору (движку игры) как если бы игра происходила в real-time?

2. Итоговая статистика (сколько каждый из игроков добыл, произвёл, убил) «аккумулятивна»? Другими словами, вычисляется в процессе воспроизведения записи или же хранится в виде «summary» файле?

Всегда пожалуйста :)

1. Да, именно так. rec-файл в основном состоит из буфера команд (ExBuf), которые передавались по сети. Действия ИИ рассчитываются заново при просмотре, так что игра как будто играется заново.

В процессе игры постоянно задействуются псевдослучайные числа из файла random.lst, и в определённом такте текущее случайное число сохраняется в записи. Это один из способов, которым игра проверяет синхронизацию. Про механизм синхронизации Казаков вообще можно отдельную статью написать :)

2. Наверняка сказать не могу, но скорее всего вычисляется во время воспроизведения, т.к. в механизме экрана статистики я не заметил разделения на игру и запись. При этом вполне возможно, что значения ресурсов периодически сохраняются в файле записи для проверки синхронизации.

Добавлю: запись действий всех игроков в реплее — это очень популярное решение. Точно знаю что точно так же делает третий варик, наверняка так же делают оба старкрафта и вторая дота.

Совершенно верно: для RTS это чуть ли не единственный метод, если вы не хотите, чтобы реплеи весили десятки (а то и сотни) мегабайт.
Здравствуйте!
Ветка комментариев заставила меня задуматься на одну тему, я не знаю пробовали ли вы играть в довольно новую игру от Blizzard «Heroes of the Storm», но говорят, она сделано на основе движка SCII, т.е. RTS. Так вот в игре есть занимательная вещь, если во время игры игрок по каким-то причинам вылетел (ошибка, дисконнект), то при повторной загрузке в игру, игрок сидит и ждет пока всё, что происходило без его участия (или вообще все действия с начала игры) воспроизводятся перед ним в ускоренном виде и когда этот «реплей» воспроизведется до текущего момента, игрок может продолжить игру. Меня это решение всегда ставило в ступор, ведь столько онлайн игр возвращают игрока без всего этого сразу и в текущий момент времени, а тут такое.

Возможно ли такое именно из движка и такого вот подхода к воспроизведению реплеев в нем?
Хотя с другой стороны есть сам SCII, в котором при дисконнекте такого не происходит.
Здравствуйте,

с «Heroes of the Storm» я не знаком, но судя по вашему описанию всё обстоит именно так.

Альтернативным методом была бы передача полного состояния игры (расположение и даные всех юнитов, экономика и пр.) в момент реконнекта. Это заняло бы слишком много времени, другим игрокам пришлось бы ждать. К тому же в играх один на один вы бы полностью доверились другому клиенту при реконнекте, а так игра восстанавливается по записи с вашего клиента, затем догоняет пропущенные события (если я правильно понял).
Как наигравший в хотс и старкрафт думаю это просто фича для удобства пользователя, в хотсе реплаи вообще пишутся автоматически, и скорее всего на стороне сервера.

Еще нужно учесть что хотс не РТС, а сохранить и передать реплай нескольких минут действий 10 (герои) + 10-20-30 (не герои) юнитов не так затратно как старкрафтовский с 200-300 юнитов, или какойнить тоталворовский или казаковский с тысячами.
Как уберечь своё приложение от такой эвристики со стороны Майкрософт, существующей и грядущей? А никак. Сегодня вы разрабатываете игры, а завтра уже стоите в одном ряду с Inno Setup и InstallShield.

А включение манифеста с явным отключением требований прав админа не помогает?

Не проверял, но в данном контексте это не имеет значения.

Функция манифеста появилась лишь в Visual Studio 2005 / MSVC 8, а описанная эвристика — ещё позже. На момент написания Казаков нельзя было предвидеть и избежать такого поворота. Это я и хотел выразить в данном предложении.
Оказывается, ОС Windows начиная с Windows Vista применяет эвристический алгоритм для определения приложений, которым может потребоваться повышение привилегий. Называется эта функция «Технологией обнаружения установщика» (см. статью в ИТ-центре Windows), и обычно она реагирует на ключевые слова вроде install или setup. Но в нашем случае виновником оказался параметр CompanyName — если он содержит строку "-GSC-\0", то просыпается UAC и требует прав администратора.

Причина не в этом. В Windows встроена огромная база данных, которая описывает, какие фиксы совместимости к каким исполняемым файлам нужно применять. В этой базе и прописано, что эта игра должна запускаться с максимальными правами. Видимо, без них что-то не работало.

image

Вообще там очень много разнообразных фиксов, которые можно применять к приложениям. Многим старым программам прописано SingleProcAffinity, например, что заставляет выполняться их на одном ядре (иначе некоторые программы эпохи до появления многоядерных процессоров просто зависают). Или, например, есть фикс, который «врёт» программе о свободном пространстве на диске, гарантируя, что число не будет больше 2 гигабайт, что помогает некоторым старым программам работать без ошибок. Эту базу можно редактировать, добавлять или удалять свои фиксы каким-то программам. Для запуска некоторых старых игр часто предлагается инсталлировать в систему *.sdb патчи — это как раз оно, добавляет новые записи в эту базу.

И да, я не совсем понял, где собственно у вас был реверс-инжиниринг? У вас же были исходные коды.
Спасибо за информацию, не знал.

И да, я не совсем понял, где собственно у вас был реверс-инжиниринг? У вас же были исходные коды.
Реверс был в первых трёх статьях, до обнародования исходников. Назвал так, потому что эта статья логичное завершение начатого проекта. Ну и код местами довольно необычный, так что реверс тут был хотя бы мысленный ;)
Интересно. Спасибо за статью.
Меня вот, что интересует. Допустим я играл на старом «железе»(Pentium 3 800 mHz) были те же «тормоза», что и сейчас, на более быстром «железе». К примеру, когда большое количество юнитов на карте, игра начинает «тормозить» или же вылетать?!
Понимаю, может быть вопрос не по адресу, просто интересно отчего же так происходит, нет ли там кода, который искусственно «затормаживает» игру ?!..
Вылет при большом количестве юнитов на экране я пофиксил. На счёт тормозов точно сказать не могу, мне не довелось протестировать сетевую игру с 8.000+ юнитами. При ~5.000 юнитах тормозов не заметил.

Возможны артефакты при большом количестве трупов на экране — временно пропадают деревья, анимации выстрелов и огня. Раньше это так же приводило к вылету, теперь нет ;)

Никто не пытался воссоздать казаков на современных технологиях? (Новые казаки не в счёт, они как-то очень криво сделаны)


Я пару лет назад ради интереса написал примитивный прототип графики на libgdx. Самой игры не было, просто рисование расставленных человечков, деревьев и рельефа с помощью OpenGL ES 2.0 — телефон нормально справлялся, а на компьютере так вообще летало.


Если упростить управление — сделать поведение солдат и крестьян более разумными, чтобы игрок не страдал от необходимости микроконтроля на маленьком сенсорном экране и сконцентрировался на управлении отрядами, то может получиться довольно интересно. Но в одиночку было сложно, и я отложил проект до лучших времён, которые, похоже, так и не наступят.


Скриншоты с телефона в 1920*1080

На человечка потратил несколько дней — моделировал в блендере, разукрашивал, потом рендерил с 16 сторон в маленькие картиночки вместе с картой нормалей… Деревья и траву взял готовые.



Ух ты, респект!

На счёт проекта — извините, но я пас. Я с Казаками в последнее время уже натерпелся ;)

Но тема интересная, желаю удачи, если всё же возьмётесь.
Посмотрите проект 0AD. Замечательный проект.
Года идут, а до сих пор альфа.
Классно выглядит!..
«Warcraft 2000» — родился через выдирание арта из оригинального «Warcraft». Но на своём движке. Который и стал основой для «Козаков». История повторяется? Прям мурашки по спине. Карма наверное.
Не уверен, что
На человечка потратил несколько дней — моделировал в блендере
попадает под «выдирание арта». К тому же, Warcraft 2000, если я не ошибаюсь, никогда не являлся коммерческим проектом.

…вы мне, кстати, напомнили историю про то, как ребята из GSC когда-то предложили представителям Blizzard помочь последним с разработкой Warcraft 3, а то движок у Warcraft 2 тянул уж слишком мало юнитов на карте. И в качестве PoC показали им упомянутый Warcraft 2000. В итоге ребята из Blizzard обиделись и не стали дружить с GSC ^_^
GSC тогда арендовал трехкомнатную квартирку в жилом 16 этажном доме напротив метро Оболонь, и свёртывал все проекты, кроме арта к «Казакам» и «Warcraft 2000». Жестоко кончались деньги. А Андрей тогда был умельцем в ассемблере, а не в С++. И это не C++ был, а VC6. И нужен он был, поначалу, только для поднятия DirectDraw. Плюсами VC6 от Microsoft и не был. И смещения в структурах фиксировались для инлайн ASM. И если ASM остался в сырцах, убедитесь что это POD, не стоит из «это» класс делать, ибо С++11.
«Warcraft 2000» — родился через выдирание арта из оригинального «Warcraft».

Не, от Казаков я только деревья брал)


Для хранения анимаций человечков в Казаках используется какой-то хитро сжатый формат и ограниченная палитра на 256 цветов — я не стал с этим связываться.


Вдобавок, я вместе с человечком сделал карту нормалей. Человечек плоский и рисуется как два треугольника, но благодаря нормалям со стороны света он поярче, а с другой — в тени. (не путать с направленными тенями от человечков на земле — те рисуются честно). Я ещё хотел с деревьями так же сделать, но руки не дошли (как и до анимации человечка и вообще всего остального). По моим прикидкам, если делать спрайты 32*32 или 32*64 пикселей размером, в текстуру 2к*2к поместится 4 или 2 тысячи спрайтов. Если рендерить каждую модель с 16 сторон, останется 512 или 256 кадров. Этого хватит на то, чтобы сделать несколько типов человечков, но не хватит, чтобы сделать всё разнообразие юнитов, как в оригинальной игре. (Юнитов разных цветов можно получить изменением цветов в пиксельном шейдере.)


В итоге получилось нечто, достаточно быстрое даже на мобильных телефонах и при этом с красивостями типа теней.


P.S. К сожалению, история вряд ли повторится, больно уж много усилий придётся затратить. Единственное что радует — современные технологии и более мощное железо позволяют экономить время и делать некоторые вещи путь и не очень оптимально, но зато более простым способом.

Всё правильно вы делаете, это не только проще, но и в сотни раз быстрее. Текстура со всей анимацией движения каждого юнита ложится на сторону видео, не нужно на прямую с пикселями маяться — пусть с ними GPU мается. Смещение UV в шейдер — на текуший кадр анимации. Ну и приближать-удалять сцену — проще. Рендерить достаточно с 8-ми сторон — остальные 8 это переворачивание UV. Для мелких юнитов и 3-х сторон вполне достаточно. (Визуально 6 направлений). GLES2.0 + QT + C++ ??
Рендерить достаточно с 8-ми сторон — остальные 8 это переворачивание UV.

Мне кажется, экономия в 2 раза — не очень много, а вот превращать левшей в правшей как-то неправильно. Раз уж я делаю 3д модельку, нет никаких сложностей отрендерить её со всех сторон. В любом случае, добавить эту фишку никогда не поздно.


GLES2.0 + QT + C++

Это долгая история :) 3 года назад я осваивал openGL, вызывал его из кода на java (в андроиде так можно), и потихоньку пилил свой движок и игру. оно даже есть на гитхабе, хотя большой практической ценности в проекте нет. Писал свои велосипеды для матриц, кватернионов, обёртку над шейдерами...


Потом мне надоело писать велосипеды, и я решил использовать libgdx — библиотека даёт удобные обёртки над openGL, вводом-выводом и прочим. Один и тот же код без проблем собирается и под PC и под android. Это очень ускоряет написание кода и отладку (да и на PC почему бы не поиграть). Скриншоты выше из версии с libgdx.


Если я возьмусь за это сейчас, буду использовать libgdx + scala. С++ знаю не очень хорошо, а scala позволяет быстро писать лаконичный и гибкий код. Ещё как вариант рассматривал kotlin, но scala больше нравится.

В девяностых игры писались на С и на С++ со вставками ASM. Причем на ASM реализовалось большая часть кода. С приходом крайне медленных Android устройств, и слабых GPU типа Mali 100 тщательный подход снова стал актуальным. Но использовать ASM нельзя из соображений кроссплатформенности. Для меня решением дилеммы стал godbolt.org, пишу на С++, объектно, с паттернами, но каждую конструкцию «вымучиваю» в godbolt, пока нужные мне GCC -O3 -fno-rtti не выдадут правильный, с моей точки зрения, ASM. Мало чем отличается от нашей писанины 20 летней давности. Тогда мы просто смотрели на ASM порождаемый компилятором С++, долго ржали над ним, копировали в свой С++ сырец, как ASM вставку, и уже там облагораживали.
Круто!

А почему на Github не выкладываете готовый исполняемый файл? Я б попробовал поиграть, но ставить ради этого MS VS и ковыряться с компиляцией очень не хочется.
Во-первых — гитхаб не для вареза :)

Во-вторых — исполняемый файл вам ничего не даст. Я немного изменил систему загрузки ресурсов, т.к. с существующеми архивами могут возникнуть проблемы (см. пункт «тонкости языка»).

В конце статьи есть несколько ссылок с дополнительной информацией, которые, возможно, вам помогут.
О да, код Шпагина это боль для глаз. :) Когда я писал АИ для Казаков 3 — пришлось нормально выучить С++ %)
Было бы интересно почитать о вашем опыте в разработке ИИ ;)
Только давайте конкретные вопросы. :)
С удовольствием :)

  1. Располагает ли ИИ в любой момент всей информацией о текущей игре или же играет «по честному», видя только в пределах тумана войны?
  2. Принимает ли ИИ решения лишь на основе доступной игрокам информации (т.е. практически «визуально» — по расположению юнитов), или же он моментально в курсе того, что игрок, например, отдал своим юнитам приказ атаковать конкретное здание?
  3. Выше вы писали, что для Казаков 3 вам понадобился C++. Разве там движок не на Delphi написан? Каким образом происходит взаимодействие с вашим ИИ?
1. Читит. Но и ставка была не на АИ — он должен развлекать игрока, а на сетевую часть.

2. Это и не требуется. АИ регирует по зонам опасности — к примеру, деверсанты ищут путь с минимальным весом опасности, а армии наоборот ищут «опасность»

3. С++ пришлось изучить что бы адекватно читать код оригинальных козаков (я не совсем программист, а игровой дизайнер), так как в момент старта мы хотели что бы Казаки3 игрались точно так же как и оригинал. Сейчас правда баланс сильно поменялся.

С технической стороны движок располагает набором внутренних функций, базовых, очень похоже на то как работает «Монобехевиор» в Юнити, только наши скрипты, а это 99,9% всей игровой логики написаны в скриптах и компилируются в память в рантайме, а не транслируются по надобности. Дальше просто идёт обычный asm: jmp на функцию движка с передайчей хендла объекта.
Спасибо!

А вы знаете, по каким причинам было решено писать свой интерпретатор для скриптов и делать движок на Delphi, а не использовать довольно распространённую в геймдеве связку из C++ и Lua? С производительностью у последнего, вроде бы, всё в порядке.
Движок уже был готов — он отлично справлялся с задачами.

LUA? Сборщик мусора — уже приговор для сильно нагруженных игр. Достаточно глянуть как «гладко и быстро» (адово тормозя и пожирая память на самом деле) работает последние версии ТоталВара, стабильно отваливаясь в десктоп после часа-другого игры.
Интересно, мне казалось, что движок специально для третьих Казаков и писали.

Сборщик мусора — уже приговор для сильно нагруженных игр-
Вовсе необязательно. Скриптовый API ведь нужен в первую очередь для описания «правил» игры, а не для хранения данных, или? К тому же, тот же Lua, например, позволяет откалибровать свой сборщик через lua_gc().
1) Дорабатывали third-party если быть точным

2) У нас на «скриптах» живёт вся логика и почти все структуры данных, разве что поиск пути из логики реализован «гвоздями», сильно уж затратная по ЦПУ задача

Я понимаю что любителям быстрой разработки «кросс-платформы» используя Бустстрап и НодеДЖ слова о гарбедж коллекторе звучат как ересь, но просто ради теста в том же юнити сделайте несколько тысяч боксов которые будут искать «врага» каждый кадр и стрелять в его сторону раз в секунду партиклом создавая новый монобехевиор — результат вас удивит. (тормозит чего-то в странных местах, ага)

Ключевые слова тут — несколько тысяч и каждую секунду. Это довольно плохая идея даже для языков, которые никакого сборщика мусора не имеют.

Так работают оригинальные казаки. Юнит делает поиск врага каждый кадр если готов к атаке.
К сожалению, RTS сейчас переживают не лучший период.
Sign up to leave a comment.

Articles