Unity: Всё, что вы не знали о разработке – Telegram
Unity: Всё, что вы не знали о разработке
1.74K subscribers
40 photos
101 links
Авторский канал о разработке в Unity от Alex Silaev (CTO в Zillion Whales). Mushroom Wars 2 моих рук дело.
Рассказываю об интересный кейсах, делюсь лайфхаками, решениями.
Download Telegram
Texture2D.GenerateAtlas

Я уже писал про PackTextures, но эта штука ломается, если невозможно запаковать текстуры, т.к. их размер превышает максимальный размер атласа. Для этого можно использовать GenerateAtlas, т.к. этот метод ничего не делает с текстурами, а только работает с ректами и возвращает true, если все объекты поместятся в атлас. То есть можно сначала вызвать его, а потом использовать PackTextures, либо запаковать самостоятельно, используя SetPixels.

#api #code #unity
🔥15👍3
Instantiate<T>()

Я часто встречаю в коде вот такой вариант:

var instance = Instantiate(prefabGo);
instance.GetComponent<MyComponent>();

Дело в том, что можно использовать такой вариант:

var instance = Instantiate(prefabMyComponent);

Который сразу вернет нам ссылку на инстанс нашего объекта.

#lifehack #basics #instantiate #code
🥱67👍18🔥6🥴5🌚1💯1
Год назад мы решили записать нашу главную тему игры в виде простого клипа:
https://www.youtube.com/watch?v=RtR4E0z5Sys
При этом музыканты - это все люди из нашей компании.

А вы делали что-нибудь подобное в своих студиях или проектах?
Делитесь ссылками в комментах!

#clip #video #soundtrack #mushroomwars2
🔥272🥰1
Для каких платформ вы делаете проект?
Anonymous Poll
41%
PC/Steam
54%
iOS
77%
Android
5%
Switch
6%
PS
6%
XBOX
16%
Другие
👍4🥰2🤨2🍓2
Может показаться, что я забыл (или забил) делать посты для канала, но на самом деле - нет, у нас просто релиз в этом месяце, а вы знаете как это запарно, ведь нужно успеть сделать 90% проекта за последние 2 недели.
На самом деле выходит очень даже неплохо и я обязательно поделюсь результатом, когда проект выйдет в релиз (будет софт).

#release #newproject #news
👍51🔥10🫡52👾2
Texture2DArray

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

На практике же всплыло 2 проблемы:
1. Слои должны быть одного размера, т.е. нельзя сделать один слой 2к, а другой - 1к;
2. Эта штука не работает на андроиде.

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

#shaders #texture2darray #android
🔥8👍5🤯2
Есть такой мемчик, где люди делятся на 2 типа и вот это все. Ну там где у человека выключено отображение ворнингов и там где включено.
Я отношусь к тем, у кого они включены и вообще не люблю когда в логах мусор.

Так вот, в шарпе есть такая штука:

#pragma warning disable
#pragma warning restore


Она позволяет включать и выключать либо все ворнинги, либо конкретные, если указано какие конкретно нужно отключить.
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/preprocessor-directives#pragma-warning

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

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

#logs #pragma #warnings
🔥15👍11😐3🤡1
В редакторе можно писать простые выражения в поля для чисел:
Например, чтобы сдвинуть объект, который находится в x=10, на 100 юнитов вправо, нужно написать 10+100 и нажать enter.

#editor #basics #formula
👍25🤡6🤯5🥴5🍓3
Array.Empty

Используйте вместо new T[0]; статичный массив, который не нужно создавать каждый раз.

#lifehack #optimization #basics
👍24😐8🔥3🗿2
ВОПРОСЫ

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

#questions
🔥7
memcmp

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

#unsafe #memcmp
🔥14🤯5
Разбираемся с профайлером

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

WaitForTargetFPS: Время, потраченное на ожидание целевого значения FPS, указанного в Application.targetFrameRate. Редактор не использует VSync на GPU, а вместо этого использует WaitForTargetFPS для имитации задержки VSync.

Gfx.ProcessCommands: Поток рендеринга охватывает всю обработку команд рендеринга. Часть этого времени может быть потрачена на ожидание VSync или новых команд из основного потока, что можно увидеть в Gfx.WaitForPresent.

Gfx.WaitForCommands: Поток рендеринга готов к новым командам, и может указывать на узкое место в основном потоке.

Gfx.PresentFrame: Поток рендеринга представляет собой время, затраченное на ожидание рендеринга и представления кадра графическим процессором, что может включать ожидание VSync.

Gfx.WaitForPresent: Когда основной поток готов начать рендеринг следующего кадра, но поток рендеринга еще не завершил ожидание представления кадра GPU. Это может указывать на то, что узкое место в GPU. Посмотрите на представление временной шкалы, чтобы узнать, проводит ли поток рендеринга одновременно время в Gfx.PresentFrame. Если поток рендеринга все еще проводит время в Camera.Render, узкое место в CPU, т.е. тратит слишком много времени на отправку вызовов отрисовки/текстур на GPU.

#profiler #profiling #performance #gpu #cpu
👍37🔥141😐1
Fluent-interface

Вы, наверное, слышали и встречали такую штуку, а некоторые даже писали.

instance.Method1().Method2()

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

Как?
Ну это довольно просто:
void Method() {…}
Меняем на
T Method() { return this; }
Где T может быть текущим типом, может быть интерфейсом (как в оригинале было и задумано, но структуры все портят).

Где?
Я использую такое в API своих ME.ECS/BECS для сборки фильтров и запросов.
Вне ecs я передаю настройки в виде структуры, которую я собираю как раз через такой подход. Ну и твинер у нас написан с таким же походом.

Зачем?
Лаконичность кода и легкость восприятия.

Пишите в комментах где вы используете fluent-interface подход.

#architecture #fluent #interface
👍6🔥5🥴4🐳2🤡1
AOT статик методы для компиляции

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

И тут знаете, как в шуточках за 300, люди делятся на несколько групп:
1. Неопытные с ужасом идут гуглить ошибку, ничего не находят, идут дергать вторую группу;
2. Более опытные понимают, что тут как-то не очень все тривиально, идут в место где рефлексией создается какой-то объект и пытаются понять как же можно было накосячить создателям плагина, что оно не работает;
3. Ну и такие как мы с вами, которые в принципе уже понимают, что билд скорее всего il2cpp, что рефлексия - это плохо, что il2cpp ничего об этом знать не знает, а значит проблема просто в том, что метода нет после компиляции.

К чему я это все. Для исправления подобных ситуаций, il2cpp нужно "подсказать", что существует метод (или конструктор), для чего я обычно делаю в проекте файл AOT.cs:


public static class AOT {
[Preserve]
public static void Dummy() {
new SomeClass().Method(); // не вырезаем конструктор и метод из билда
...
}
}


Т.е. мы никогда не вызываем этот метод, но при сборке эти методы будут добавлены.
Вообще это касается всех вызовов через reflection и когда в проекте не указывается вызов.

#aot #il2cpp #error
🔥38👍9
https://news.1rj.ru/str/madcsharp

@alexmtq создал канал про оптимизации и unsafe приколдэсы на шарпе и в дотнете.
Сейчас пилю либу для унификации ансефа по всем рантаймам. Будет работать как в Mono/IL2CPP, так и в настоящем дотнете, т.е когда юнитеки завезут .NET Core всё продолжит работать.
Основные фичи:
1. вызов методов без .Invoke (т.е без аллокаций), вызов нативных методов без обёртки через нэйтив.
2, Изменение длины и типа массивов — это полезно для бинарной сериализации, рентерпретации данных без дополнительных аллокаций итд
3. Изменение типа размера листов + thread-safe способ для добавления элементов, в том числе через Burst, без дополнительной аллокации NativeList<T>
4. Изменение стрингов без переаллокации
И много всякого другого.
👍20🙈4
В Unity есть возможность Indirect рендеринга: Graphics.RenderMeshIndirect

Начну чуть издалека.

Что есть дроуколл?

Это запуск GPU пайплайна по неким параметрам.
Вот вспомните ту самую картинку, которую вы видели:
вертексный шейдер -> геометрический -> .... -> фрагментный

Для дроуколла надо выставить эти параметры:
-переключатели на гпу для НЕпрограммируемых stages
-программы для программируемых
-забиндить буферы и прочие ресурсы

Основные буферы - это вертексный (позиции) и индексный (индексы вертексов).

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

Что есть инстансинг?

это когда мы не делаем новые дроуколлы, а просто перезапускаем пайплайн X раз в рамках одного дк.
Для каждого перезапуска пайплайна мы просто меняем SV_InstanceID (видеокарта сама его меняет) - SV_InstanceID от 0 до X.
Мы не перебиндиваем ничего и не ждём команд от цпу - поэтому инстансинг очень быстрый.

Что есть индирект?

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

Но в рамках одного вызова, даже вызова индирект - мы не можем забиндить разные индексные оффсеты.

(p.s. схожим образом, кстати, работают Task и Mesh шейдера)

Что есть MDI?

Это когда гпу может сама решить сколько вызовов indirect ей нужно.
И не только запустить несколько вызовов пайплайна, но и индексные оффсеты сопоставить на разных вызовах самостоятельно.
Для этого гпу помимо номера инстанса - SV_InstanceID знает и номер текущей отрисовки.

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

Метод Graphics.RenderMeshIndirect позволяет рисовать MDI там где это поддерживается и делает серию обычных Indirect вызовов там, где MDI не поддерживается.
Однако, как показали тесты, семантика SV_DrawID поддерживается не везде =(

Автор: @shiko_q
Источник: https://news.1rj.ru/str/unity_cg/48214

#rendering #graphics
🔥28🗿41👍1🤩1
Давно у нас не было никаких событий, надо исправляться 🙂
В эту субботу (c 16:00 по мск) пройдет событие на тему ME.BECS, я расскажу как она устроена, отвечу на ваши вопросы.

Регайтесь по ссылке:
https://unsafecsharp.timepad.ru/event/2583150/

#event #ecs
🔥23👍2
Параметры метода (params)

Есть такая штука, которая позволяет вызывать метод, когда мы не знаем сколько параметров хотим туда передать:

void Method(params object[] arr)


Но есть некоторые особенности, которые нужно понимать:

1. По-умолчанию такой вызов создает новый массив (т.е. триггерит GC, что плохо):

void Method1(params object[] arr)
void Method2(object[] arr)

var arr = new int[10];
Method1(arr); // будет создан массив object[1] и к первому элементу присвоен массив arr
Method2(arr); // будет ошибка компиляции, т.к. object[] не соотвествует типу int[]



2. Если мы передадим точный тип массива, то это не будет создавать новый массив:

void Method1(params int[] arr)
void Method2(int[] arr)

var arr = new int[10];
Method1(arr);
Method2(arr);
// Эти 2 вызова идентичны



3. Любой вызов будет создавать новый массив:

void Method(params int[] arr)

Method(1, 2, 3)
Method(1)


Вывод: избегайте методов с params, особенно если это хот часть.

#gc #performance #params #basics
👍17🤡4🔥1