Gamedev Андрей Верещагин
5 873

Настоящие паровозы: как устроена физика поездов в Assassin's Creed Syndicate

Масса вагонов и ускорение.

В закладки

Бартоломей Вашак (Bartlomiej Waszak) из Ubisoft написал на сайте Gamasutra статью о том, как работает физическая симуляция поездов в Assassin’s Creed Syndicate. Оказывается, система учитывает множество параметров: от массы вагонов и их ускорения до силы трения. Мы выбрали из материала главное.

Вступление

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

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

У решения разработчиков было несколько преимуществ.

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

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

Система также поддерживает разъединение вагонов и их столкновения.

Контроль локомотива

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

Locomotive::SetDesiredSpeed(float DesiredSpeed, float TimeToReachDesiredSpeed)

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

Здесь F — это вычисляемая сила, m — масса локомотива, Vdiff — это желаемая скорость минус текущая скорость, а t — время, требуемое для того, чтобы достичь желаемой скорости.

Как только F найдена, её значение передаётся физическому состоянию вагона (WagonPhysicsState) как «сила двигателя» для того, чтобы вести локомотив.

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

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

Базовая симуляция

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

struct WagonPhysicsState
{
// Значения меняются в ходе интеграции:
// расстояние пути и импульс.
RailwayTrack m_Track;
float m_LinearMomentum;

// Скорость, рассчитанная от импулься.
float m_LinearSpeed;

// Текущее значение сил.
float m_EngineForce;
float m_FrictionForce;

// Позиция в мире и вращение, полученные напрямую от железнодорожного пути.
Vector m_WorldPosition;
Quaternion m_WorldRotation;

// Постоянная во время симуляции:
float m_Mass;
}

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

В качестве базового шага симуляции для каждого вагона использовался метод Эйлера. Здесь «dt» — это время для одного шага.

void WagonPhysicsState::BasicSimulationStep(float dt)
{
// Рассчёт производных.
float dPosition = m_LinearSpeed;
float dLinearMomentum = m_EngineForce + m_FrictionForce;

// Обновление импульса.
m_LinearMomentum += dLinearMomentum*dt;
m_LinearSpeed = m_LinearMomentum / m_Mass;

// Обновление позиции.
float DistanceToTravelDuringThisStep = dPosition*dt;
m_Track.MoveAlongSpline( DistanceToTravelDuringThisStep );

// Получение новой позиции от железнодорожного пути.
m_WorldPosition = m_Track.GetCurrentWorldPosition();
m_WorldRotation = m_Track.AlignToSpline();
}

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

Третье уравнение определяет импульс (P) как произведение массы и скорости.

Применение импульса к вагону — это просто ещё одна операция, которая добавляется к текущему импульсу.

void WagonPhysicsState::ApplyImpulse(float AmountOfImpulse)
{
m_LinearMomentum += AmountOfImpulse;
m_LinearSpeed = m_LinearMomentum / m_Mass;
}

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

Высокоуровневые шаги симуляции поезда

Вот так выглядит код полного шага симуляции для одного поезда.

// Часть A
Обновление стартовой скорости поезда

// Часть B
Для всех вагонов поезда
ApplyDeferredImpulses

// Часть C Для всех вагонов поезда
UpdateCouplingChainConstraint

// Часть D
Для всех вагонов поезда
UpdateEngineAndFrictionForces
SimulationStepWithFindCollision
CollisionResponse

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

Симуляция с коллизиями

Так выглядит код для функции SimulationStepWithFindCollision.

WagonPhysicsState SimulationStepWithFindCollision(WagonPhysicsState InitialState, float dt)
{
WagonPhysicsState NewState = InitialState;
NewState.BasicSimulationStep( dt );
bool IsCollision = IsCollisionWithWagonAheadOrBehind( NewState );
if (!IsCollision)
{
return NewState;
}
return FindCollision(InitialState, dt);
}

Cперва система проводит пробный шаг симуляции, обращаясь к функции NewState.BasicSimulationStep( dt ); и проверяя, зафиксированы ли в новом состоянии какие-то коллизии: bool IsCollision = IsCollisionWithWagonAheadOrBehind( NewState ). Если ответ будет отрицательным, то игра рассчитывает новое состояние. Однако если коллизия всё-таки есть, то применяется функция FindCollision, чтобы обнаружить точные время и физические данные до того, как коллизия произошла. Для этого используется бинарная система поиска.

Так выглядит луп поиска времени и физического состояния коллизии.

{
WagonPhysicsState Result = CurrentPhysicsState;
float MinTime = 0.0f; float MaxTime = TimeToSimulate;
for (int step = 0 ; step<MAX_STEPS ; ++step)
{
float TestedTime = (MinTime + MaxTime) * 0.5f;
WagonPhysicsState TestedPhysicsState = CurrentPhysicsState; TestedPhysicsState.BasicSimulationStep(TestedTime);
if (IsCollisionWithWagonAheadOrBehind(TestedPhysicsState))
{
MaxTime = TestedTime;
}
else { MinTime = TestedTime;
Result = TestedPhysicsState;
}
}

return Result;
}

Коллизии проверяются в трёхмерном пространстве при помощи m_WorldPosition и m_WorldRotation, полученных из WagonPhysicsState. Метод IsCollisionWithWagonAheadOrBehind позволяет проводить тест коллизий между двумя oriented bounding boxes (OBB).

Ответ коллизий

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

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

Затем определяется коэффициент реституции (r). Он показывает, насколько «упругим» будет ответ коллизии. Если r=0, то энергия теряется, если же r=1 — не происходит никаких потерь энергии. Таким образом формула расчета выглядит следующим образом.

А так меняется уравнение для того, чтобы можно было получить импульс j.

Само значение j рассчитывается следующим образом.

В Assassin’s Creed Syndicate значение r равно 0,35.

Система применяет импульс +j к локомотиву и –j к прицепам. Тем не менее, к прицепам применяются «отсроченные» импульсы. Так как интеграция для локомотива уже отработана, разработчики не хотели, чтобы менялась его текущая скорость, поэтому импульс «сдвигается» на следующий кадр симуляции. Это не создаёт каких-либо видимых визуальных изменений, так как разницу в один кадр тяжело заметить. «Отсроченные» импульсы собираются и применяются к вагону в части В на следующем кадре симуляции.

Сцепка вагонов

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

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

Здесь X — это дистанция пути, V — текущая скорость, а t — время, которое потребуется на шаг симуляции.

Будущая длина сцепной цепи (FutureChainLength) — это сумма текущей длины и разности между дистанциями, которую проедут за шаг симуляции локомотив и прицеп.

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

d = будущая длина цепи — максимальная длина цепи

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

Сами импульсы рассчитываются с помощью коэффициента С.

Сумма обоих С равна 1. Разработчики хотели, чтобы прицепы проезжали дополнительную дистанцию равную d умноженное на С, а локомотивы — -d умноженное на С. Для этого нужно умножить дистанции на массу, поделённую на время шага симуляции.

Таким образом, формула для расчета C выглядит так.

С её помощью можно легко вывести значения импульсов.

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

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

Коррекция на резких участках дороги

Из-за того, что симуляция происходит в одномерном пространстве, у авторов игры возникали проблемы с подгонкой сцепки на резких поворотах. Сами сцепки находятся в трёхмерном «мире», но импульсы передаются только в одномерном. Для коррекции положения сцепки пришлось модифицировать её максимальную длину в зависимости от относительного угла между направлением локомотива и прицепа. Чем больше угол, тем меньше максимально возможная длина цепи. Сперва вычисляется точка между двумя нормализованными векторами.

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

float DistanceConvertedFromCosAngle = 2.0f*clamp( (1.0f-s)-0.001f, 0.0f, 1.0f );
float DistanceSubtract = clamp( DistanceConvertedFromCosAngle, 0.0f, 0.9f );

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

Старт поезда

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

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

Разработчики расширили формулу.

Здесь mi — это масса вагона i (m первое — масса локомотива), VA — текущая скорость локомотива (допускается, что все вагоны имеют одну и ту же скорость). Вводится также дополнительный символ mx.

Формулу можно упростить.

Искомое VB рассчитывается следующим образом.

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

Трение

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

У отсоединённых вагонов сила трения меняется.

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

Здесь t — время, прошедшее после отсоединения вагонов (измеряется в секундах).

#assassinscreed

{ "author_name": "Андрей Верещагин", "author_type": "editor", "tags": ["assassinscreed"], "comments": 47, "likes": 93, "favorites": 61, "is_advertisement": false, "subsite_label": "gamedev", "id": 25513, "is_wide": false }
{ "id": 25513, "author_id": 22254, "diff_limit": 1000, "urls": {"diff":"\/comments\/25513\/get","add":"\/comments\/25513\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/25513"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 64954 }

47 комментариев 47 комм.

Популярные

По порядку

Написать комментарий...
92

В это время Бесезда:

Ответить
5

Всё гениальное - просто!

Ответить
7

Всё гениальное - просто!

Просто костыли!

Ответить
28

Кажется, DTF нужен нормальный синтакс хайлайтер

Ответить
1

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

Ответить
15

Как сделать так, что бы пользователи DTF остались довольны?
Очень просто. Замените "Ubisoft" на "Naughty Dog."

Ответить
6

Физика - наше всё

Ответить
–13

Это все очень интерестно , но есть 1 вопрос. Нахера?! Нахера такая сложная физика транспорта в Assasin creed-e? По моему время потраченное на проработку физики поезда , можно было потратить на проработку более качественного геймплея.

Ответить
25

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

Вот ты сейчас прочитал за поезд, а это даже не верхушка айсберга того труда, который вкладывается в подобные проекты:)

Ответить
2

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

Ответить
0

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

P.S. Вот сейчас такая история с новым Shadow of The Tomb Raider

Ответить
16

Вот меня просто убивают такие комментарии. Парень, довольно таки давно человечество додумалось до такой шьуки как разделение труда. Это значит что один человек делает одну определенную работу, а другой иную. Когда результат их труда соединяется воедино, получается система, по качеству превосходящая ту, которая была бы создана каждым из участников ее создания, по отдельности. Довольно таки не сложная концепция, как мне кажется. Ну так к чему это я... Ах, да. Как ни странно, физикой занимаются программисты, а геймпреем - геймдизайнеры. В очень маленьких студиях один человек может одновременно заниматься этими двумя видами работ, но это не случай такого гиганта как Ubisoft. Следовательно, говорить что лучше бы они занимались гейсплеем а не физикой, такой же идиотизм как говорить, что американцы не должны строить небоскребы, пока не добьются полной эмансипации женщин. В смысле, чего блядь? Где архитекторы и где SJW? Это разные миры. Настолько же разные как и миры геймдизайнеров и программистов.

Ответить
1

До главы менеджмент трудовых ресурсов ещё не дошли?

Ответить
15

Открою тебе маленький секрет: всё что тут написано сделано максимально кратко, просто и лаконично.

Ответить
0

Ну, за пару месяцев разработчики перенесли Юнити на новые текстурки, надо было чем-то заниматься остаток года.

Ответить
–11

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

Ответить
12

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

Ответить
–3

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

Ответить
1

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

Ответить
–3

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

Ответить
7

Так быть может они и добавили, а ты просто не заметил? Это раздел геймдев и посыл статьи скорее для разработчиков, которым интересно как именно реализован тот или иной момент в различных играх. Тут не сказано, что Юбисофт гордятся этим или что это их фишка. Статья ОТ разработчика и ДЛЯ разработчиков и интересующихся, почитать было интересно

Ответить
0

Но бюджет один

Ответить
5

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

Ответить
–3

Еще скажите что у юбисофт уровень "гениальности" , как у Кодзимы

Ответить
–1

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

Ответить
0

Это не путь юбисофта, вы что.

Ответить
1

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

Ответить
0

Там ещё водный транспорт офигенен.

Ответить
1

Чтобы использовать в будущих играх, например watch dogs 3 или cr3w

Ответить
0

А почему эту "технологию" не использовали в "смотри собака 2" и "среш 2"?

Ответить
0

В Смотри Собаки 2 нет едущих поездов, кроме трамваев, а в ТНЕ СГЕШ 2 поезда видно слишком редко, чтобы так с ними заморачиваться. А в Синдикате мало того, что поезда часто видны, так ещё и у ГГ личный поезд-штаб, который очень важен для геймплея. Просто Юбики решили не усложнять свои игры, где не надо

Ответить
0

Чтобы на консолях разрешение до 900р порезать ))

Ответить
0

Местная овощебаза тебя задизлайкала, зато лови мой царский лайк.

Ответить
5

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

Ответить
4

Когда эту статью переводили на гиктаймс там еще была такая картинка:

Ответить
1

я не понимаю как он это отцепляет просто ударяя кулаком сверху вниз

Ответить
3

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

Ответить
0

И из столкновений вспоминается только крушение в начале игры в кат-сцене

Ответить
0

Можно отцеплять вагоны)

Ответить
3

Пожалуй впервые захотелось поиграть в синдикат...

Ответить
1

Ждать the crew про поезда?
Было бы поездато

Ответить
1

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

Ответить
0

Как на уроке математики побывал.

Ответить
0

Вставки кода на DTF выглядят отвратительно.

Ответить
0

Только сверхразум способен понять

Ответить
0

Асасин крид блэк флаг - симулятор кораблей и пиратов
Асасин крид синдикат - симулятор поездов

Ответить
0

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fizc" } } }, { "id": 4, "label": "240х200_mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "flbq" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjog" } } }, { "id": 10, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-250597-0", "render_to": "inpage_VI-250597-0-1134314964", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=clmf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Плашка на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudo", "p2": "ftjf" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fzvc" } } } ]
В Steam появилась функция продажи
подержанных цифровых копий игр
Подписаться на push-уведомления