SerializeReference
Я уже рассказывал про этот аттрибут, который позволяет серилизовать любую структуру. Но недавно я столкнулся с интересным багом, т.к. по мне так это именно баг:
Если добавить поле с именем
На всякий случай я заглянул в доку и не нашел там ничего про то, что нельзя использовать поле с этим именем.
#unity #bug #serializereference
Я уже рассказывал про этот аттрибут, который позволяет серилизовать любую структуру. Но недавно я столкнулся с интересным багом, т.к. по мне так это именно баг:
public interface IMyData {}
[System.Serializable]
public struct MyStruct {
[SerializeReference]
public IMyData data;
public int references;
}
public MyStruct[] items;
Если добавить поле с именем
references
в эту же структуру где у вас будет SerializeReference, то при компиляции будет ошибка The same field name is serialized multiple times
с указанием именно на поле references.На всякий случай я заглянул в доку и не нашел там ничего про то, что нельзя использовать поле с этим именем.
#unity #bug #serializereference
Assembly Definition и Assembly Definition Reference
Я уже затрагивал эту штуку несколько раз, но совсем забыл рассказать про
Assembly Definition Reference
.Если
Assembly Definition
- это непосредственно либа, которая собирается из папки.То
Assembly Definition Reference
- это продолжение этой же либы, но уже в другой папке.Итого можно сделать несколько папок, которые будут собираться в одну библиотеку.
Это на самом деле дает возможность использовать internal классы/структуры/методы из пакетов, которые вы используете в проекте. Т.е. просто сделать указание на либу из пакета и можно спокойно использовать нужные internal методы.
#unity #asmdef #asmref
Буду на gdc, если кто-нибудь хочет встретиться - буду рад поболтать, обсудить проекты или еще что-нибудь :)
Если хотите что-нибудь конкретное узнать со стендов/докладов, могу сделать несколько постов.
#gdc2025
Если хотите что-нибудь конкретное узнать со стендов/докладов, могу сделать несколько постов.
#gdc2025
Unity 2d
Sprite Mask
Теперь можно использовать любой 2d renderer в качестве маски через mask source, включая tilemap.
Sprite Libraries
Выше уровнем, чем SpriteAtlas, по сути дает возможность нормально контролировать использование ресурсов.
Через Sprite Resolver можно подставлять части.
Pixel art
Поддержка aseprite, новый importer, включая анимации.
Performance
Завезли SRP Batcher.
2D light batching debugger по слоям.
Physics
Появились Layer overrides.
Simulation layers позволяют отключить слои симуляции.
Delaunay tessellation для генерации коллайдеров.
Rigidbody slide functions.
Advanced
Sprite shape geometry creator позволяет делать сложные формы.
ScriptablePacker позволяет интегрировать свой алгоритм паковки.
2D Renderer поддерживает Render Graph.
WIP
Render as 2D - позволяет рендерить 3д объекты как 2д с сортировкой, тенями, светом и пр.
#gdc2025
Sprite Mask
Теперь можно использовать любой 2d renderer в качестве маски через mask source, включая tilemap.
Sprite Libraries
Выше уровнем, чем SpriteAtlas, по сути дает возможность нормально контролировать использование ресурсов.
Через Sprite Resolver можно подставлять части.
Pixel art
Поддержка aseprite, новый importer, включая анимации.
Performance
Завезли SRP Batcher.
2D light batching debugger по слоям.
Physics
Появились Layer overrides.
Simulation layers позволяют отключить слои симуляции.
Delaunay tessellation для генерации коллайдеров.
Rigidbody slide functions.
Advanced
Sprite shape geometry creator позволяет делать сложные формы.
ScriptablePacker позволяет интегрировать свой алгоритм паковки.
2D Renderer поддерживает Render Graph.
WIP
Render as 2D - позволяет рендерить 3д объекты как 2д с сортировкой, тенями, светом и пр.
#gdc2025
Performance tips & tricks
Advanced profiling
CPU Native profilers (ios Instruments, android studio, etc)
Или
Use Superluminal (подключается к юнити, показывает методы включая cpp)
CPU issues
Shader variants (их может быть тысячи)
Используйте shader prewarming
SVC имеют метод WarmUp
GraphicsStateCollections заменяют SVC в unity 6
MPB не используйте в URP /HDRP
MeshRenderer в некоторых случаях будет лучше: если рисуем одну мешку больше 1к раз, используем одинаковые свойства
Используйте RenderMeshInstanced или BRG
Draw calls: используйте HLOD
Используйте меньше камер, каждая камера имеет большой оверхед
Используйте разные SRP для разных камер
Resident Drawer
Split jobs
GPU issues
Overdraw: прозрачные объекты перекрывают друг друга, исключите полностью прозрачные объекты
Используйте renderdoc
Quad Overdraw: используйте LOD/HLOD
Shaders: исключайте if (например shader keywords), используйте half, int16
Memory
Используйте native profilers
Избегайте коротких (по времени) и длинных аллокаций вместе
Используйте unmanaged аллокации
Используйте Memory profiler memory map
Используйте GC.Alloc call stacks feature
Asset duplication - ресурсы будут загружены в память несколько раз (при использовании бандлов)
Shaders
Используйте shader_feature вместо multi_compile
Используйте IPreprocessShaders API
#gdc2025
Advanced profiling
CPU Native profilers (ios Instruments, android studio, etc)
Или
Use Superluminal (подключается к юнити, показывает методы включая cpp)
CPU issues
Shader variants (их может быть тысячи)
Используйте shader prewarming
SVC имеют метод WarmUp
GraphicsStateCollections заменяют SVC в unity 6
MPB не используйте в URP /HDRP
MeshRenderer в некоторых случаях будет лучше: если рисуем одну мешку больше 1к раз, используем одинаковые свойства
Используйте RenderMeshInstanced или BRG
Draw calls: используйте HLOD
Используйте меньше камер, каждая камера имеет большой оверхед
Используйте разные SRP для разных камер
Resident Drawer
Split jobs
GPU issues
Overdraw: прозрачные объекты перекрывают друг друга, исключите полностью прозрачные объекты
Используйте renderdoc
Quad Overdraw: используйте LOD/HLOD
Shaders: исключайте if (например shader keywords), используйте half, int16
Memory
Используйте native profilers
Избегайте коротких (по времени) и длинных аллокаций вместе
Используйте unmanaged аллокации
Используйте Memory profiler memory map
Используйте GC.Alloc call stacks feature
Asset duplication - ресурсы будут загружены в память несколько раз (при использовании бандлов)
Shaders
Используйте shader_feature вместо multi_compile
Используйте IPreprocessShaders API
#gdc2025
Тут один хороший человек расписал работу моего аллокатора, который я использую в ME.ECS и ME.BECS. За что ему отдельное спасибо :)
P.S: Для чего нужен аллокатор и сорсы в этом посте: https://www.tgoop.com/unsafecsharp/49
#allocator #memory
P.S: Для чего нужен аллокатор и сорсы в этом посте: https://www.tgoop.com/unsafecsharp/49
#allocator #memory
Animator
На самом деле тут есть 2 момента, которые я бы хотел обозначить с точки зрения кода:
1. У аниматора есть
Например: при клике по кнопке нужно проиграть анимацию, которая висит на рут объекте, но будет затрагивать объект, который мы создаем в иерархии. Тогда перед тем как "заказывать" стейт для проигрывания у аниматора, нужно сделать animator.Rebind().
2.
#animator #unity #animations
На самом деле тут есть 2 момента, которые я бы хотел обозначить с точки зрения кода:
1. У аниматора есть
Rebind
. Используется в том случае, если анимация затрагивает объекты, которые создаются динамически, но на момент запуска аниматора их еще не было. Например: при клике по кнопке нужно проиграть анимацию, которая висит на рут объекте, но будет затрагивать объект, который мы создаем в иерархии. Тогда перед тем как "заказывать" стейт для проигрывания у аниматора, нужно сделать animator.Rebind().
2.
keepAnimatorStateOnDisable
. Это такая штука, которая позволяет "сохранить" (точнее не сбрасывать) состояние аниматора при выключении и последующем включении объекта, на котором есть аниматор. На самом деле если перейти в debug-режим инспектора, то эту галочку можно включить там и без кода.#animator #unity #animations
ME.BECS #1: Создание и инициализация проекта
@heavyfront сделал то, до чего у меня не доходили руки;) За что ему огромное спасибо!
https://youtu.be/PCdhnXEjRQI
#becs #tutorials
@heavyfront сделал то, до чего у меня не доходили руки;) За что ему огромное спасибо!
https://youtu.be/PCdhnXEjRQI
#becs #tutorials
YouTube
ME.BECS #1: Создание и инициализация проекта
С чего начать и как инициализировать проект в ME.BECS.
Телеграм канал: https://www.tgoop.com/unsafecsharp
csc.rsp:
-define:EXCEPTIONS_CONTEXT
-define:EXCEPTIONS_THREAD_SAFE
-define:EXCEPTIONS_COLLECTIONS
-define:EXCEPTIONS_COMMAND_BUFFER
-define:EXCEPTIONS_ENTITIES…
Телеграм канал: https://www.tgoop.com/unsafecsharp
csc.rsp:
-define:EXCEPTIONS_CONTEXT
-define:EXCEPTIONS_THREAD_SAFE
-define:EXCEPTIONS_COLLECTIONS
-define:EXCEPTIONS_COMMAND_BUFFER
-define:EXCEPTIONS_ENTITIES…
UI.Windows
Рассказываю об основных возможностях UI.Windows
https://youtu.be/S6qLvaY204M
#tutorials #uiws #ui
Рассказываю об основных возможностях UI.Windows
https://youtu.be/S6qLvaY204M
#tutorials #uiws #ui
YouTube
unsafecsharp: Как использовать Unity UI.Windows
Как использовать UI.Windows, основные возможности
Телеграм канал: @unsafecsharp
https://github.com/chromealex/UI.Windows-submodule
Телеграм канал: @unsafecsharp
https://github.com/chromealex/UI.Windows-submodule
ME.BECS #4: Query, Jobs. Network input events, transport.
https://youtu.be/dzUZorAW_uI
#tutorials #becs
https://youtu.be/dzUZorAW_uI
#tutorials #becs
YouTube
ME.BECS #4: Запросы в мир, jobs, работа с сетью, сетевые сообщения
Как использовать queries и jobs. Работа с сетью, как создавать инпут события, настройки транспорта, окно Replays для дебага.
Я тут на выходных посидел и все таки добил вариант с детерминированным созданием сущностей в многопоточке.
Как это работает:
1. На этапе кодогена, разбираются все джобы и в IL смотрю сколько раз вызывается создание сущности в конкретной джобе. Данные кодогенятся в специальную табличку;
2. Когда мы в коде делаем AsParallel и Schedule для джобы - если для этой джобы больше одного создания сущности за одну итерацию, то резервируем столько сущностей, сколько необходимо чтобы обеспечить несколько потоков;
3. Каждый поток знает индекс смещения и итерацию, т.е. фактически знает откуда забрать новые id сущностей и какими они будут;
Какую проблему оно решает? Представим код джобы:
Если такой код запустить в однопоточке - будет 1, потом 2.
А вот если в многопоточке, то тут как повезет, первым будет id 1, а вторым что угодно, т.к. id 2 мог забрать себе уже другой поток.
Таким образом, если я буду знать сколько ентитей нужно создать за одну итерацию, то и проблем возникнуть не должно.
Ограничением становятся только циклы. Если при разборе IL, я вижу, что создание сущности вложено в цикл, то такую джобу зашедулить многопоточно будет нельзя (будет исключение).
Если хотите подробнее про IL и каким образом я разбираю код - могу сделать отдельный пост.
#becs #news
Как это работает:
1. На этапе кодогена, разбираются все джобы и в IL смотрю сколько раз вызывается создание сущности в конкретной джобе. Данные кодогенятся в специальную табличку;
2. Когда мы в коде делаем AsParallel и Schedule для джобы - если для этой джобы больше одного создания сущности за одну итерацию, то резервируем столько сущностей, сколько необходимо чтобы обеспечить несколько потоков;
3. Каждый поток знает индекс смещения и итерацию, т.е. фактически знает откуда забрать новые id сущностей и какими они будут;
Какую проблему оно решает? Представим код джобы:
var e1 = Ent.New(); // выдается id 1
var e2 = Ent.New(); // выдается id 2
Если такой код запустить в однопоточке - будет 1, потом 2.
А вот если в многопоточке, то тут как повезет, первым будет id 1, а вторым что угодно, т.к. id 2 мог забрать себе уже другой поток.
Таким образом, если я буду знать сколько ентитей нужно создать за одну итерацию, то и проблем возникнуть не должно.
Ограничением становятся только циклы. Если при разборе IL, я вижу, что создание сущности вложено в цикл, то такую джобу зашедулить многопоточно будет нельзя (будет исключение).
Если хотите подробнее про IL и каким образом я разбираю код - могу сделать отдельный пост.
#becs #news
IL или как я разбираю джобы
Что такое IL? IL (или Intermediate Language) - это промежутный язык, который еще и не рантайм, но уже и не C#.
Его смысл в том, что это плюс-минус набор инструкций, по которым можно понять суть происходящего в методе.
Как оно выглядит?
Чем я пользуюсь? https://github.com/chromealex/ME.BECS/tree/main/Runwww.tgoop.com/Extensions/Mono.Reflection
Это не Mono.Cecil, но для моих задач подходит отлично.
Работа этой штуки сводится к тому, чтобы разобрать инструкции метода и сложить их в плюс-минус удобный список.
Давайте разберем задачу из прошлого поста:
Нам нужно посчитать количество вызовов метода.
Фактически в данный момент мы посчитали количество вызовов определенного метода в джобе рекурсивно заходя в каждый метод. У меня к этому коду еще добавлены различные аттрибуты, чтобы не заходить в ненужные методы и чтобы не попасть в рекурсию, но я уверен, что вы справитесь с этой задачей.
Как я говорил в прошлом посте, нам нужно считать не просто количество вызовов, но и посчитать количество вызовов в цикле (ну или определить, что они в принципе там есть).
Для этого нам нужно обратиться к
Кодов очень много, но нас интересуют лишь коды бранчей.
Бранч - это ветка. Бранчи могут быть созданы в различных кейсах: это может быть if, может быть цикл foreach, for, while и т.д.
Код вида
И тут начинается самое интересное. Любые циклы и условия - это одна и та же операция
Так вот если адрес этого перехода меньше, чем адрес текущей инструкции - мы закончили итерацию цикла и возвращаемся назад.
А если инструкция
В коде выше можно увидеть эту инструкцию в виде
Текущий код по поиску информации можно посмотреть здесь:
https://github.com/chromealex/ME.BECS/blob/a665a344e6957ff18945dd9862981d79910c0491/Editor/CodeGenerator/Generators/JobsEarlyInitCodeGenerator.cs#L437
#becs #il #jobs
Что такое IL? IL (или Intermediate Language) - это промежутный язык, который еще и не рантайм, но уже и не C#.
Его смысл в том, что это плюс-минус набор инструкций, по которым можно понять суть происходящего в методе.
Как оно выглядит?
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld int32 C::a
IL_0007: ldc.i4.s 10
IL_0009: cgt
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: brfalse.s IL_0018
IL_000f: nop
IL_0010: ldarg.0
IL_0011: call instance void C::A()
IL_0016: nop
IL_0017: nop
IL_0018: ldarg.0
IL_0019: call instance void C::A()
IL_001e: nop
IL_001f: ldarg.0
IL_0020: call instance void C::A()
IL_0025: nop
IL_0026: ret
Чем я пользуюсь? https://github.com/chromealex/ME.BECS/tree/main/Runwww.tgoop.com/Extensions/Mono.Reflection
Это не Mono.Cecil, но для моих задач подходит отлично.
Работа этой штуки сводится к тому, чтобы разобрать инструкции метода и сложить их в плюс-минус удобный список.
Давайте разберем задачу из прошлого поста:
Нам нужно посчитать количество вызовов метода.
var count = 0;
var targetMethod = typeof(Ent).GetMethod(nameof(Ent.New), ...);
var instructions = executeJobMethod.GetInstructions();
for (int i = 0; i < instructions.Count; ++i) {
var inst = instructions[i];
{
// тут можем обработать инструкцию
if (inst.Operand is System.Reflection.MethodInfo method && method == targetMethod) {
++count;
}
}
{
if (inst.Operand is System.Reflection.MethodInfo method) {
instructions.InsertRange(i + 1, method.GetInstructions());
}
}
}
Фактически в данный момент мы посчитали количество вызовов определенного метода в джобе рекурсивно заходя в каждый метод. У меня к этому коду еще добавлены различные аттрибуты, чтобы не заходить в ненужные методы и чтобы не попасть в рекурсию, но я уверен, что вы справитесь с этой задачей.
Как я говорил в прошлом посте, нам нужно считать не просто количество вызовов, но и посчитать количество вызовов в цикле (ну или определить, что они в принципе там есть).
Для этого нам нужно обратиться к
OpCodes
. Это по сути код операции у инструкции, которую мы сейчас обрабатываем.Кодов очень много, но нас интересуют лишь коды бранчей.
Бранч - это ветка. Бранчи могут быть созданы в различных кейсах: это может быть if, может быть цикл foreach, for, while и т.д.
Код вида
br*
будет приводить к переходу (типа goto) к конкретной инструкции.И тут начинается самое интересное. Любые циклы и условия - это одна и та же операция
br*
, но за одним исключением: вместе с этой операцией передается номер строки (например, IL_0007
), куда нужно осуществить переход.Так вот если адрес этого перехода меньше, чем адрес текущей инструкции - мы закончили итерацию цикла и возвращаемся назад.
А если инструкция
br*
ведет нас вперед, то это открытие условия или цикла.В коде выше можно увидеть эту инструкцию в виде
IL_000d: brfalse.s IL_0018
. То есть мы переходим в 0018, что находится ниже, значит это условие, а не цикл.Текущий код по поиску информации можно посмотреть здесь:
https://github.com/chromealex/ME.BECS/blob/a665a344e6957ff18945dd9862981d79910c0491/Editor/CodeGenerator/Generators/JobsEarlyInitCodeGenerator.cs#L437
#becs #il #jobs
GitHub
ME.BECS/Runwww.tgoop.com/Extensions/Mono.Reflection at main · chromealex/ME.BECS
Bursted Entity Component System. Contribute to chromealex/ME.BECS development by creating an account on GitHub.
https://youtu.be/vItAprfcc0A
Про EntityConfig, Left/Right/Full join, Static компоненты.
#becs #tutorials
Про EntityConfig, Left/Right/Full join, Static компоненты.
#becs #tutorials
YouTube
ME.BECS #5: EntityConfigs
Как использовать и применять конфиги на Entity, как использовать Static компоненты. Типы join'ов у конфигов и как они работают.
https://youtu.be/4mbOPXLfArM
Про Addressables, что такое GlobalEvents как их использовать.
#becs #tutorials
Про Addressables, что такое GlobalEvents как их использовать.
#becs #tutorials
YouTube
ME.BECS #5: Addressables, GlobalEvents
Как работать с Addressables, что такое GlobalEvents и как их использовать
https://github.com/chromealex/ME.Monitor/tree/main
Написал тут тулзу для мониторинга за серваками.
Умеет ping, tcp, rest, выводит графики, рисует маршруты. Предназначена для сборки в качестве standalone/adroid/ios билдов. Строит наглядно визуализацию расположения серверов и маршруты к ним. Для примера есть Config.json с рандомными сервисами, которые можно легко заменить на свои.
Проект написан на коленке и там ничего не структурировано, так что без поллитра вряд ли разберетесь. Тем не менее, вполне хорошее решение получилось и свою задачу выполняет отлично. Собираете билд, подменяете конфиг на свои серваки и вперед наблюдать.
Предложения принимаются :)
#unity #tools #monitoring
Написал тут тулзу для мониторинга за серваками.
Умеет ping, tcp, rest, выводит графики, рисует маршруты. Предназначена для сборки в качестве standalone/adroid/ios билдов. Строит наглядно визуализацию расположения серверов и маршруты к ним. Для примера есть Config.json с рандомными сервисами, которые можно легко заменить на свои.
Проект написан на коленке и там ничего не структурировано, так что без поллитра вряд ли разберетесь. Тем не менее, вполне хорошее решение получилось и свою задачу выполняет отлично. Собираете билд, подменяете конфиг на свои серваки и вперед наблюдать.
Предложения принимаются :)
#unity #tools #monitoring