Чтобы понять как они работают, нужно понять как работает Enumerator. Если коротко, то это некий объект, у которого есть метод MoveNext(), если его вызвать, то произойдет переключение на следующий шаг:

// step
yield return null;
// step 2

А теперь каким образом юнити собственно это делает. Раз у нас есть объект, мы можем добавить его в какой-нибудь список.

IEnumerator MyMethod() {
    // step 1
    yield return null;
    // step 2 
}

list.Add(MyMethod()); 

А теперь в update мы просто переключаем шаги:

for (int i = 0; i < list.Count; ++i) {
     if (item.MoveNext() == false) {
         item.Current // тут мы можем проверить возвращаемое значение, например, если там внутренняя корутина, то ее тоже хорошо бы выполнить 🙂
         list.RemoveAt(i);
         --i;
     }
}  

Таким образом, мы будем выполнять шаги, пока есть чего выполнять.

Корутины в юнити работают примерно таким образом. Еще я бы добавил, что при вызове StartCoroutine сразу выполняется первый шаг, т.е. вызывается MoveNext(), если он возрващает true, то значит дальше что-то есть и нужно добавлять корутину в список выполнения.

Т.е. нужно понять главное: корутины - это не какая-то особенная штуковина, которая работает только в юнити, это стандартный синтаксис и коллекции C#.

Read More  

in - это сахар от ref. Любой in в итоге становится ref. Соотвественно относиться к нему следует таким же образом. Но все же стоит понимать разницу. in - это доступ только на чтение, ref - чтение и запись, out - только запись.

Пример:

void Method(in Vector3 vector) {
  vector.x = 123f; // будет ошибка доступа
  vector.Method(); // будет сделана копия vector, если метод не readonly
}

А вот тот же пример с ref:

void Method(ref Vector3 vector) {
  vector.x = 123f; // ошибки не будет, значение будет изменено&nbsp;
  vector.Method(); // копии не будет
}

Теперь рассмотрим вариант с возвращаемым значением по ссылке:

Vector3[] arr;
public ref Vector3 Method(int index) {
  return ref arr[index];
}

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

void Method() {
  ref var a = 123;
}

Во-вторых, нужно понимать, что вернув ссылку - вы по сути получили указатель на память, т.е. не стоит менять тот участок, который вы используете:

ref var data = ref Method(index);
System.Array.Resize(ref arr, ...); // Меняем данные, на которые держим ссылку
data.x = 123f; // Изменит данные в предыдущем массиве, а не в новом, т.е. фактически изменений не будет

В-третьих, можно вернуть данные только для чтения:

public ref readonly Vector3 Method() {...}

только не путать с этим:

public readonly ref Vector3 Method() {...}

В первом варианте мы возвращаем данные, доступные для чтения, а во втором мы делаем метод, который не меняет объект, в котором объявлен.

Особенно прекрасно выглядит это:

public readonly ref readonly Vector3 Method() {...}

Лично я считаю, что это накосячили разрабы языка, т.к. readonly ref readonly - это дичь, почему было не использовать слово const?

Ну и последнее - нужно понимать, что in и ref - это ссылки, а значит void Method(in LargeStruct s) имеет смысл, чтобы избежать копирования, а вот void Method(in int val) не имеет, т.к. такой int будет передаваться по ссылке ref, но запрещать изменения, при этом ссылка будет занимать 8 байт.

Read More  

Cross Product (векторное произведение) - по сути это способ нахождения нормали. Если у нас есть 2 вектора, то эти два вектора так или иначе образуют плоскость. Так вот Cross дает нам нормаль этой плоскости. Направление этой нормали (она же может смотреть как вверх, так и вниз) зависит от порядка этих векторов, т.е. если Cross(a, b), то нормаль смотрит вверх (как на картинке). Если же Cross(b, a), то нормаль будет смотреть вниз. Можно запомнить по так называемому правилу "правой руки":

Есть еще несколько свойств, которые мы получаем, подробнее можно почитать, например, в википедии или https://en.wikipedia.org/wiki/Cross_product.

Dot Product (скалярное произведение) - по сути способ определить угол между векторами. Формула довольно простая: x1*x2 + y1*y2 + z1*z2. То есть мы складываем произведения по каждой из осей. Окей, что нам это дает? Можно запомнить, что по сути это дает нам косинус угла между векторами, если они нормализованы (т.е. длина вектора единица), т.е. acos(dot(a, b)) дает нам угол.
Подробнее тут или https://en.wikipedia.org/wiki/Dot_product.

Да, это далеко не полный перечень того, что можно получить, но я же пытаюсь объяснить "простыми словами" :)

Read More  

Размер указателя зависит от платформы, на которой выполняется код. На 32-битных платформах размер указателя равен 4 байта, а на 64-битных - 8 байт.

Read More  

Довольно часто мы пишем в коде подобные штуки: direction * deltaTime * 2f 

Фактически мы умножаем Vector direction на float и на какой-нибудь мультипликатор, например, 2. На самом деле лучше писать вот так:

direction * (deltaTime * 2f) 

Т.к. в таком случае операций умножения для вектора будет одна вместо двух.

Read More  

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

sqrMagnitude внутри: x*x + y*y + z*z

Vector3.Distance внутри: sqrt(x*x + y*y + z*z)

В sqrMagnitude по теореме Пифагора мы вычисляем расстояние между точками, тут все просто.

Но когда мы можем использовать sqrMagnitude? Самое банальное: проверка расстояния, когда каждая точка вычисляет расстояние одинаково. Получается, что корень считать нам нет никакой нужды, если нам нужно найти ближайший объект или, например, определить находится ли юнит в радиусе для выстрела.

Read More  

int i = 123; // всегда атомарно
long j = 234L; // атомарно на x64, но кому сейчас надо x32?
i++; // никогда не атомарно, т.к. мы читаем данные, увеличиваем, а потом записываем
Read More  

Метод Array.Resize не проверяет размер массива в сторону уменьшения. То есть всегда будет выделен массив необходимого размера на выходе.

Read More  

Можно вызывать internal методы из других assembly, но для этого нужно разрешить другим assembly видеть эти методы. Для этого нужно написать аттрибут InternalsVisibleTo с указанием имени assembly, которая будет видеть internal-методы:

[assembly: InternalsVisibleTo("Friend.Assembly.Name")]
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.

В C# строки хранятся в памяти в единственном экземпляре. Называется интернирование.

Код 

if (str1 == “something” || str2 == “something”)

не будет создавать 2 строки, а будет использовать ссылку на один и тот же объект (да, строка - это Reference Type).

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.

Чтобы атомарно изменить значение переменной можно использовать lock, но это один из самых долгих способов. Гораздо быстрее использовать Interlocked методы.

В некоторых случаях (в хот частях) лучше вообще обходиться без синхронизаций между потоками.

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.

Для сравнения Unity Object с null можно использовать конструкцию if (obj is null) вместо if (obj == null).

А еще если obj - это Unity Object, то оператор == перегружен и проверяет не только фактический null на стороне C#, но и объект на стороне C++.

Тот же эффект достигается при использовании ReferenceEquals.

Read More