en
Feedback
Unity: Всё, что вы не знали о разработке

Unity: Всё, что вы не знали о разработке

Open in Telegram

Авторский канал о разработке в Unity от Alex Silaev (CTO в Zillion Whales). Mushroom Wars 2 моих рук дело. Рассказываю об интересный кейсах, делюсь лайфхаками, решениями.

Show more
1 774
Subscribers
No data24 hours
No data7 days
-730 days
Posts Archive
Time.deltaTime vs Time.fixedDeltaTime Я уже делал пост про FixedUpdate (https://t.me/unsafecsharp/103), но не писал про разницу между fixedDeltaTime и deltaTime. В методе Update deltaTime будет равен времени, которое прошло с прошлого кадра, при этом fixedDeltaTime будет равен фиксированной величине. А вот в FixedUpdate fixedDeltaTime останется, а вот deltaTime будет равен fixedDeltaTime. Другими словами, можно использовать deltaTime внутри FixedUpdate. #unityloop #deltatime

StaticBatchingUtility.Combine Позволяет комбайнить меши, которые находятся на GameObject. Довольно удобно, чтобы не ползать по иерархии и не собирать их там. Есть способ комбинировать меши и без этого: mesh.CombineMeshes Но придется передавать туда уже структуры, в которых описываются матрицы и мешки. Мы такое часто использовали для 3D, когда мы создавали одну мешку и вместо 1к объектов получали всего один. Есстественно, нужно понимать, что батчить нужно по-материально, т.е. какой-то код с этой логикой нужно будет все же написать. #batching #static #code #api

Texture2D.PackTextures Мы часто используем этот метод для динамического создания атласа, когда, например, мы загружаем аватарки игроков, а какие они будут мы заранее не знаем. Или мы формируем атлас для боя, когда игроки могут выбрать какие-нибудь скины юнитов, а нам все еще нужен 1 draw call ;) #atlas #runtime #code #api

Lerp vs LerpUnclamped Давайте сначала разберемся что такое Lerp. Это линейная функция, которая задается двумя точками B и C (
Lerp vs LerpUnclamped Давайте сначала разберемся что такое Lerp. Это линейная функция, которая задается двумя точками B и C (см изображение), а третье значение t - это как значение между 0 и 1, где 0 - это B, а 1 - это C. Таким образом получается, что перемещение по линии - это и есть lerp или интерполяция. А вот LerpUnclamped - это экстраполяция, т.е. когда мы не обрезаем значение t до 0..1.
b + (c - b) * clamp01(t);
Т.е. по сути мы сначала сдвигаем сетку координат в ноль (c - b), а потом умножаем получившийся вектор на t, а после сдвигаем получившийся вектор назад. С Unclamped мы просто не делаем операцию clamp, тем самым можем позволить результату выходить за границы b-c. #basics #lerp

Вспомнил забавную технику, которую я использовал когда мы делали Mushroom Wars 2 еще для Steam (а это была наша первая таргет платформа, если что). Так вот, мы изначально решили делать 2д игру, а не 3д, хотя и опыт позволял, да и можно было хайполи позволить для стима. Но мы решили, что не стоит недооценивать время и любое "красивое 3д" через несколько лет станет "всратым говном". Поэтому мы сделали ставку на 2д. А теперь к делу. Мы рисовали домики и юнитов на карте в 2д и pixel perfect. Но мы хотели еще real-time тени туда впихнуть. И вот тогда придумали забавное решение: мы запилили low-poly мешки домов по краям с бОльшей геометрией, внутрь мешки вставили 2д спрайт, а саму мешку не рендерили, т.е. рендерили и блендили только тени на мешку и от мешки. В итоге получился pixel perfect рендер с realtime тенями. Потом мы отказались от этой штуки в пользу спрайтов и render texture, т.к. нам нужно было на чайниках выходить, но история осталась 🙂 #rendering #shadows #story

Упоротая оптимизация. История произошла совсем недавно: мне нужно было понять почему у нас крашился слабый девайс на загрузке одной конкретной игровой карты, при этом на других он вел себя хорошо. У меня сразу подозрение пало на память, т.к. поведение характерно именно для такого кейса. Проверил логи - действительно память закончилась. А теперь самое инетерсное, начал разбираться и оказалось, что наш картоделатель посадил по 5к юнитов в домики "за картой", чтобы потом оттуда пускать их по триггеру. А рендер работает таким образом, что ему нужно знать верхнее ограничение по отрисовке количества юнитов. Для нормальной игры это вычислялось по формуле: "берем сумму всех юнитов во всех домиках на карте и умножаем на количество игроков (т.к. в теории каждый игрок может применить какой-нибудь скилл, который сдублирует юнитов, например)". Но если вдруг мы что-то не учли - мы просто не рисуем юнитов и все, что в принципе ок, т.к. когда у тебя 1к юнитов на экране - ты уже не понимаешь где кто, а когда 4к - так и подавно. Так вот этому коду уже 7 лет и понятное дело, что его никто не трогал все это время, но когда в 2015 году мы писали этот код - юнити мало чего умела нормально рисовать в больших количествах, и мы оптимизировали и микрооптимизировали все что видели. Так вот мы жили с такой игровой картой уже месяца 3 и только сейчас заметили проблему на девайсе с памятью, хотя у нас вместо того, чтобы рисовать и обрабатывать 4к юнитов максимум (+рендер на 20к), мы вдруг начали обрабатывать 220к и никто этого не заметил... И, наверное, если бы не этот девайс - так бы и пошли в релиз 🙂 А вы все "преждевременная оптимизация, преждевременная оптимизация..." 😂 #story #bug

Если вы не используете параметр out, по которому возвращается значение, то можете писать просто _: Method(out int notUsed); Method(out _); #basics

Умножение vs Деление Надеюсь, что вы все в курсе, что операция деления выполняется намного медленнее операции умножения. Если нет, то имейте ввиду. Я часто встречаю вот такой код:
a += b / 2f;
И мне все время хочется такой код написать так:
a += b * 0.5f;
Еще можно заменять по тому же принципу и любые другие константы. Но что делать, если у нас деление не на константу, а на x? Да все просто, делаем y = 1f / x и используем уже y. #basics #performance #code

Запись с последней лекции про основы современной компьютерной графики: https://youtu.be/EiucC4M0VuY #record #event
Запись с последней лекции про основы современной компьютерной графики: https://youtu.be/EiucC4M0VuY #record #event

Overdraw опаснее draw call. Дело касается в основном мобилок, но вообще принцип тот же везде. Overdraw - это по сути метрика, с помощью которой можно понять сколько раз вы закрашиваете один и тот же пиксель. Во-первых, красить пиксель дважды - плохая примета. Во-вторых, видяхи на мобилах довольно слабые и каждый новый проход по одному месту может сильно просаживать производительность. На практике легко получить такую проблему партикл системой с тысячами частиц. Может казаться, что производительность падает из-за количества частиц, но на самом деле проблема на 99% в overdraw. Кстати, для этого в настройках рендера партикл системы есть параметр, который ограничивает максимальный размер частицы относительно размера экрана, т.к. чем больше площадь - тем хуже. Поэтому 100 больших частиц у камеры хуже, чем 10к, но далеко. #rendering #overdraw

Лекция закончилась :( Надеюсь, что было интересно)) По-традиции, запись будет позже. Пишите свои комменты по этой лекции и по
Лекция закончилась :( Надеюсь, что было интересно)) По-традиции, запись будет позже. Пишите свои комменты по этой лекции и по темам, которые вы бы хотели обсудить. #event

Напоминаю, что сегодня пройдет лекция про основы современной трехмерной графики: https://t.me/unsafecsharp/141 Регайтесь :) #
Напоминаю, что сегодня пройдет лекция про основы современной трехмерной графики: https://t.me/unsafecsharp/141 Регайтесь :) #event

Мы часто используем для редактора поля вида
UnityEngine.Object dir;
для того, чтобы в инспекторе перетащить туда папку. Это очень удобно, чтобы не писать константы в коде. #editor

В C++ нет дженериков. Их нет, да, но как тогда работают дженерики, которые мы делаем в C#? Там то они есть;) а мы используем il2cpp почти всегда для продакшена. В итоге под каждый тип, который мы вызываем, будет создан метод. Это стоит учитывать, когда вы создаете дженерик метод и делаете миллион разных вариантов вызовов, то ожидайте код в размере миллиона копипастных методов;) #cpp #generics #il2cpp

Вот такую задачку мы задаем на собесе, чтобы понять понимает ли человек основы и вообще работал ли он со структурами (или только с классами): void Method(IInterface obj) {} public struct S : IInterface {} public struct A : IInterface {} void Something() { Method(new S()); Method(new A()); } interface IInterface {…} Но вопрос мы ставим так: «какие проблемы вы видите с этим кодом?». А дальше внимательно слушаем, если ответ типа «ну тут нужно все вообще переписать», то так и будет в вашем прод проекте, все перепишут, а в итоге результата будет ноль;) #interview #code

Всем привет! В эту субботу (в 16:00 по мск) пройдет лекция про основы современной трехмерной графики от Андрея Короткова (@me
Всем привет! В эту субботу (в 16:00 по мск) пройдет лекция про основы современной трехмерной графики от Андрея Короткова (@megadrone86), который имеет 10 лет опыта C++ разработки и 13+ лет опыта в геймдеве. До того как Unity стал мейнстримом разрабатывал собственный игровой движок: http://dgle.dronprogs.org Персональный сайт: https://dronprogs.org Ссылка на регистрацию: https://unsafecsharp.timepad.ru/event/2491854/ #event

Запись с лекции на тему вёрстки UGUI: https://youtu.be/6evvmkMwpso Все, кто хотят меня поддержать, могут это сделать тут: Евр
Запись с лекции на тему вёрстки UGUI: https://youtu.be/6evvmkMwpso Все, кто хотят меня поддержать, могут это сделать тут: Евро: NL19 BUNQ 2070 8912 32 Доллары: GB45 TCCL 0414 0434 4305 90 #event #record

Быстрый указатель в алгоритмах. Допустим, что вам нужно найти середину связного списка, как это быстрее всего сделать? Завести 2 указателя, один будет шагать через один элемент, а второй - по каждому элементу. Таким образом когда первый указатель доберется до конца такого списка - второй будет указывать на середину. Так вот первый указатель называют "быстрым". Недавно встретил такую задачку и хотел бы с вами поделиться: На вход нашему методу передается некий граф, который содержит ноды вида
Node {
  int value;
  Node next;
}
Нам нужно найти ноду в этом графе, которая начинает бесконечный цикл. Например:
1 -> 2 -> 3 -> 4 -> 2
Тут нода со значением 2 начинает цикл. Если же ноды нет, то метод должен вернуть null. Решение этой задачи довольно простое, если бы не одно условие: нужно найти решение без использования дополнительной памяти. Насколько я помню, я встретил ее по теме "какие задачи не нужно задавать на собесах", т.к. если решения ты не знаешь - решить ее практически невозможно, но все равно - подумайте, ну а вдруг 🙂 p.s: задача была задана на собесе в компанию Apple. #algorithms #interview

Лекция закончилась, запись будет позже. Извиняюсь, если не рассказал о каких-то моментах на лекции по ugui, тема достаточно обширная и у нас ушло почти 3 часа на эту тему, хотя даже не закрыли и половину;( Надеюсь, что все равно было интересно, вы для себя нашли что-то интересное и я ответил на ваши вопросы. Все, кто хотят меня поддержать, могут это сделать тут: Евро: NL19 BUNQ 2070 8912 32 Доллары: GB45 TCCL 0414 0434 4305 90 #event