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

 Метод void (объект params [] arr)

Но есть некоторые особенности, которые необходимо учитывать:

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

 void Method1 (объект параметров [] arr)
void Method2 (объект [] обр)

вар arr = новый int[10];
Метод1(обр); // будет создан массив object[1] и к первому элементу будет применен массив arr
Метод2(обр); // будет ошибка компиляции, т.к. объект[] не соотвествует типу int[]

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

 void Method1 (параметры int [] arr)
недействительный метод2 (int [] обр)

вар arr = новый int[10];
Метод1(обр);
Метод2(обр);
// Эти 2 вызова ответственных

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

 Метод void (params int[] arr)

Метод(1, 2, 3)
Метод(1)

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

Читать далее  

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

Читать далее  

Давайте сначала разберемся, что такое Лерп. Это линейная функция, которая задается двумя точками B и C (см изображение), третье значение t — это как значение между 0 и 1, где 0 — это B, а 1 — это C.

Таким образом получается, что перемещение по линии - это и есть lerp или интерполяция. А вот LerpUnclamped - это экстраполяция, т.е. когда мы не обрезаем значение t до 0..1.

 б + (с - б) * зажим01(т);

Т.е. по сути мы сначала меняем набор координат на ноль (c - b), а потом умножаем получившийся вектор на t, а после постепенно набираемый вектор назад.

С разжатым зажимом мы просто не делаем операцию, тем самым мы можем дать результат выезду за границу Британской Колумбии.

Читать далее  

Если вы не используете параметр <span>out</span>, по которому возвращается значение, то можете писать просто _:

Method(out int notUsed);
Method(out _);
Читать далее  

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

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

 а += б/2ф;

И мне все время такое хочется, напиши такой код:

 а += б * 0,5f;

Также можно заменить любые другие константы по тому же принципу. Но что делать, если у нас разделение не на константу, а на x? Да все просто, делаем y = 1f / x и используем уже y.

Читать далее  

Довольно простой вопрос, который решается банальной проверкой (v2.sqrMagnitude <= radius * radius). Мы такой вопрос часто задаем на собесах, но не только для того, чтобы выяснить считает ли человек через квадрат радиуса, а скорее для того, чтобы задать второй вопрос: "а в эллипс?". И вот на этом вопросе люди начинают сами себя закапывать. Кто-то придумывает несуществующие правила и теоремы, кто-то говорит, что мол "да я это не помню, там высшая математика, кому это вообще надо", ну а кто-то предлагает решение.

А самое интересное, что решение довольно простое, которое не требует никаких знаний и формул:

Изменяем Y проверяемой точки на фактор соотношения Rx к Ry, а дальше проверяем на попадание в радиус Rx. То есть мы вытягиваем эллипс таким образом, чтобы он стал кругом и считаем уже относительно круга.

Читать далее  

Он сравнивает не точное значение, а приблизительное, с учетом погрешностей:

 вар v = новый Vector3 (0f, 0f, 0f);
вар v2 = Vector3.one - Vector3.one;

v == v2
Читать далее  

Условие всегда ленивое и хочет побыстрее выйти. Если v1 будет правдой, то что там дальше его не будет интересовать:

 if (v1 == true || v2 == true) {...}

Таким образом, если у нас есть такой код:

 вар v1 = CalcV1();
вар v2 = CalcV2();
if (v1 == true || v2 == true) {...}

Выглядит хоть и симпатично, но совершенно непроизводительно.

Лучше написать так:

 if (CalcV1() == true || CalcV2() == true) {...}

Естественно нужно понимать, что CalcV2 не будет возвращаться, если CalcV1 вернет true, поэтому не нужно это расчитывать. Но я надеюсь, что вы это :) знаете

Читать далее  

Destroy - это маркер, который говорит, что мы хотели бы удалить объект, но это не произойдет моментально. Юнити отложит удаление до конца кадра и он будет удален позже.

Object.Destroy(gameObject
if (gameObject != null) {
     // gameObject is alive
}

DestroyImmediate - это фактическое удаление, т.е. когда нужно удалить объект прямо здесь и сейчас.

Object.DestroyImmediate(gameObject
gameObject // тут он уже помер

Дело в том, что DestroyImmediate ломает юнити флоу, т.к. с точки зрения всех юнити-колбэков они выполнятся моментально, а не в нужном порядке, например, при использовании DefaultExecutionOrder.

При этом DestroyImmediate имеет дополнительный флаг, который позволяет убить ассет из проекта, про что стоит не забывать :)

И ремарка: Object.Destroy/Object.DestroyImmediate могут убить то, что вы туда передаете, т.е. передали компонент - убивается компонент, передали go - убивается go, текстуру - текстура и т.д. Иногда встречаю код вида Object.Destroy(component), при этом явно хотели убивать не компонент, а go.

Читать далее  

Любой n-мерный массив можно представить в виде одномерного:

 arr[x, y] = arr[x * ширина + y]

И да, это будет быстрее, чем двумерный массив.

Читать далее  

Довольно часто мне нужно упаковать 2 инта в лонг, или из лога получить 2 инта, ну или в шортов сделать инт и т.д.

Это применяется в основном в каких-нибудь Dictionary (или подобных кейсах) в виде ключей, чтобы не городить структуру, да и работать оно будет быстрее.

void ToInts(long a, out int a1, out int a2) {
     a1 = (int)(a & uint.MaxValue);
     a2 = (int)(a >> 32); 
}

long ToLong(int a1, int a2) {
     long b = a2;
     b = b << 32;
     b = b | (uint)a1;
     return b; 
} 
Читать далее  

Вы, наверное, не раз сталкивались с таким понятием как сложность алгоритмов. Существует несколько нотаций, которыми можно описать сложность алгоритма, в основном используется О-нотация (или О-большое), т.к. она описывает верхнюю границу сложности алгоритма в зависимости от входных параметров. Например, у вас есть такой метод:

int Method(int n) {
     var sum = 0;
     for (int i = 0; i < n; ++i) {
         sum += i;
     }
     return sum; 
} 

Т.е. мы передаем в метод некое число n, которое обозначает количество итераций цикла внутри метода. Верхняя сложность такого алгоритма будет O(n). При этом если мы добавим в конец метода еще несколько строк:

for (int i = 0; i < 10; ++i) {
    sum += i;
}

то казалось бы, что сложность должна увеличиться на 10 (O(n + 10), но в О-нотации это будет константное время, а значит мы не будем учитывать это в сложности, т.е. сложность все еще останется O(n).

Поэтому нужно понимать, что алгоритм с О-нотацией O(1) (константное время) может на самом деле занимать гораздо больше времени, чем вы расчитываете, это лишь показывает общую сложность алгоритма, но не говорит о его количестве операций.

Вот для сравнения методы со сложностью O(n) и O(1).

Метод O(n):

int Method(int n) {
     var sum = 0;
     for (int i = 0; i < n; ++i) {
         sum += i;
     }
     return sum; 
} 

Метод O(1):

int Method() {
     var sum = 0;
     for (int i = 0; i < 100_000; ++i) {
         sum += i;
     }
     return sum; 
} 

Как видно из примера, любой вызов первого метода с n < 100_000 будет отрабатывать быстрее, чем второй метод с константным временем выполнения. Так что когда вам говорят, что какая-то коллекция работает за константное время на добавление элементов, например, то это совсем не означает что она делает это максимально эффективно.

Читать далее