Чтобы понять как они работают, нужно понять как работает 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#.

Читать далее  

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 байт.

Читать далее  

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.

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

Читать далее  

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

Читать далее  

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

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

direction * (deltaTime * 2f) 

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

Читать далее  

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

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

Вектор3.Расстояние внутри: sqrt(x*x + y*y + z*z)

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

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

Читать далее  

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

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

Читать далее  

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

 [сборка: InternalsVisibleTo("Friend.Assembly.Name")]
Читать далее  

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

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

Код 

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

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

Читать далее  

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

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

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

Читать далее  

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

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

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

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

Читать далее