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

Для этого нужно указать sub emitter у основной партикл системы, а все модули основной системы отключить. Мы это использовали для поджигания травы (Кто не видел пост - https://t.me/unsafecsharp/48), чтобы нарисовать огонь для каждой травинки.

Да, весь прикол в том, что вся эта радость будет рисоваться в 1 DrawCall, т.к. партикл система знает все, что нужно о своих подсистемах, за что ей отдельное спасибо.

В итоге мы контролируем огонь через Emit + GetParticles/SetParticles, а что там за огонь - это уже vfxер нарисует, настраивая обычную систему. Имейте ввиду, что ограничение в подсистемах на maxParticles должен быть расчитан на все системы, а не на одну.

Читать далее  

В Unity добавлен атрибут HideInCallstack, который пока не работает

Читать далее  

если мы знаем, что в Unity Profiler можно включить Deep и посмотреть, что там вообще происходит в ваших методах. Проблема только в том, что в больших проектах это тормозит, занимает некоторое время, а в профайлере нужно долго потом искать проблемные места. Для этого можно использовать вот такой светильник:

 var маркер = новый ProfilerMarker («Мой маркер»);
маркер.Начать();
// вот код, который мы хотим проверить в профайлере
маркер.Конец();

Более того, этот маркер можно использовать в Burst.

Читать далее  

MemoryAllocator проще всего представить как один большой неразрывный массив байт. Чтобы положить туда данные - нужно всего лишь знать по какому индексу это делать. Для этого аллокатор разбивается на блоки. В пустом аллокаторе блок всего один, он занимает всю область памяти от начала и до конца. Блок - это структура, у которой есть часть заголовка (с указателями на следующий/предыдущий блоки, состоянием "свободен"/"занят" и размером блока) и следом сами данные.

[block_size][state][prev][next][user_data]

Когда мы просим аллокатор дать нам память определенного размера, нам нужно найти свободный блок памяти. Тут мы просто переходим от первого блока до последнего и ищем блок подходящего размера. Если блок не нашли - добавляем новый. А возвращаем мы не unsafe-указатель, а собственный указатель, в котором записан тот самый индекс в нашем массиве байт. Когда мы просим освободить память, мы выставляем блоку состояние "свободен" и мерджим его с соседними свободными блоками, если такие есть.

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

Таким образом мы получаем следующие бенефиты:

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

2. Выдаваемые указатели можно так же передавать по сети, т.к. они будут валидны на любом клиенте, если тот имеет такой же аллокатор;

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

Реализацию можно посмотреть тут: https://github.com/chromealex/csharp-memory-allocator

Читать далее  

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

Читать далее  

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

System.Runtime.CompilerServices.MethodImplAttribute(MethodImplOptions.AggressiveInlining)

или

MethodImpl(MethodImplOptions.AggressiveInlining)

Я обычно пишу гораздо короче: 

[INLINE(256)]

При этом объявляю 

using INLINE = System.Runtime.CompilerServices.MethodImplAttribute 

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

Читать далее  

Записывайте большие числа читаемо. 

const long SOME_CONST = 1000000000L; // пример плохой записи 
const long SOME_CONST = 1_000_000_000L; // пример хорошей записи 
Читать далее  

Используйте блоки кода вида 

{     
     // block 1 
}
{     
     // block 2 
} 

для того, чтобы избежать конфликтов локальных переменных и перестать выдумывать нечитаемые названия типа i, k, j, z etc 🙂

Читать далее  

Существует такой атрибут Conditional, который дает возможность отключить/включить куски кода по дефайну.

 [Условное("ОТЛАДКА")]
недействительный метод() {...}

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

Читать далее  

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

Вы можете написать метод GetEnumerator в любой структуре или классе, это позволит использовать конструкцию foreach.

Но есть несколько моментов, которые стоит понимать:

  1. Результат метода должен вернуть структуру или объект, в котором есть метод MoveNext и свойство Current;
  2. При использовании интерфейса IEnumerable (например, в List<>) при любом использовании foreach или GetEnumerator значение будет запаковано (boxing) и избежать этого уже никак не выйдет.
Читать далее