Анатомия игры - Моды и моддинг - Strategium.ru Перейти к содержимому

Анатомия игры

Рекомендованные сообщения

spacer.png

 

Анатомия Игры (перевод статей программистов CKIII)

 

От автора (balckninja9939 (Мэтью)

 

      Привет всем! Меня зовут Мэтью, и я программист CK3, добро пожаловать на первый пост из серии направленных на раскрытие технической стороны игры, под общим названием "Анатомия игры".

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

      Что происходит, когда вы запускаете игру? Как мы можем убедиться, что дополнения и моды загружены? Что на самом деле происходит за один игровой день? Что заставляет ИИ принять решение для безжалостного нападения на Вас?

      Я надеюсь ответить или, по крайней мере, пролить немного света на некоторые из этих и других вопросов в ходе этой серии.

     Эти посты не являются заменой нашим более традиционным Дневникам Разработчиков, и если вы ищете информацию или тизеры, связанные с предстоящим контентом, то эти статьи не для Вас. Но если Вас интересует основа того, что заставляет игры работать, или вы начинающий разработчик игр, то это должно Вас заинтересовать!

 

От себя.

 

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

 

Содержание

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Изменено пользователем Fester
Добавление разделов
  • Like (+1) 3
Ссылка на комментарий

Закреплённые сообщения

Часть I. Запуск и загрузка.

 

1.1 Настройка игры.

 

     В сегодняшней статье я собираюсь начать с … с самого начала! Когда Вы запускаете игру и загружаетесь, чтобы выбрать своего персонажа, что мы на самом деле делаем?
     Это может показаться простым вопросом, поскольку очевидно, что мы загружаем все, правильно? Но как мы можем сделать это так, чтобы быстро обмениваться информацией, причём как можно быстрее? Какие вещи мы на самом деле должны загрузить сейчас, а с чем разберёмся позже? Как моды и DLC играют роль в перезаписи элементов?

На высоком уровне ход запуска игры состоит из следующих шагов:

  1. Запуск базовых функций в Вашем движке в следующем порядке: файловая система, ведение журнала операций, система задач потоковой передачи
  2. Загрузка библиотеки под названием SDL, которая является основной библиотекой для доступа к оборудованию в играх
  3. Загрузка большого количества различных операций, процедур и других вещей, чтобы движок выполнял свою работу, многие из которых являются фоновыми задачами в потоках
  4. Создание окна для игры
  5. Загрузка ещё большего количества различного материала для самой игры
  6. Игра теперь загружена, и вы можете начать играть

     Так почему же шаг 1 выполняет столько подэтапов в строго определённом порядке? Это определённая последовательность, которая имеет особый смысл, если вы более внимательно на неё посмотрите. Многие системы ведут журнал операций, но для этого, конечно же, нужны файлы, поэтому сначала мы загружаем файловую систему. Кроме того, мы хотим загрузить много информации в фоновый поток данных, но работа этой системы будет записываться в журнал, поэтому нам нужно сделать это после запуска системы ведения журнала и файловой системы.

     Конечно, это очень высокоуровневый набор операций, который не учитывает множество деталей. Я буду опускать множество деталей в статье, но не стесняйтесь задавать любые вопросы. Основная масса интересных вещей происходит на этапах 3 и 5, они составляют большую часть нашей загрузки при запуске и на их оптимизацию уходит большая часть времени.

     Зачем оптимизировать время загрузки, спросите Вы? Конечно, для вас, как для конечного пользователя, это важно, так как вместо наших великолепных экранов загрузки и основных тем, вы, вероятно, предпочли бы играть в игру.
     Что касается нас, то мы часто запускаем игру в любое время в течение дня. И каждая секунда, потраченная на ожидание загрузки игры, - это просто потраченное впустую время и деньги. Когда мы запускаем игру, мы также делаем это без компилятора, который произвел бы более глубокую оптимизацию нашего кода, поэтому наш код работает еще медленнее, чем Ваш.

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

     Как вообще можно оптимизировать многое из этого? Как правило, происходит деление между более эффективным использованием процессорного времени и уменьшением времени ожидания.

     Для оптимизации времени работы процессора существует пара методов: чем более многопоточная работа, тем больше ресурсов процессора используется; оптимизируйте используемые Вами алгоритмы низкого уровня; и, наконец, вы можете подойти очень близко к архитектуре оборудования, чтобы оптимизировать ошибочно предсказанные ветвления процессором, а также потери кэш-памяти.

     Что касается ожидания, то основной задачей является сделать его меньше. Ожидание - это, когда вашему процессору приходится ждать, чтобы получить доступ к ресурсу, ждать, пока какая-то потоковая работа будет выполнена в установленный срок, и даже такие вещи, как чтение данных с диска, поскольку это происходит, когда ваш код ничего не делает.

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

 

  • Like (+1) 3
Ссылка на комментарий

1.2 На что посмотреть.


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

 

Спойлер

spacer.png


      Этот замечательный логотип прикрывает нас, пытающихся загрузить всё, что мы можем, и заставить Вас не думать, что всё сломалось и выводить Вас из себя. После того, как мы её показали, мы начинаем предварительно загружать некоторые необходимые игровые компоненты, такие как текстуры мыши и абсолютный минимум файлов пользовательского интерфейса, например, такие которые дают возможность нашему тексту правильно отображаться на загрузочном экране.
      Еще одна вещь, которую мы стараемся загрузить как можно скорее - база данных музыкальных композиций, чтобы могли воспроизвести основную музыкальную тему, потому что просто смотреть на экран загрузки в тишине не так весело, чем слушать замечательную музыку нашего композитора, которая поможет Вам дождаться завершения загрузки. Хотя, какая ирония, одна из наших основных целей - заставить Вас как можно меньше слушать эту заглавную музыкальную тему. Прости Андреас…

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

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

      Чтобы привести современный пример, вспомним поездки в лифте в Mass Effect 1, где у Вас были великолепные шутливые диалоги компаньонов в таких местах, как Цитадель, это были просто загрузочные экраны, скрытые повествовательным изложением, но даже при запуске игры на современном оборудовании Вы бы не смогли пропустить эти заставки.
      В недавно выпущенном ремастере Mass Effect Legendary Edition была добавлена кнопка пропуска таких сцен, так как теперь современное устройство может быстро загрузить всё. Но вы, конечно, еще можете послушать замечательные шутки между вашими товарищами по команде.

    До сих пор мы загружали много материала многопоточно, но с этого места всё, что мы загружаем, происходит в самой игре. И теперь мы загружаем намного больше всего, такие вещи, как оставшиеся файлы для пользовательского интерфейса, данные для наших 3D-объектов, данные карты, файлы игровой логики, а затем внешний и пользовательский интерфейсы.

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

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

 

Спойлер

spacer.png

 

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

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

  • Like (+1) 2
Ссылка на комментарий

Aurelius36
В 18.07.2021 в 18:41, Fester сказал:

мы начинаем предварительно загружать некоторые необходимые игровые компоненты, такие как текстуры мыши и абсолютный минимум файлов пользовательского интерфейса

Так, стоп, это какие ещё мыши :scare:

Ссылка на комментарий

SirMichael
21 минуту назад, Aurelius36 сказал:

Так, стоп, это какие ещё мыши 

Указатель мышки меняется на кастомный? ИМХО, это оно.

  • Like (+1) 1
  • good (+1) 1
Ссылка на комментарий

Aurelius36
1 минуту назад, SirMichael сказал:

Указатель мышки меняется на кастомный? ИМХО, это оно.

Точно! А я ещё думаю, что там за мыши завелись :D

Ссылка на комментарий

1 минуту назад, SirMichael сказал:

Указатель мышки меняется на кастомный? ИМХО, это оно.

 

Так точно, указателя мышки :smile37:

  • good (+1) 1
Ссылка на комментарий

1.3 Загрузка базы данных


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

     Так как же мы довольно просто добиваемся того, чтобы DLC и файлы модов, работали на том же уровне, что и игра? Для нашей файловой системы мы используем интегрированную и модифицированную версию PhysicsFS, или сокращенно PhysFS, которая отлично работает, позволяя вам устанавливать несколько каталогов в одну логическую иерархию, в которой в дальнейшем Вы можете выполнять поиск, для того чтобы найти определенные файлы или несколько файлов в иерархии для загрузки.

    Во время запуска, когда мы настраиваем нашу файловую систему, мы устанавливаем наши основные директории ядра игры, а затем, когда мы загружаем дополнения и моды, мы заглядываем в каталог пользователя и проверяем, что доступно, и устанавливаем эти каталоги по тому же пути. Поэтому в поисках папки, такой как “common/landed_titles”, мы будем искать как в основном каталоге игры, так и в каталогах доступных загруженных модов и будем рассматривать их как одно и то же с точки зрения кода при загрузке этих файлов в базе данных названий титулов.

    Так как же мы загружаем эти базы данных? В текущей версии CK3 мы загружаем их все в одной из наших фоновых задач и по очереди загружаем, причем порядок поддерживается с помощью заданных зависимостей, так как некоторым системам необходимо ссылаться на другие. Это то, что долгое время работало “достаточно хорошо”. Но по мере того, как мы добавляли всё больше скриптовых систем и объём загружаемого контента стал огромным, то в этой методике начали проявляться недостатки.

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

     Следует отметить, что эта идея параллельной загрузки баз данных не была бы сделана без работы, которую проделал наш программист, @MatRopert, технический руководитель HoI4, когда он работал над Stellaris. Его работа улучшила нашу файловую систему PhysFS. Она стала работать намного лучше в среде с большим количеством потоков. Если бы работа с файлами была единственным узким местом для потоков данных, то мы не получили бы никакой выгоды. Это распространенная проблема, когда за одним узким местом скрывается множество других. Я бы рекомендовал посмотреть Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое. , хотя обратите внимание, что она адресовано программистам, в отличии от этого поста.

     Так что же сделал я? Первым шагом была формализация зависимостей нашей базы данных в корректном графе кода, с которым я мог бы затем работать. Если бы она была слита на диск в виде образа, можно было бы поплакать над нашими зависимостями, запустить автоматические тесты на нём, чтобы убедиться, что изменения и дополнения не рушат всё, или выполнить непосредственно загрузку всего, для того чтобы мы могли играть в игру.

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

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

 

Спойлер

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое. spacer.png

 

 

     Загрузка этого графа - это то, где мы получаем прирост производительности, так как мы точно знаем, какие базы данных зависят от того, что мы можем загрузить в параллельные базы данных, которые НЕ зависят друг от друга, что является основой баз данных. Затем, когда что-то загрузилось, оно может уведомить зависящие от него объекты о том, что загрузка была завершена, и если эта зависимость была последней, они могут начать свою загрузку и так далее.

    О том, что связано с потоковой передачей данных, всегда проще сказать, чем сделать. Вы должны убедиться, что ваши операции не конкурируют друг с другом или не устанавливают исключительные права пользования или атомарные операции, где это необходимо, чтобы все оставалось синхронизированным. И при этом убедитесь, что увеличение скорости, которое вы производите, не будет уменьшена всей синхронизацией, которую вы выполняете.

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

      Итак, некоторые числа после этих изменений, а также других, сделанных мной и @Meneth:

  • Внутренние отладочные сборки раздела загружались более 20 секунд, а теперь время сократилось до менее чем 4 секунд.
  • В конечная сборка раздела сократилась с 7 секунд до менее чем секунды.

     Конечно, это не приведет к мгновенной загрузке игры и не лишит Вас созерцания замечательной основной заставки, но определенно сэкономит Вам время, а нам сэкономит много времени, которое мы сможем потратить на создание игры вместо того, чтобы просматривать твиттер в поисках мемов…

 

 

Спойлер

spacer.png

 

1.4 История в реальности


    Как только мы переходим в главное меню, то можем выполнить заключительную часть запуска игры: загрузка сохранения или начало новой игры. Оба варианта довольно похожи, поэтому я объединяю их вместе.

     С точки зрения высокого уровня архитектуры, загрузка сохранения  проста: мы создаем наше базовое состояние игры, а затем просто просматриваем ваш файл сохранения и читаем разные его части, которые заполнены разными данными игры. Наши сохранения и все файлы в наших играх используют довольно простой синтаксис данных, который мы читаем.

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

    Примером этого являются модификаторы, которые применяются к персонажу из многих источников. Мы не записываем их в наши обычные сохранения, потому что это очень большой объём данных, который мы можем полностью восстановить из сохранения. Мы записываем эти данные только при выполнении несинхронной проверки и хотим их сравнить, чтобы убедиться, что ни одно из значений не отличается или что наши кэши неверны.

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

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

 

Спойлер

spacer.png

 

    Если вы мододел, вы можете заметить, что в настоящее время у нас гораздо меньше небольших файлов хронологии. Вместо этого мы объединяем их в более крупные. Это связано с тем, что система открытия файлов Windows работает не так быстро, как в Linux или OSX, поэтому мы вместо этого стремимся открывать реже, но большие файлы. Тем более, что мы стараемся выполнять большинство наших загрузок чаще параллельно. Так как уменьшение времени ожидания в файловой системе - великое благо, отсюда и меньшее количество маленьких файлов.

 

1.5 Мы сделали это!


    Итак, мы вошли в игру! Круто! Самое время поиграть, или, если вы занимаетесь оптимизацией, закрыть её, изменить часть кода и запустите снова несколько раз, чтобы изменить больше...

   Я надеюсь, вам немного понравилась эта маленькая статья (можно ли считать, что 8 страниц в Google docs мало?) о погружении в некоторые аспекты игры и её технические детали. Я старался не углубляться в сложные аспекты, потому что большинство из вас, скорее всего, не являются инженерами-программистами, но, пожалуйста, дайте отзыв, если уровень подачи слишком высок или становится слишком техническим, чтобы я мог подстроиться для будущих публикаций!

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

   Но это не относится к разработке игр. Это одна из крупнейших индустрия развлечений в мире, но большинство пользователей на самом деле не знают, что в неё входит, что приводит к неправильным представлениям, и я думаю, что чем больше мы сможем попытаться опустить занавес тайны, тем легче нам будет общаться с нашими фанатами в целом. И, надеюсь, заинтересует кого-нибудь сделать карьеру в ИТ.

    Так что это все на этой неделе, ребята! Не стесняйтесь задавать любые технические вопросы, и я постараюсь сделать всё возможное, чтобы ответить на них!

  • good (+1) 2
Ссылка на комментарий

Заключение к Части 1. Начало.

 

Спойлер

Действительно, очень интересный пост! Действительно приятно видеть такие истории и с технической стороны.
Я был удивлен, увидев, что используется SDL (я думал, SDL2). Вы используете его только для инициализации контекста OpenGL и звука/ввода или он выполняет какие-то простые функции рендеринга?

 

SDL2 да, честно говоря, его реальное применение крайне редко в нашей игре, в основном это самая малая часть нашего движка. По моему опыту работы с ним в движке, мы используем его для нескольких базовых вещей, обработки событий ввода с мыши и клавиатуры и т. д., создания контекстного окна верхнего уровня, а также получения некоторой информации об оборудовании для передачи другим рендерам. О, а также поддержка буфера обмена для упрощения копирования и вставки!
Во всём остальном, насколько мне известно, мы не используем сам SDL очень сильно. У нас есть либо более конкретные сторонние библиотеки, такие как fmod для звука. Мы просто используем что-то более стандартное, например, обычные потоки C++ вместо SDL, либо мы внедрили нашу собственную систему и добавили абстракции, чтобы самостоятельно работать с различными операционными системами.

 

Спойлер

Истории о маленьких хитростях, которые вам нужно сделать в игре, чтобы решить проблему, о которой никто не заподозрит, кроме как на самом деле попытаться создать игру, - это весело. Например, Windows интерпретирует отсутствие реакции на вводимые пользователем данные как признак сбоя программы, а не отсутствия необходимых вводимых данных.
Очень интересная штука!

 

Я имею в виду, что часть "соглашения", которое программа совершает, запускаясь в Windows, заключается в том, что "Я всегда буду оперативно реагировать на ввод".
Ввод должен обрабатываться часто; предпочтительно более 60 раз в секунду, но как минимум один раз.

Windows интерпретирует отсутствие такой обработки не как сбой, а скорее как остановку. Что объективно и есть; входная очередь слишком долго оставалась необработанной. Причина в том, что в подавляющем большинстве случаев не реагирование на ввод данных означает, что что-то пошло не так.

 

Спойлер

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

 

Анализ файлов во время компиляции исходного кода? Это было бы довольно сложно, учитывая природу DLC и модов, и изменение текстового файла потребовало бы перестройки exe файла, что в некоторой степени противоречит цели динамических данных, если нам нужно будет вставить их обратно в exe. Не говоря уже о том, что это потребовало бы от нас constexpr во всём нашем коде, что, хотя я часто предпочитаю для уменьшения времени выполнения задач, где это возможно, невозможно для чего-либо, нуждающегося в переменных данных в куче, до C++20, который еще никто особо не использует.

Может быть, вы имеете в виду еще один шаг между пересылками игры, когда мы объединяем все файлы вместе в какой-то большой двоичный объект данных, который мы сможем быстрее прочитать при загрузке? Ведь мы учли что проблема заключалась в том, чтобы указать, какие дополнения и моды включены, а файлы были изменены для включения. И поскольку наши запуски в настоящее время довольно хороши, мы не сильно исследовали такой масштабный сдвиг парадигмы, как этот. Хотя я знаю, что это периодически возвращающаяся идея!

 

Спойлер

Будет ли структура нового поколения игр поддерживать определённое начальное значение зерна для получения воспроизводимых начальных сценариев?

 

При случайном зерне у нас есть возможность при загрузке в режиме отладки задать определённое значение аргументу командной строки, чтобы повторять тестовые модели. Мы используем это в наших автоматизированных тестах, таким образом ИИ и игра всегда поступают одинаково.

 

Спойлер

Вы рассматривали возможность использования чего-то другого, кроме SDL? Особенно когда дело доходит до обработки данных с клавиатуры?

 

Я уверен, что мы рассматривали возможность использования чего-то другого, кроме SDL, но прямо сейчас он уже есть и работает отлично. С точки зрения игровой команды мы используем её абстракции, так как наш движок в разное время работал вещах отличных от ПК, например, консоль Stellaris, поэтому у нас есть более общая система "событий ввода", которую мы используем. Поэтому, если наша команда разработчиков в один прекрасный день переключится с SDL или мы в игровой команде найдем причину, чтобы поддержим их, тог я буду полностью за это.

 

Спойлер

Это очень интересно. Спасибо, что поделились!

Я знаю, что это форум CK3, и это игра, над которой вы работаете, но, возможно, вы можете ответить на такой вопрос: некоторое время назад разработчики EU4 сделали так, что больше нельзя вернуться в главное меню, если было загружено сохранение. Вместо этого программа выходила на рабочий стол, и все приложение необходимо перезагружать. Причина для этого заключалась в том, что было много ошибок, когда люди загружали сохранения из разных кампаний, и перезагрузка всего приложения, казалось, решала эти проблемы. Они знали, что это неправильный способ решения , и попытались исправить ее в патче 1.30, но в конце концов решили, что это слишком сложно.

У меня есть два основных вопроса:

  1. Каковы технические причины, по которым перезагрузка всего приложения позволила бы устранить эти ошибки, и
  2. Почему EU4 - единственная игра, которая так работает? Это то, что, вероятно, должна делать и CK3 и остальная линейка Paradox, или это что-то специфичное для более старой кодовой базы EU4? Я не думаю, что в CK2 все делали так, но я загружал эту игру очень давно.

В EU4 намного более старый основной код, чем в играх вроде CK3. Проблема заключается в количестве состояний игры и других данных приложения, которые необходимо очистить. Если так не сделать, это приведёт к другой контрольной сумме и потенциально повреждению состояния игры (хотя это встречается реже). Следовательно, необходим перезапуск, чтобы избежать этого. EU4 делает это уже довольно давно.

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

У CK2 действительно была эта проблема. Она просто не пустила бы вас на главный экран вместо того, чтобы перезапуститься, чтобы все исправить. Что касается того, откуда разница, я бы предположил, что это потому, что в EU4 было больше вещей, которые не были тщательно очищены до такой степени, что самым безопасным для начала новой игры, было просто перезапустить все.

  • Like (+1) 1
Ссылка на комментарий

Заключение к части 1.

 

Спойлер

Я не смог поиграть в Mass Effect после релиза, так как не мог пропускать заставки. Я решил дать ей ещё один шанс после выхода переиздания и примерно через час удалил игру и оформил возврат по той же чёртовой причине.
По теме: На основании чего было принято решение запаковать все аудиофайлы в проприетарные .BANK файлы и не добавлять файл конфигурации звукового содержимого, для возможности игроком включения/выключения отдельных звуковых эффектов?
CK2 был великолепен в этом плане, так как звуковые эффекты находились в отдельных файлах. Если один или два конкретных звука были особенно раздражающими, игрок мог работать с ними. Когда  вышел CK3 v1.1 с постоянным звуковым сопровождением осады, он настолько сводило меня с ума, что в конце концов я просто переименовал все звуковые файлы  с расширением .BANK файл, чтобы избавиться от этого звука. Вместе со всеми другими звуками, конечно.

 

Хмм, не могу сказать, что я испытывал похожие трудности при перепрохождении Легендарного издания. Я пропускал много сцен.
Файлы .BANK созданы потому, что мы используем гораздо более сложную и лучшую стороннюю аудиотеку под названием FMOD. Наша старая озвучка была слишком простой, чтобы удовлетворить желания наших аудиодизайнеров и композиторов.

 

Спойлер

Поскольку мы говорим о фазе загрузки: что случилось со значком "Инициализация игры" в левом верхнем углу, который выглядит так, как будто он должен повернуться, но не поворачивается ? =)

 

Я на 80% уверен, что это просто потому, что мы не обновляем анимационную систему во время загрузки внешнего интерфейса или что-то в этом роде. Это очень незначительное замечание, поэтому мы не занимались этим вопросом.

 

Спойлер

Хочу обратить внимание, что необходимо больше оптимизации времени загрузки игры. Даже с процессором i9 и твердотельным накопителем m2 со скоростью 3,5 ГБ/сек загрузка игры занимает очень много времени по сравнению с другими играми.

 

Для всего нужно больше оптимизации. Я думаю, это очевидно. Это вопрос первостепенной важности.
С хорошим процессором и твердотельным накопителем я был бы удивлен, если бы загрузка занимала много времени. На моём достаточно хорошем, но не идеальном компьютере загрузка занимает меньше 60 секунд , и это в режиме полной отладки. В режиме конечной версии загрузка происходит намного быстрее.
Жесткие диски работают намного медленнее, и мы знаем об этом, хотя это опять же вопрос приоритетов работы.

 

Спойлер

На протяжении многих лет я пробовал использовать различные решения для визуализации графов зависимостей. Что вы использовали для визуализации своего графа зависимостей? Он преобразовывался прямо из кода объекта или через промежуточный текстовый дамп? И если Вы можете ответить, к какому классу относится объект graph в вашем коде?

 

Я использовал формат “DOT” в качестве промежуточного текстового дампа, а затем онлайн-инструменты, такие как GraphVis и подключаемый модуль проводника, чтобы визуализировать этот текстовый дамп.

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

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

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

 

Спойлер

Вы не хотите использовать отладочные сборки во время игры или хотя бы использовать какое-нибудь иное программное обеспечение? Отладочные сборки могут дать некоторые "дополнительные" возможности по сравнению с конечными сборками (помеченными в коде c++ чем-то вроде #ifdef DEBUG), но обычно их не так много. Такого рода "возможности" обычно существуют для обхода некоторых ограничений в вашей среде, когда вам требуется какие-то особые параметры во время отладки. Но во время отладки вы обычно хотите, чтобы приложение работало как можно ближе к конечному продукту.

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

Например, где-то в коде у вас может быть что-то вроде этого:


Код:
auto a = 1;
auto b = a;//и после этого a не используется в остальной части кода
auto c = b;//и после этого b не используется в остальной части кода
auto d = c;//и после с не используется в остальной части кода
auto e = d//и после этого d не используется в остальной части кода
auto f = e;//и после этого e не используется в остальной части кода
// теперь сделайте что-нибудь с f
// у вас не будет этого примера в реальном коде, я просто показываю его в качестве примера, чтобы объяснить, что можно оптимизировать


В конечной сборке компилятор может сказать, что он не использует переменные a, b, c, d, e, поэтому заменит их на auto f = 1; Это намного быстрее и занимает гораздо меньше памяти. Но в отладочной сборке эта замена никогда не выполняется. В режиме отладки вы хотите видеть, когда переменной a присваивается значение 1, затем переменной b и т. д. Вы хотите знать, в какой момент выполняются эти строки.

Конечно, это абсурдный пример, вы никогда не напишете такой код. Но есть гораздо более тонкая оптимизация, при которой код сокращается и ускоряется во время сборки конечных версий. Это пример, чтобы показать суть.

Таким образом, при использовании программного обеспечения Вы не будете выяснять, почему всё работает неправильно, вы предпочтёте использовать конечную сборку, а не отладочную.

Что касается различий между "-debug_mode" и его включением в игру через графический интерфейс, я бы сказал, что есть некоторые дополнительные различия. Наверняка во время загрузки подключены FileWatcherы, но у вас, вероятно, не будет такой возможности при графическом интерфейсе. И, возможно, Вы ведёте журнал. Я не могу придумать больше идей о том, что может быть при "-debug_mode" ещё, кроме графического интерфейса. Но всё это, если есть дополнительные возможности, должно быть связано с тем, что этот присутствует флаг во время загрузки. И все эти функции относятся не к отладочной сборке, а к отладочному режиму в конечной сборке.

 

В дополнение к этому, скриншот реального примера из CK3..
Здесь представлена информация, доступная, когда я приостанавливаю выполнение игры во время выполнения функции, которая занимается обработкой всех данных для создания кадра в финальной сборке.

 

spacer.png

 

spacer.png

 

Около половины переменных полностью исчезли. 4 отдельные функции в стеке вызовов тоже исчезли (те, которые помечены как "Inline Frame").
Попытка отладить это - сущий ад, поэтому мы в основном делаем это только в том случае, когда в финальной сборке произошел сбой, который мы не можем воспроизвести в отладочной сборке.
В отладочной сборке ничего из этого не исчезло бы, что значительно облегчило бы мою жизнь программиста.

 

Очень хорошее изложение, спасибо за ваш пост!

Ваша догадка насчёт командной строки по сравнению с консольной командой для режима отладки также верна. Она просто включается раньше, поэтому всё, что требуется - чтобы режим отладки работал во время загрузки. Например, прикрепление file watcherов и ведение дополнительного журнала, перед входом в игру с настройками пользовательского интерфейса для включения режима отладки.

 

Спойлер

Мне любопытно, как вы настроили систему, в которой события, локализация и некоторые другие файлы могут быть перезагружены во время игры, когда работает моддинг.

 

У нас есть система наблюдения за файлами, в которой мы используем особую функциональность завёрнутую в единый интерфейс, чтобы получать уведомления об изменениях в пути к файлу.

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

Таким образом, код, который хранит и использует, например, некоторые черты характера из баз данных, никогда не узнает, что черта характера изменилась. Просто в следующий раз, когда они попытаются получить, например, модификатор, к которому применяется черта, он увидит новые значения и будет использовать их .

 

Спойлер

Вызову ли я ошибку сегментации, если я загружу изменение, заменяющее значение однострочного скрипта формулой в 300 строк, которая использует все базы данных в игре?

Скорее всего, нет. Но, пожалуйста, не делай этого.

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

 

Спойлер

Да, просто хотел оставить комментарий и сказать, что это действительно было захватывающе! Как человек, только начинающий заниматься программированием, уровень знаний и опыта, необходимый для понимания и работы с этими системами, немного устрашает, но, с другой стороны, ваше объяснение потока на верхнем уровне действительно делает его вполне удобоваримым.

Без достаточных знаний, не могу задать точного вопроса. Мне было любопытно, как функции моддинга были переведены из основного кода игры в сценарий paradox. Сопоставим ли язык со всех концов, или он очень сильно видоизменился? И какие проверки входят в процесс добавления некоторых новых функций в язык, является ли безопасность большой проблемой?

 

Рад, что вам понравилось! Я определенно понимаю, насколько это пугает. Когда я начал изучать программирование несколько лет назад, это казалось таким огромным, и там было так много всего. Но плюс в том, что вам нужно всего лишь изучать по чуть-чуть каждый день или неделю, прежде чем вы достигнете критической массы для того чтобы начать писать свой код по-настоящему хорошо. И с этого момента вы сможете учиться, совершенствоваться и открывать все больше вещей по ходу дела!

Каждый триггер и эффект затем знает, что делать со своими данными, когда мы оцениваем/выполняем или создаем для них всплывающую подсказку. Во время выполнения у нас есть среда, которую мы проходим через древовидную структуру, чтобы оценить/выполнить что-то для данного персонажа, чтобы он знал, с каким значением золота ему нужно сравнить, например, с 500.

 

Спойлер

В случае, если вы еще не планировали, мне интересно узнать больше о десинхронизации и о том, что входит в многопользовательский режим.

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

Так получилось, что прямо там, где я живу и работаю, есть школа программирования, и я знаю довольно много людей, которые там учились. Они изучают навыки программирования, но мне кажется, что они не изучают управление проектами, как управлять эффективным рабочим процессом в команде и так далее.

 

Я запишу это как идею для будущего!

  • Like (+1) 2
Ссылка на комментарий

 Часть II. Изменения состояний игры

 

    Всем добрый день! Меня зовут Магне Скьяран, и я старший программист в Crusader Kings 3. Я хочу представить вам вторую статью из серии "Анатомия игры". 
    Сегодняшняя тема будет посвящена тому, как мы меняем состояние игры. Это основа всей модели игры, так как, если всё неизменно, то и игры нет.

    Здесь я затрону три основные темы. Сначала я расскажу о системе команд, с помощью которых происходят всё взаимодействия с игрой. Затем поговорим о том, как мы определяем, что нужно изменить по сравнению с тем как мы на самом деле это меняем. И в конце расскажу о том, что такое рассинхронизация и как она возникает.

    Всё, что я здесь расскажу, будет основано на CK3, но многое из этого относится и к другим нашим играм. Но есть и различия; иногда большие! Так что не ассоциируйте всё с другими нашими играми.


2.1 Система команд


    Суть нашей игры - моделирование. Оно работает само по себе, даже если ни один посредник (игрок или искусственный интеллект) не вносит в него какие-либо изменения. Но модель, на которую вы не можете повлиять - скорее обычная игрушка, чем настоящая игра. Вот здесь на помощь и приходят команды.

Команда - это набор данных о том, как изменить состояние игры. Простым примером может быть команда “поставить в очередь перемещение этого подразделения в эту провинцию” или “выбрать это событие”.
   Все взаимодействия, происходящие с помощью команд, также облегчают нам поиск всего, на что игрок может повлиять, что облегчает отладку различных ошибок и даёт нам понимание о том, как работает игра.

 

Спойлер

spacer.png


    Система команд - основа многопользовательской игры (МП). Все, что делает игрок, передаётся оборудованию других игроков путем отправки команд по сети. Таким образом, встраивание всех взаимодействий в систему делает мультиплеер JustWork™ в подавляющем большинстве случаев без необходимости писать отдельный код для МП. Когда программист внедряет новую систему, редко приходится много думать о многопользовательской игре (в то время как дизайнеру, вероятно, нужно немного подумать, чтобы убедиться, что эта функция интересна как в ОП, так и в МП).

     Неудивительно, что взаимодействие с игроком происходит через интерфейс. Интерфейс это модуль не связанный с игровой логикой; он охватывает такие вещи, как то, что показывать и как с ними взаимодействовать. Игровая логика вообще не знает о существовании интерфейса в коде, что позволяет избежать целого класса ошибок, когда логика каким-то образом зависит от интерфейса, что иногда случается в наших старых играх. Интерфейс может считывать только состояние игры, и это обеспечивается нашими программами. Команды - это единственный способ для интерфейса повлиять на состояние игры.

 

Спойлер

spacer.png

 

     Поскольку состояние игры не может видеть существование интерфейса, это означает, что ему трудно взаимодействовать с интерфейсом. Естественно, это может вызвать проблемы. Например, представьте, что с игроком что-то происходит, и мы хотим отправить уведомление об этом; например, если игрок повысит уровень престижа. Конечно, интерфейс может сохранить уровень престижа игрока, а затем создать уведомление при его изменении, но это приведёт к копированию большого количества состояний между логикой и интерфейсом. Поэтому вместо этого у нас есть система, аналогичная командам для отправки информации из логики в интерфейс, которую мы называем “сообщениями”. Как и команды, это конкретные фрагменты информации, на которые интерфейс должен каким-то образом воздействовать. Они обрабатываются в интерфейсе, поэтому, когда логика отправляет сообщение “игрок повысил свой уровень престижа”, интерфейс занимается тем, чтобы показать это уведомление игроку.

     Теперь достаточно об игроке. А как насчет искусственного интеллекта? ИИ, по существу, играет по тем же правилам. Все, что не происходит с помощью самой модели, также выполняется командой для ИИ. Периодически ИИ принимает различные действия, которые он может совершить, и для всего, что он решит сделать, он отправит команду. Обычно это точно такая же команда, которую послал бы игрок; игрок и искусственный интеллект будут использовать одну и ту же команду, например, для “переместить это подразделение в эту провинцию”.
Искусственный интеллект и игрок, использующие одну и ту же систему, упрощают нам возможность гарантировать, что они будут играть по одним и тем же правилам. Еще более важно, что одно и то же предпринятое действие дает один и тот же результат, избегая тонких ошибок из-за различий между тем, как ИИ и игрок взаимодействуют с игрой.

 

Спойлер

spacer.png

 

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

 

2.2 Оценка и выполнение


     Изменения состояния игры состоят из двух основных частей: принятие решения о том, что делать (оценка), и фактическое исполнение (выполнение). Чаще всего много времени уходит на то, чтобы понять, что делать, а не на то, чтобы сделать это. Например, выбор одного события из сотен доступных в ежегодном такте занимает больше времени, чем применение события, которое можно выбрать.

     Вообще, мы можем выполнять только одну вещь за раз (в противном случае мы выходим из синхронизации; подробнее об этом позже). Однако, используя потоки, мы можем анализировать несколько вещей одновременно. Таким образом, вместо того, чтобы персонажи по-очереди  решали, какие события запускать, мы можем рассмотреть 8 или более (в зависимости от того, сколько потоков процессор игрока поддерживает) событий персонажей одновременно. Затем каждый персонаж добавляет в очередь события, которые он собирается запустить. Эта часть должна быть синхронизирована, поскольку мы не можем добавить две вещи в список одновременно, но поскольку подавляющее большинство времени тратится на оценку, а не на добавление в список, мы экономим огромное количество времени, распределяя работу. Позже мы сможем просмотреть очередь и запустить каждое событие, удалив всё, что больше нельзя применить по какой-либо причине (может быть, персонаж умер?).

      Это разделение между оценкой и выполнением является одним из краеугольных камней того, как мы выполняем потоковую обработку на CK3. Состояние игры разделено на различные “менеджеры”, каждый из которых отвечает за одну часть игры. Например, есть Менеджер Секретов, Менеджер Событий, Менеджер Персонажей и Менеджер Титулов. Основная часть того, как мы продвигаем игру за один день, разделена на две части: предварительное обновление и основное обновление. В предварительном обновлении каждый менеджер проводит свои собственные расчёты и делает заметки о том, что нужно сделать позже. Видимое состояние игры не изменяется, поэтому каждый менеджер может спокойно смотреть на вещи, которыми он не управляет (например, Менеджеру Титулов разрешено смотреть на обладателя титула, даже если у него нет персонажей). Вместо этого они могут изменять только то, что невидимо для остальной части игры (например, очередь событий, упомянутая ранее).

 

Спойлер

spacer.png


    Разделение позволяет нам очень легко создавать потоки, так как нужно следовать только одному правилу (не изменять видимое состояние). В потоковой передаче в нашей старой игре было гораздо больше правил, которым нужно было подчиняться (смотрите только на свои собственные данные, не смотрите туда, не изменяйте это и т. Д.), Что означало, что как опытным программистам, так и новичкам было легко совершать ошибки. С помощью только одного правила ошибки стало совершать сложнее, легче поймать и легче исправить. В результате мы стали более продуктивными, и CK3-наша самая многопоточная игра на сегодняшний день.

    ИИ работает примерно также. Он запускается после основного обновления, а не до него, но работает по тому же принципу. ИИ не имеет права изменять что-либо, кроме определенных частей внутреннего состояния ИИ, и вместо этого просто посылает команды. ИИ разделен на множество подзадач, составленных вместе на основе периодичности. Например, отдельный ИИ проверит, следует ли ему изменять свои законы и следует ли ему покидать фракцию в одной задаче, поскольку это происходит с одинаковой частотой. Каждая такая группировка задач может выполняться одновременно с любой другой группировкой задач. Детализация означает, что потоковая обработка ИИ очень эффективна (известная как “хорошая балансировка нагрузки”), поскольку один поток вряд ли выполнит свою работу значительно раньше, чем другой поток (что переведёт его в режим ожидания).

 

Спойлер

spacer.png


     Как упоминалось ранее, использование системы команд означает, что влияние ИИ хорошо обособлено от процесса принятия решений. Это облегчает повторение, облегчает осмысливание и облегчает оптимизацию.
    Теперь давайте перейдем к последней теме сегодняшнего дня:  рассинхронизация.

 

2.3 Рассинхронизация.

 

     Если вы играете в мультиплеер любой из наших игр, вам известен особенно страшный набор слов: “игра не синхронизирована”. Когда это произойдет, вы не сможете продолжить игру, и в зависимости от игры вам придется либо перезапустить, либо выполнить повторную синхронизацию. Но что такое рассинхронизация (OOS), помимо того, что мы, программисты, смеемся над Вашим несчастьем?

 

Спойлер

 

spacer.png


    Чтобы объяснить, что такое OOS, мне сначала нужно объяснить, как работает сам мультиплеер. В большинстве игр суть многопользовательской игры заключается в том, что сервер (или машина игрока, действующая в качестве сервера, если сеть одноранговая (P2P) сообщает всем клиентам о состоянии всего в игре. Где всё это: куда оно движется, сколько у него осталось здоровья и т. Д. Обычно не учитывается только то, что является статичным (например, как выглядит карта во многих играх). В соревновательных играх часто также упускаются вещи, о которых клиент не мог бы знать (например, положение другого игрока на другой стороне карты) для борьбы с WallHack и тому подобным.
Как правило, это очень разумная модель, но она ломается, если по сети несколько раз в секунду передается слишком много данных о состоянии игры. В шутере от первого лица с 10, 20, может быть, даже 100 игроками вся эта информация может храниться в нескольких килобайтах, но CK3, для сравнения, обычно содержит около 20 тысяч символов, не говоря уже обо всём остальном. Полное состояние игры CK3 занимает от 30 до 100 МБ для хранения в несжатом состоянии, и даже при сжатии вы легко получите 10-20 МБ. Очевидно, что это не то, что мы можем отправлять по сети часто.

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

Но вот в чем проблема: это означает, что мы должны убедиться, что каждый клиент моделирует игру одинаково. Потому что, если что-то изменится, каким бы незначительным оно ни было, это крошечное изменение в конечном итоге приведёт к возникновению резких различий между тем, что происходит на каждой машине. Таким образом, в то время как одному игроку только что объявили войну какие-то викинги, на другом клиенте этого бы вообще может не произойти.

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

   Так как же происходит рассинхронизация? Обычно это сводится к отсутствию детерминизма. Детерминизм-это когда одни и те же входные данные всегда приводят к одному и тому же результату. Пока это так, рассинхронизация невозможна (за исключением случаев, когда какие-либо входные данные потеряны или повреждены, скажем, из-за проблем с сетью). Но детерминизм-это нелегко.
    Это достаточно просто, если ваша игра однопоточная, но тогда она будет медленной. Любая потоковая передача может привести к недетерминированному поведению, если вы не будете осторожны. Наиболее распространенный случай связан с проблемой порядка. Допустим, у вас есть номер X. Он имеет значение 10. Поток А хочет добавить к нему 2. Поток B хочет умножить его на 2. Если поток A запустится первым, конечный результат будет (10 + 2) * 2 = 24. Но если поток B запустится первым, то будет (10 * 2) + 2 = 22. Поэтому, если по какой-либо причине потоки выполняются в разном порядке на двух машинах (возможно, одно ядро процессора было занято чем-то другим в течение доли секунды), произойдет рассинхронизация.

    Это основная причина, по которой мы обычно проводим только многопоточный расчёт. Если ничего не изменилось, то порядок не имеет значения. Иногда мы также следим за вещами, которые изменяют видимое состояние, но это гораздо реже, и мы гораздо более осторожны в том, чтобы гарантировать, что порядок не имеет значения.

   Другой причиной рассинхронизации, которая была гораздо более распространена в наших старых играх, был интерфейс, каким-то образом влияющий на состояние игры. В качестве простого примера, представьте, что у нас есть какое-то значение, которое мы редко обновляем, потому что обновление отнимает много времени. Но когда игрок смотрит на него, мы хотим, чтобы оно было новым. Может возникнуть соблазн заставить его обновиться, когда плеер откроет интерфейс, но упс… теперь вы ввели рассинхронизацию.
    То, как мы структурировали CK3, значительно усложняет создание этой ошибки, так как гораздо сложнее изменить состояние игры с помощью интерфейса. Вместо этого мы отправили бы команду для обновления значения и/или, возможно, выполнили бы вычисления для нового значения только в интерфейсе и оставили бы состояние игры нетронутым.

   Аналогичным образом, легко создавать проблемы из-за особенностей игровой логики в зависимости от того, является ли персонаж локальным игроком или нет. Например, мы хотим обновлять прогнозируемый доход игрока ежедневно, а не ежемесячно, чтобы информация об игроке была актуальной. Наивная реализация здесь означала бы, что на каждом клиенте персонаж клиента обновляется ежедневно, но другие игроки обновляются ежемесячно. Таким образом, игра будет рассинхронизирована, так как у персонажей игроков будут сохранены  разные доходы.
    В CK3 мы избегаем этого, просто проверяя, что они являются игроком, а не человеком, играющим на этой машине. Кроме того, мы намеренно усложнили проверку “является ли локальным игроком”, чем просто проверку “является ли любым игроком”. Нам все еще немного нужно первое (в первую очередь для отправки уведомлений), но оно предполагает, что программист в основном говорит: “Да, я уверен, что знаю, что делаю”.:

 

Спойлер

spacer.png


   Обратите внимание на “ALLOW_GET_LOCAL_PLAYER_IN_SCOPE”; это наш способ убедиться, что мы проверяем, кто является локальным игроком, только если нам это действительно нужно. В противном случае мы легко закончим тем, что изменим что-то только у персонажа игрока у клиента, фактически играющего за этого персонажа.

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

  • Like (+1) 1
Ссылка на комментарий

Заключение к части II.

 

Спойлер

Когда я прочитал "система команд", у меня появилась слабая надежда, что вы наконец устраните проблему, которая не позволяет нам включать и выключать debug_mode в запущенной игре (как в консольных командах). Как в старых играх PDS.

Но нет, для этого нам все еще нужны моды. *sigh*

 

Это совершенно не связано, и это технический пост, в котором мы не будем описывать ничего нового. Мы даём объяснение и информацию о том, как работает игра.

Мы уже объясняли причину включения debug_mode, и это расстраивает. С учетом того, что режим легко включить с помощью флага запуска, и это совсем не то, что необходимо пользователям по умолчанию, эта задача не является ключевой. В связи с этим переработка системы контрольных сумм, с целью её работы в игре, не является первостепенной задачей, по сравнению с работой над другими возможностями и исправлениями в игре.

 

Спойлер

Есть ли причина, по которой Вы делаете рехост, а не приостанавливаете игру, чтобы хост отправил всем точное состояние игры для повторной синхронизации? Я думаю, если что-то пойдет не так, можно было бы ожидать, что даже после повторной синхронизации, снова произойдут отклонения?

 

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

 

Спойлер

Еще одна замечательная статья!

От лица моддеров, есть ли что-то, о чём мы должны знать в отношении рассинхронизации? Либо подводные камни, которых следует избегать, либо хорошая практика, которую следует соблюдать?

Или у нас всё равно будет происходить рассинхронизация, так как защита от сбоев со стороны кода будет препятствовать этим попыткам с нашей стороны?

 

Если мод вызывает рассинхронизацию, то это ошибка в игре, а не в моде.

Если бы у нас были какие-либо подводные камни, о которых мы знаем, мы бы исправили их, а не предупреждали вас о них. За исключением предупреждения, если такое исправление будет сделано не скоро.

Определённо, существует некоторая вероятность того, что моды могут сделать что-то, что вызовет рассинхронизацию, которой нет в ванили (или происходят гораздо реже). Если мод вызывает много сбоев, то мы бы хотели знать об этих ошибках.

 

Спойлер

Приятно знать об этом! Есть ли какая-либо конкретная информация, которую было бы полезно предоставить в отчёте об ошибках рассинхронизации в игре с модом, или вам просто нужны журналы сообщений рассинхронизации? Я полагаю, что изучение того, как мод вызывает рассинхронизацию - титаническая задача, но вы всё равно хотели бы, чтобы все файлы модов, были включены в отчёт об ошибке (какими они были во время рассинхронизации)?

Недавно я получил несколько отчётов о рассинхронизации, отправленных моими игроками, и определил, что единственное различие в состоянии игры состояло в том, что в одной провинции был модификатор supply_limit_mult 0,25, а в другой-нет, но нет информации о том, чем это вызвано. Насколько я могу судить, единственным статическим модификатором, который мог бы сделать это сам по себе, является модификатор из ванилы coastal_province_modifier, но он нигде не используется - ни в ваниле, ни в самом моде.

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

 

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

Да, нам нужны файлы модов.

Это очень специфическое различие в модификаторах. Звучит довольно интригующе и может быть достаточной отправной точкой для нашего исследования.

 

Спойлер

В ответ на: Искусственный интеллект и интерфейс
Мне было любопытно, как ИИ обрабатывает графические интерфейсы со скриптами? Если, скажем, я добавлю кнопку графического интерфейса скрипта в область графства, будет ли ИИ "видеть" кнопку в каждом графстве, в котором она есть? И будет ли это плохо сказываться на производительности?

Я бы очень хотел увидеть Дневник Разработчиков по ИИ, это звучит интересно.

 

ИИ не знает о заскриптованных графических интерфейсах, пока вы не создадите ИИ, который будет частью этого.
В этом случае это примерно равносильно принятию решения. Обратите внимание, что доступен только ИИ-персонаж, а не информация из интерфейса. Это довольно мало.

(Это основано на беглом взгляде кода; система заскриптованного графического интерфейса - это то, с чем я плохо знаком, поэтому я могу ошибаться)

 

Спойлер

Спасибо за объяснения.

Если я правильно понимаю, если я выкуплю персонажа обратно другому правителю в многопользовательской игре, то команда на изменение состояния игры сработает только тогда, когда другой правитель примет моё требование о выкупе. И если другие правители-это ИИ, то всё это происходит на моём компьютере (до тех пор, пока команда не будет отправлена), в то время как если другой правитель-игрок, ему будет отправлено уведомление для ответа.

Я правильно понял?

 

Вы направляете предложение - это  одна команда.
Соглашение другим правителем - другая, если он игрок. Если это искусственный интеллект, принятие обрабатывается без второй команды.

 

Спойлер

Когда у Paradox нет свободного менеджера по связям, они ставят программиста, чтобы развлекать людей. :D

 

Ну, наша команда сообщества тоже не пишет наши постоянные дневники разработчиков или летние тизеры ;) У них гораздо менее веселая работа-выслушивать мои глупые идеи, собирать отзывы, управлять общением и планировать его.

Хотя, к счастью, Трой был очень внимателен и взволнован, когда я предложил ему идею этого блога, как потому, что он хороший и не хочет убивать мои надежды и мечты, так и потому, что это новый тип контента для нас, который мы можем проанализировать в различных областях игры и рассказ о наших ролях для тех, кто заинтересован! :D

 

Спойлер

По теме рассинхронизации: мы с моим другом сталкиваемся с рассинхронизацией, возможно, каждые 50 лет или около того в нашей игре для двух игроков. Мы полагали, что использование многопользовательской бета-версии достаточно, чтобы гарантировать, что вы получите точную информацию о проблемах, с которыми мы сталкиваемся. Является ли это верной гипотезой?

 

Обязательно зайдите и оставьте сообщение об ошибке с информацией о вашей рассинхронизации и сбросьте Ваш файл ошибки!

 

Спойлер

Продумав всё это, у меня появилось три технических вопроса:

  1. В многопользовательской игре, как вы работаете с разными компьютерами, имеющими разную вычислительную мощность? На более старой и медленной машине эти вычисления займут больше времени и имеется меньше потоков для распределения работы. Итак, как игры синхронизируют шаг обновления в унисон, чтобы все оставались на одной и той же дате?
  2. Что касается обновления состояния игры, рассматривали ли вы идею, в которой изменение настроек сохраняются, а затем фиксируются с использованием транзакционной памяти? Или шаг обновления настолько быстрый, что он просто не связан с производительностью?
  3. Поскольку так много времени тратится на вычисления обновлений, компилируете ли вы игровые сценарии в байт-код или какую-либо другую внутреннюю структуру данных, чтобы интерпретатор не стал слабым местом?
  1. Если кто-то слишком сильно отстает, мы замедляем скорость, выбранную для всей сессии, или даже делаем паузу, чтобы они могли догнать. С нашими минимальными характеристиками и не совсем бесполезным Интернетом вы можете работать с приличной скоростью в сеансе MP.
  2. Эффекты большинства команд не являются узким местом нашей модели. Им является, скорее, общий ежедневный тик моделирования, а не какие-либо конкретные командные действия.
  3. Они считываются один раз во внутренних структурах данных во время загрузки, а затем мы просто просматриваем их для оценки и выполнения, не возвращаясь к файловой системе или интерпретации языка сценариев.
Спойлер

"Самая простая" реализация состояла бы в том, чтобы просто один компьютер (машина хоста) принимает все решения ИИ, а затем передаёт эти данные всем другим игрокам. С другой стороны я понятия не имею, как вы могли бы сохранить нормальное состояние игры, даже если бы попытались сделать большую часть обработки детерминированной; в конечном итоге у вас был бы один и тот же объект искусственного интеллекта (персонаж, армия или что-то еще) на двух машинах, одновременно принимающих разные решения.

 

Именно поэтому они используют команды. Только хост-машина фактически отправляет команды ИИ в сеансе, которые затем обновляют действия ИИ для остальных клиентов.

 

Спойлер

Статья, возможно, не для всех, но она очень информативна и поучительна. Я очень ценю это.

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

 

Я имею в виду, что в любом случае нет большой разницы между реальным временем с паузой и пошаговым в этом отношении, и то, и другое объединяются, когда между обновлениями существует гипотетически бесконечное количество времени, поэтому есть система, основанная на тиках. Реальное время с паузой представлено как тики с интервалом, в то время как пошаговое время это тик только по требованию.
Конечно, есть множество вариантов игрового процесса, и вы хотели бы обрабатывать изменения по разному, но само ядро в любом случае не будет  сильно отличаться.

  • Like (+1) 1
Ссылка на комментарий

Гость
Эта тема закрыта для публикации сообщений.
  • Ответы 11
  • Создано
  • Последний ответ
  • Просмотры 2454

Лучшие авторы в этой теме

  • Fester

    9

  • Aurelius36

    2

  • SirMichael

    1

Лучшие авторы в этой теме

Популярные сообщения

Fester

Анатомия Игры (перевод статей программистов CKIII)   От автора (balckninja9939 (Мэтью)         Привет всем! Меня зовут Мэтью, и я программист CK3, добро пожаловать на первый пост и

Fester

Часть I. Запуск и загрузка.   1.1 Настройка игры.        В сегодняшней статье я собираюсь начать с … с самого начала! Когда Вы запускаете игру и загружаетесь, чтобы выбрать своего

Fester

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

SirMichael

Указатель мышки меняется на кастомный? ИМХО, это оно.

Fester

1.3 Загрузка базы данных       Итак, до этого было много слов о том, что мы загружаем, и о некоторых причинах, лежащих в основе приоритетов загрузки. Теперь я перейду к более конкретному приме

Fester

Заключение к части 1.     Хмм, не могу сказать, что я испытывал похожие трудности при перепрохождении Легендарного издания. Я пропускал много сцен. Файлы .BANK созданы потому, что м

Fester

Так точно, указателя мышки 

Fester

Заключение к Части 1. Начало.     SDL2 да, честно говоря, его реальное применение крайне редко в нашей игре, в основном это самая малая часть нашего движка. По моему опыту работы с ни

  • Сейчас на странице   0 пользователей

    • Нет пользователей, просматривающих эту страницу


Copyright © 2008-2024 Strategium.ru Powered by Invision Community

×
×
  • Создать...