Gamedev Maltsev Denis
3 791

Баланс C++ и Blueprints в UE4

Все в кучке: С++, Blueprints и результат http://www.rionix.com
В закладки
Аудио

В продолжение моего прошлого поста:

В этот раз поговорим про С++ и Blueprints и о том, как они могут повлиять на память и производительность игры. И, как обычно, категорически приветствуется обсуждение статьи в комментариях! Делимся своим опытом и становимся лучше 😁

C++ vs Blueprints

Можно ли написать игру используя только Blueprints? Можно. Можно ли написать игру используя только C++? Можно. А что тогда выбрать? А выбирать не надо, надо использовать оба языка. Главное - правильно соблюсти баланс.

Можно писать на чем-нибудь другом? Конечно: Python, C #, SkookumScript, Lua и др. Вот только, за исключением питона - это неофициальные плагины. Да и на питоне можно писать код только для редактора, но не самой игры. Это плохо? С точки зрения эпиков - С++ и блюпринты покрывают все потребности разработчиков. Я с ними согласен. Вы - решайте сами.

С++ - сложнааааа!!! В общем случае - да 😃 Если говорить об UE4, то не сложнее C # в Unity. Если не лезть глубоко внутрь исходников движка, то ужасы С++ обойдут стороной. Основная сложность любого движка заключается в его структуре, в понимании взаимодействия его модулей. UE4 не исключение.

Особенности работы движка

  • Если что-то подгружаем в С++ (ConstructorHelpers), то это будет загружено при старте модуля. Грубо говоря, в момент старта игры. Неважно, используется это или нет, создан этот объект или нет. И висеть все будет в оперативке или видеопамяти на протяжении всей работы модуля.
  • Зависимости (references). Например, меш зависит от материала(ов), а материал зависит от текстур. Когда мы грузим меш, подтягиваются и все его зависимости. Ням-ням-ням и кирпичик из 8-ми вершин, тянет за собой пару-тройку текстур в 4К.
  • Чтобы вызвать маленькую функцию из какого-нибудь блюпринта, движок загружает его весь. Благодаря зависимостям, это может потянуть за собой половину проекта, включая текстуры и звуки.
  • Блюпринты сильно медленнее С++, даже нативизированные. Часто звучат такие цифры: от 7 до 30 раз.

C++ и Blueprints

Даже если вы решили писать игру только на Blueprints, создавайте именно С++ проект. В скором времени вы убедитесь, что многие вещи гораздо проще решить с использованием С++. А переконвертировать Blueprint проект в С++ не так и просто.

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

Одно условие - не надо в конструкторе загружать ассеты. Откуда взялось такое условие? Дело в том, что для каждого AActor автоматически создается его default версия. И, естественно, для нее вызывается конструктор.

Эм... а блюпринты куда? А блюпринты грузятся только тогда, когда они нужны. И в этом их прелесть - ассеты подтянутся, только тогда, когда блюпринт начали использовать (в другом блюпринте или поместили на уровень). Поэтому, часто разбивают код так: всю логику работы выносят в базовый класс на С++, а настройки, меши, материалы и эффекты - в блюпринты-наследники. Даже если этот блюпринт-наследник будет единственным.

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

Блюпринты великолепны для прототипирования. Вот только с усложнением кода, становится все сложнее разбираться в получившейся "лапше". Как только идея устаканилась, старайтесь перенести ее на С++. Анализ кода, поиск и читаемость - не лучшие стороны блюпринтов:

Надо сводить к минимуму зависимость блюпринтов друг от друга. Зависимость Controller и Character обоснована - они не могут работать друг без друга. Но, ситуация, когда А тянет за собой В, который тянет C и D и все дальше и глубже, приведет к печальным последствиям. Причем простейший "Cast to" уже введет зависимость и начнет грузить другой блюпринт! Интерфейсы или взаимодействие на уровне С++ помогут разрулить ситуацию.

Циклические зависимости могут привести проект в полностью нерабочее состояние. У меня такое тоже было... Пришлось переписывать весь код игры.

Я написал блюпринт и хочу использовать его переменную или вызвать его функцию в С++. Это возможно? Если коротко, то: Да. Однако, по словам эпиков: "Если бы вы видели этот код, то никогда не захотели бы его использовать". Так что правильный ответ: Нет. Хотите что-то использовать в С++ из блюпринтов - объявите это в С++.

Производительность блюпринтов. Ее достаточно для игровой логики. Что-то сложнее, лучше выносить в С++. Крайне не рекомендуется делать блюпринты с Tick'ом. "Тикать" лучше на С++. С другой стороны, нет ничего страшного, если блюпринт изредка включает свой тик на короткое время, или тикает не каждый кадр, а пару раз в секунду. Также стоит помнить, что Tick - это просто таймер по умолчанию. Вынос "тикающего" кода в другие таймеры или Timeline не исправит ситуацию.

Все блюпринты, унаследованные от AActor, создаются по умолчанию с включенным Tick. Это можно поправить настройками проекта (Can Blueprints Tick by Default). Но лучше всего перепроверять вручную соответствующие галочки. Ну и консольная команда "dumpticks" в помощь.

Что еще посмотреть или почитать?

Я не рассказал про hard references и soft references. Исправляюсь тут:

Отличный пример того, как правильно сбалансировать С++ и Blueprints, показан в демо-проекте Action RPG:

И советую посмотреть эти видео:

Blueprints In-depth - Part 1 | Unreal Fest Europe 2019
Blueprints In-depth - Part 2 | Unreal Fest Europe 2019

PS: Еще, я не рассказал про блюпринты и совместную работу. Но это тема для отдельного разговора...

#ue4 #gamedev #development

Материал опубликован пользователем. Нажмите кнопку «Написать», чтобы поделиться мнением или рассказать о своём проекте.

Написать
{ "author_name": "Maltsev Denis", "author_type": "self", "tags": ["ue4","gamedev","development"], "comments": 76, "likes": 81, "favorites": 194, "is_advertisement": false, "subsite_label": "gamedev", "id": 57618, "is_wide": false, "is_ugc": true, "date": "Wed, 10 Jul 2019 14:27:46 +0300" }
Подкаст «Жиза ГД»:
Можно ли «продать»
плохую игру?
Слушать фоном🎧
{ "id": 57618, "author_id": 92857, "diff_limit": 1000, "urls": {"diff":"\/comments\/57618\/get","add":"\/comments\/57618\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/57618"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 64954, "last_count_and_date": null }
76 комментариев

Популярные

По порядку

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

Автор продолжай ) не хватает как раз базовых статей на эту тему

Ответить
4

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

Ответить
0

А если у меня изначально делался проект на блупринтах... смогу ли я сделать новый проект на с++ и всё туда перенести без проблем?

Ответить
2

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

Ответить
0

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

Ответить
1

Изучай готовые проекты эпиков, там все хорошо в этом плане. К примеру шутер гейм

Ответить
0

интересно не просто код/проект посмотреть, сколько понять/услышать почему так. И как раз интересно не шутаны глянуть как делают на анриле

Ответить
3

С++ в анриле не настолько сложный, насколько геморный. Апиха богатая, располагает к правильной архитектуре приложения, но все эти кучи макросов перед каждым методом, которые еще и не автокомплитятся в ide, просто выводит из себя))
Поэтому стараюсь делать так, как и пишет автор – сначала писать логику на bp (в тех случаях, когда это возможно), и лишь когда она ±фиксируется, переписывать тяжелые ее куски на плюсы.

А статья хорошая, ссылки полезные, спасибо :)

Ответить
2

Что имеешь в виду под "не автокомпилятся в ide"?

Ответить
3

А вообще надо понимать, что от этих макросов в конечном счёте только плюсы сплошные, уж лучше с макросами, зато с рефлексией, чем без них :)

Ответить
0

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

Ответить
3

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

Ответить
0

Библиотеки функций - прямой путь к циклическим зависимостям и референсам на полпроекта. Удачки 😁
Тики - смотрите реализацию на С++. Обычный таймер.
Очень часто реальный код в тексте читабельнее, понимательнее и искательнее на порядки лучше макарон. 😉

Ответить
0

Библиотеки функций - прямой путь к циклическим зависимостям и референсам на полпроекта.

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

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

Ответить
2

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

Ответить
2

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

Ответить
4

Иногда код на блюпринтах получается чище, понятнее и элегантнее, чем на С++.

Ответить
1

Я могу и в блупринты, и в С++
Всё равно в некоторых местах просто удобнее использовать блупринты

Ответить
1

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

Ответить
0

Ну это то понятно. Просто в них намного легче разбираться как по мне. Да и гайдов куча этой направленности. А на С++ так с нуля то писать игры и не начнешь.

Ответить
1

Как по мне, они в основном рассчитаны на использование в качестве финального дочернего класса, как в статье написано, в котором задаются всякие там ссылки на ассеты, настраиваются параметры и т.п.

Ответить
0

Хмм, вообще, как я понял из кучи статей, БП - для геймдизайнеров, а не разработчиков (условно). Чтоб было удобно жонглировать параметрами, ассетами и т.п.

Ответить
2

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

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

Ответить
2

Так и не понял как писать в статье C# слитно 😊

Ответить
0

Можно скопировать откуда-нибудь и вставить в редактор

Ответить
0

Пробовал. После сохранения он все равно разделяет на тэг и букву. Баг редактора наверное...

Ответить
2

Хороший материал, надеюсь такого будет побольше.

Ответить
1

Насколько сложно трансфернуться из Юньки на УЕ? Опыта 5 лет.

Ответить
3

выдели месяц, должно хватить на основы

Ответить
0

А зачем?

Ответить
0

интересно же

Ответить
0

что лучше юнити или унриал

Ответить
4

Что лучше, пепси или кола?

Ответить
2

пепси

Ответить
4

Dr Pepper.

Ответить
0

не очень сладкая кола с ароматизатором вишневого beermix'а и на половину с вишневой жевачкой эклипс
так се кароч

Ответить
0

Это ты еще вишневый Dr Pepper не пробовал. А если охладить его до околонулевой температуры, то вообще заебись будет.

Ответить
0

вообще то и то и то пробовал
так се водичка
если бы в украине на неё были цены как на пепси то может бы и пил иногда

Ответить
0

Есть парадокс пепси. Из белых стаканчиков людям больше нравится пепси, а из брендированных — кола.

Ответить
1

Смотря для чего лучше. В общем случае на этот вопрос невозможно ответить)

Ответить
1

Даже если вы решили писать игру только на Blueprints, создавайте именно С++ проект.

БП проект конвертится в ЦПП проект созданием любого цпп класса.

Ответить
1

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

Ответить
0

Перепарентить бп на цпп класс несложно - 1 клик. Вариаблы из бп в цпп переносить геморно, но об этом, как и вообще о многом, у автора ни слова.

Ответить
1

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

Ответить
0

Я могу сильно ошибаться. Извиняйте. 🤔
Последний раз когда сталкивался с подобным, было в версии 4.10 или даже раньше. И мне опыт очень не понравился.

Ответить
1

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

Ответить
1

У меня небыло опыта работы на C# в UE4. Я советую посмотреть именно на С++, от него, считай, только синтаксис остался. Не должно быть больших проблем с переходом.
Но, если С++ совсем не нравится, то ИМХО лучше остаться на Unity. Для него это родной язык и значит меньше проблем будет.

Ответить
0

"не автокоМПЛИтятся" – практически все время их приходится вспоминать самому и подглядывать в доки :)

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

Ответить
3

Юзай ассист или решарпер, если ты про всплывающие подсказки. Хотя на 19 студии вроде и intellisense справляется нормально, но это не точно, не проверял.

Ответить
0

Я проверял на 2019 и мне результат нравится. Чуть хуже ассиста, но уже не критично.

Ответить
0

эх, не туда коммент упал

Ответить
1

Также стоит помнить, что Tick - это просто таймер по умолчанию.

Не стоит вводить людей в заблуждение. Таймер работает совсем иначе, у них есть timer manager который содержит массив этих таймеров и тригерятся таймеры только когда подошло их время. Если не ставить таймер меньше 0,016 секунд, то таймер будет дешевле, ибо когда актор тикающий он обрабатывается особым образом и общий тик не произойдет пока все тикающие акторы не тикнут(даже если в тике ничего не вызывается).

Ответить
0

Ссылки, пруфы, пояснения? Лично я ничего не понял.

Ответить
0

https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Source/Runtime/Engine/Private/TimerManager.cpp

Какие тут еще могут быть пояснения? Тик блупринта дороже таймера (только если интервал больше текущей частоты обновления 0,016 для 60 fps). И я вполне объяснил причину этого. Приравнивать тик к таймеру не корректно. Так же не корректно приравнивать таймеры к таймлайну, реализация абсолютно иная и производительность тоже.

Ответить
0

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

Ответить
0

Уважаемый, у меня не так много свободного времени чтоб читать вам лекции. Действительно ли вы знаете реализацию тика? Понимаете ли почему BP tick дороже C++ tick?
Сотня таймеров куда дешевле, чем сотня тиков.
Во-первых Timer отрабатывает не на каждый тик, тикает только менеджер, который тригерит таймеры время которых подошло.
Во-вторых выполнить 100 операций в одном объекте дешевле чем выполнить 1 операцию в 100 объектах, а если учесть оверхед от VM то выходит куда дешевле.
В-третьих из первого вытекает то, что тик становится менее нагружен. Во время тика необходимо пройтись по всему пуллу FTickableGameObject'ов, пройтись по всем прибинженным функциям, проверить состояние функции и сформировать стек вызова. Все это накладывает большой оверхед.

Я уже не говорю о таймлайнах. Таймлайны работают совсем иным образом нежели timer. Это actorComponent, а следовательно каждый таймлайн это еще один vm объект, который еще и тикает каждый тик.

Ответить
0

Т.е. таймер с рейтом ноль ничем не отличается от Тика, о чем я и говорил. Как и тики акторов ничем не отличаются от тиков компонентов. Более того, оверхед на блюпринтах будет гораздо больше любого нативного оверхеда. В чем претензии то?

Ответить
0

Т.е. таймер с рейтом ноль ничем не отличается от Тика, о чем я и говорил.

Тик блупринта дороже таймера (только если интервал больше текущей частоты обновления 0,016 для 60 fps)

Таймер с рейтом ноль не надо ставить никогда. Это будет даже дороже тика. Но большинство вещей не требует обновлений раз в 60 секунд на самом деле.

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

О чем вы? Я разве утверждал что native оверхед больше bp?

Ответить
0

Таймер с рейтом ноль даже не запустится, естественно я говорил про близкие к нулю значения (1/60 или 1/120). В комментарии было сказано, что я в статье "ввожу людей в заблуждение". Вот я и хочу узнать в чем.

Ответить
0

Также стоит помнить, что Tick - это просто таймер по умолчанию.

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

Ответить
0

Тик может работать НЕ со скоростью частоты кадров. И я об этом написал в статье. Так в чем я не прав?

Ответить
0

То что вы указали Tick Interval ниже, не избавляет вас от оверхеда тикающего объекта. Редкий тик дороже редкого таймера опять же, за счет timer manager'а и меньшего оверхеда на vm объектах.

Ответить
0

А вот это уже более-мнее осмысленный разговор. Замеры то есть? Как я и просил в начале: ссылки, пруфы, пояснения?

А то получается странно... Если интервал у таймера ставим маленький, то:

Таймер с рейтом ноль не надо ставить никогда. Это будет даже дороже тика. Но большинство вещей не требует обновлений раз в 60 секунд на самом деле.

А если ставим интервал большим, то:

Редкий тик дороже редкого таймера опять же, за счет timer manager'а и меньшего оверхеда на vm объектах.

Я и правда с удовольствием почитал бы на эту тему статьи.

Ответить
0

Так я пояснил причины этого. Все из-за реализации. Все таймеры обрабатываются в одном месте в один тик. В то время как тик объектов происходит на уровне vm, где приходится делать огромное количество операций прежде чем тикнуть(проход по всем биндам всех тикающих объектов, их проверка + составление call stack). Если вы ставите галочку tick на акторе, то он автоматически добавляет оверхед, ибо добавляется в пулл тикающих объектов и его обработка усложняется, даже если вы ничего не делаете в тике, оверхед будет присутствовать.

Если будет время, я соберу тестовые проект и продемонстрирую вам разницу.

Ответить
0

Будет супер, если все это выльется в статью :) Думаю, многим интересна данная тема.

Ответить
1

Собрал элементарный проект. На сцену спавнятся 100 акторов, которые раз в 0.5 секунд сдвигаются по x координате. Реализовал 2 актора, один двигается на тике с указанным интервалом, другой на таймере с тем же интервалом в 0.5

Вот результаты профайлера. Могу скинуть *.ue4stats профайлера, на статью это не тянет.

Ответить
0

Интересные результаты, не ожидал. Надо будет погонять тесты...

Ответить
0

Читаю всё это и тихо радуюсь тому, что в студенческие годы заинтересовался Unity...

Ответить
1

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

Ответить
1

Лучший. Продолжай пожалуйста

Ответить
1

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

Ответить
0

Я конечно вовремя но с 4.22 добавили live++, очень сильно экономит время

Ответить
0

Прямой эфир

{ "remaining": "WzAsMSwyLDMsNCw1LDYsNyw4LDld" } [ { "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" } } } ]
Гейб Ньюэлл наконец-то анонсировал то,
чего все так долго ждали
Подписаться на push-уведомления