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

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

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

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

Read More  

В Unity добавили аттрибут HideInCallstack, который пока не работает 🙂 Но использовать его уже можно, когда заработает - тогда и заработает.

https://docs.unity3d.com/ScriptReference/HideInCallstackAttribute.html

Read More  

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

var marker = new ProfilerMarker("My Marker");
marker.Begin();
// тут код, который мы хотим проверить в профайлере
marker.End();

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

Read More  

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

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

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

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

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

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

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

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

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

Read More  

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

У нас 2д-игра, карту мы рисуем тайлами, траву мы тоже рисуем тайлами, но на специальной тайлмапе. Во время загрузки игры, эта тайлмапа выключается, а каждая нарисованная травинка превращается в партикл и рисуется уже ParticleSystem 🙂 Т.е. для контенщика ничего не меняется в его пайплайне, а для рендера становится все гораздо лучше.

Как шевелить. Тут мой любимый способ: добавляем камеру, которая снимает исключительно юнитов (можно сетить пиксель в текстуру - не принципиально). Текстура - это вся карта. Дальше в шейдере травы читаем из текстуры пиксель и применяем с этой силой шевеление вертексов (чем выше вертекс, тем больше он шевелится и т.д.).

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

Ну и да, горящая трава убивает юнитов 🙂

Read More  

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

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

или

MethodImpl(MethodImplOptions.AggressiveInlining)

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

[INLINE(256)]

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

using INLINE = System.Runtime.CompilerServices.MethodImplAttribute 

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

Read More  

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

const long SOME_CONST = 1000000000L; // пример плохой записи 
const long SOME_CONST = 1_000_000_000L; // пример хорошей записи 
Read More  

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

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

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

Read More  

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

[Conditional("DEBUG")]
void Method() {...}

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

Read More  

This is a generic blog article you can use for adding blog content / subjects on your website. You can edit all of this text and replace it with anything you have to say on your blog.

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

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

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