Как стать автором
Обновить
33
0
Алексей Дюдя @Etlay

Пользователь

Отправить сообщение

Перестал читать после фразы:

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

Автор явно ничего серьезного на Unity не разрабатывал...

Ну тут нет серебряной пули, процессы подбираются под команду а не наоборот :)

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

Все в целом сводится к фразе автора:

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

Во первых рефлексия это всегда оверхеад как по памяти (аллокации) так и по лишним операциям на цпу.

Современные DI под юнити, например Zenject https://github.com/modesttree/Zenject широко используют помимо рефлексии еще LINQ. Что тоже добавляет лишних аллокаций. Тут стоит заметить что garbage collector для игр - это в большинстве случаев плохо, а не хорошо. На одном из проектов где мы широко использовали Zenject - у нас доходило до 100 mb аллокаций на старте сцены, что бы построить дерево объектов.

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

Особенно если инъекция происходит не в конструкторе класса а в какие-то его поля.

В итоге создание с первого взгляда безобидного объекта в памяти могло породить очень большее количество аллокаций.

Но тут я наверное не совсем корректно выразился, DI как концепция ок. Проблема в контейнерах которые скрывают зависимости. Сейчас я стараюсь использовать концепцию Poor Man's DI в проектах - когда зависимости классов явно определены в конструкторах, а дерево объектов объявлено явно и собирается руками в контекстах.

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

Любопытная статья. Не могу согласиться с автором во всем. Сам уже больше 10 лет в геймдеве.

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

Да и если попробуете найти инвесторов и организовать свой проект/студию - статистика не на вашей стороне. Очень много проектов закрывается не окупив себя. Особенно последний год. Хотя редко - но может выстрелить очень громко (тот же Minecraft)

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

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

Gamedev-мир отстает от "большого IT" - вот тут я бы вообще поспорил. Много гейм.девных решений было бы неплохо затащить в "большой IT". Тот же Data oriented design. А то что тащат сейчас в гейм.дев из энтерпрайса - лучше бы не тащили. За те же модные DI в Unity нужно по рукам бить.

Маленький уютный мирок - вот тут полностью согласен. Компаний не так много, а геймдев в России, по понятным причинам, умер. Так что нужно быть готовым к релокации.

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

Вот тут - зависит от культуры в компании. На свои проектах в текущей компании вводил практики дизайн-ревью (https://youtu.be/4Y0XJXRZv6k https://youtu.be/IDj3x__YZgE)

и обсуждений задач в Slack/на созвонах. И есть культура и инструменты шаринга знаний между разными проектами...

Ну вот как-то совсем не коррелирует с моим опытом работы на удаленке. У нас в компании даже исследование небольшое проводили и статью на хабре писали https://habr.com/ru/companies/pixonic/articles/504796/

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

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

Ну у них есть десктопное приложение, могли бы при желании в нем сделать. У них изначально как раз в десктопном приложении можно было установить color space монитора, а в вебе только sRGB.

Используем figma для дизайна UI в геймдеве (Unity, Unreal), года два назад писали большой запрос в figma по поддержке Linear color space. Так как весь современный гейм.дев на нем сидит. Так и не сделали :(

Ну собственно в этом и был посыл, если СТО занимается тем что пишет код, вместо налаживания процессов то что-то тут не так.

Дополню автора:

В “нашем” представлении руку к коду игры придется прикладывать везде и часто.

Честно говоря, очень странное представление о работе СТО в таких компаниях. Сам в геймдеве уже больше 9 лет, и на практике, уже на уровне лида с командой из 4-5 программистов, на написание кода остается меньше 10% времени.

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

У нас есть инструмент по генерации сцен разного уровня качеств. Художники работают в Source сцене, которая так же является сценой максимального качества (содержит все типы пропсов). Художники или тех.артисты размечают к какому качеству относятся пропсы и на основе этих данных мы генерируем _Hd, _LD и _ULD сцены. Эти сцены в дальнейшем пакуются в бандлы. В зависимости от выбранного на девайсе качества мы грузим нужный бандл сцены.
Я бы еще рекомендовал видео с GDC о том как работает сетевой код в овервотч www.gdcvault.com/play/1024001/-Overwatch-Gameplay-Architecture-and
Мы вдохновлялись их идеями при разработке этого проекта
Тут довольно сложная ситуация.
Если исходить из того что нужно использовать один и тот же движок на клиенте и на сервере, то возникают следующие проблемы:
1) Если использовать PhysX Unity на клиенте, то мы не можем гарантировать что на сервере будет использоваться та же версия PhysX. Да Unity публикует данные о том какую версию PhysX они используют, но в той версии которую они поставляют с Unity вполне могут быть кастомные изменения для поддержки движка. Либо использовать Unity на сервере, но на мой взгляд это плохое решение.
2) Если не использовать PhysX, то появляется проблема портации C++ на мобильные платформы и интеграция его с Unity. Наш проект запускался одновременно под Android и iOS. Тратить несколько месяцев разработки на такую задачу, когда проект находится на стадии софт-лаунча, довольно рискованно.
К тому же, как я уже говорил, у нас довольно простая физика в игре, все что нам требовалось делать рейкасты и свипкасты.
Не понял ваш комментарий по поводу «вектора изменений».

Физика на клиенте нужна, для расчета предикшена локального игрока, в частности такие вещи как передвижение и ассист в системе прицеливания делаются на основе рейкастов. Более подробнее как работает сетевой код я писал в этой статье habr.com/ru/company/pixonic/blog/415959

Если же вы про то, зачем нам на клиенте хранить историю физики (UnityPhysicsHistorySlice). То в Production-билдах не зачем, вся история храниться в ECS. История физики нужна только для специального режима «локальной симуляции», когда клиент помимо собственно клиентского кода, еще эмулирует сервер. Это довольно сильно ускоряет прототипирование фичей, так как можно разрабатывать игровую логику, без редеплоя сервера.
аллокации в апдейт методах
//Обрабатываем клик левой кнопки мыши
       if (Input.GetMouseButtonDown(0))
       {
           //Берем точку по которой игрок нажал и отправляем всем компонентам уведомление
           var hit = GetMouseHit();
           Events.PublishAsync("poittogound", new PointOnGroundEventData { Sender = this, Point = hit.point });
       }


 protected override void OnUpdate()
   {
       //Передача состояния по позиции агента
       if (agent.remainingDistance > agent.stoppingDistance)
       {
           Events.Publish("agentmoved",
               new AgentMoveEventData { Sender = this, DesiredVelocity = agent.desiredVelocity });
       }
       else
       {
           Events.Publish("agentmoved",
               new AgentMoveEventData { Sender = this, DesiredVelocity = Vector3.zero });
       }
   }


Массивы
protected override Task[] OnUpdateAsync()
{
   //Передача состояния по позиции агента
   if (agent.remainingDistance > agent.stoppingDistance)
   {
       return new Task[] { Events.PublishAsync("agentmoved",
           new AgentMoveEventData { Sender = this, DesiredVelocity = agent.desiredVelocity }) };
   }
   else
   {
       return new Task[] { Events.PublishAsync("agentmoved",
           new AgentMoveEventData { Sender = this, DesiredVelocity = Vector3.zero }) };
   }
}


Также, то что уже сказали до меня: link

И это только для управления одним персонажем. А если в у нас будет в сцене 5-10-20 объектов построенных на этой системе?

Возможно для десктоп платформ это еще может работать в небольших инди проектах, то для мобильных проектов это недопустимо. На проектах которых мне довелось работать, количество аллокаций в кадр было в районе 500 — 600 байт. При больших значениях у вас GC будет вызваться и отрабатывать настолько часто и долго, что добиться стабильных 30 FPS не удасться
Если хотите сделать что-то сложнее одного бегающего человечка, не нужно делать так. С таким количеством аллокаций производительность убита на корню.
1) Нет, все компоненты привязаны к какой-то entity. Я упустил в статье момент создания SpawnAvatarRequest. Он у нас происходит не внутри систем ECS, а при первоночальном создании GameState:
var avatarRequestEntity = gs.WorldState.CreateEntity();
avatarRequestEntity.AddSpawnAvatarRequest(1); // создаем игроков на первом тике мира

2) Да такой механизм нужен и используется. Это по-сути одна из «фичей» ECS. Когда можно динамически добавить новое поведение к уже существующей сущности. На примере нашего шутера игроку в рантайме могут добавляться компоненты: неуязвимости, невидимости, ускорения движения и т.д. Пример кода из системы расчета невидимости:
if (ghost.IsInHidingZone) // если игрок в зоне "невидимости"
{  
     playerEntity.DelAimable(); // удаляем компонент, который отвечает за то что в игрока можно целится
     playerEntity.AddInvisible(); // добавляем компонент невидимости
}

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

4) Верно у нас в интерфейсе entity для каждого компонента существуют методы Add*MyComponent*() и Del*MyComponent(). Однако мы используем кодогенерацию в нашей ECS, и вам как программисту нужно лишь написать свой компонент, и указать что он используется в Enitity. Вот пример как выглядит класс Entity в проекте кодогенерации:
public class Entity
{
       public Transform Transform;
       public Movement Movement;
       public CharacterRotation CharacterRotation;

       [DontPack]
       public AmmoToAdd AmmoToAdd;
       
       [DontPack]
       public ShotDamage ShotDamage;
}

Кодогенератор добавить в проект все необходимые методы по созданию, удалению, итерированию и сериализации этого компонента.
В нашей системе тик произойдет в любом случае. Спавн персонажей выполняется только на сервере, а сервер тики не пропускает. Данный пример был взят из реального проекта где используется отложенный по времени спавн персонажа. В целом все таймеры в игре реализованы таким же способом. Записывается время тика на котором должно произойти событие. И условие которое проверят текущее время системы.
Тот же самый метод мы используем в примере HealthPowerUpMovementSystem:
if(pair.Value.NextChangeDirection <= gs.Time)
{
   pair.Value.NextChangeDirection = (uint) (healthPowerUpStats.SecondsToChangeDirection * GameState.Hz);
 movement.Velocity *= -1;
}
1

Информация

В рейтинге
Не участвует
Откуда
Москва, Москва и Московская обл., Россия
Дата рождения
Зарегистрирован
Активность