Используйте битмаски. Я редко встречаю код с битмасками, уж не знаю почему, но в основном люди предпочитают обходить их стороной. Разбираемся, ведь в них нет ничего сложного.
Битовые маски могут использоваться для манипулирования отдельными битами в числе. Это может быть полезно, например, чтобы проверить, является ли определенный бит установленным или снятым.
Можно использовать биты в
Записи эквиваленты друг другу, я встречал оба варианта.
Для вывода можно использовать аттрибут
#bitmask #code
Битовые маски могут использоваться для манипулирования отдельными битами в числе. Это может быть полезно, например, чтобы проверить, является ли определенный бит установленным или снятым.
int value = 0b1011; // ставим дефолтное значение
// проверка, является ли второй бит установленным
if ((value & (1 << 1)) != 0) {
// бит установлен
}
// установка третьего бита
value |= (1 << 2); // теперь value == 0b1111
// сброс третьего бита
value &= ~(1 << 2); // теперь value == 0b1011
Можно использовать биты в
enum, записывать их можно по-разному:
enum MyEnum {
None = 0,
Value1 = 1 << 0,
Value2 = 1 << 1,
Value3 = 1 << 2,
Value4 = 1 << 3,
Value5 = 1 << 4,
Value1OrValue3 = Value1 | Value3,
}
enum MyEnum {
None = 0,
Value1 = 0x1,
Value2 = 0x2,
Value3 = 0x4,
Value4 = 0x8,
Value5 = 0x10,
Value1OrValue3 = Value1 | Value3,
}
Записи эквиваленты друг другу, я встречал оба варианта.
Для вывода можно использовать аттрибут
System.Flags, но он не является обязательным, хотя влияет на отображение в Unity Inspector и на вывод в лог.#bitmask #code
👍22🔥1
Давайте теперь про Layout структур.
Давайте представим, что у нас есть 2 структуры:
А теперь напишем джобу, которая перекладывает данные из одного массива в другой:
Какой вариант джобы будет работать быстрее?
Логика подсказывает нам, что V3, т.к. данных копировать нужно меньше, да и вообще размер будет намного меньше.
Давайте разберемся же, что там получается на выходе:
Для V3 варианта мы должны скопировать структуру значение за значением, т.е. 3 раза.
Для V4 варианта мы вроде должны скопировать 4 значения. Но тут вламывается векторизация и выходит, что вариант V4 будет работать примерно на треть быстрее, чем вариант V3.
Но не расстраивайтесь, можно все исправить: (да, можно исправить разными способами)
#burst #struct #structlayout #alignment #simd
Давайте представим, что у нас есть 2 структуры:
public struct V3 {
public float3 x;
}
public struct V4 {
public float4 x;
}
А теперь напишем джобу, которая перекладывает данные из одного массива в другой:
[BurstCompile]
public struct MyJob : IJob {
[ReadOnly] public NativeArray<V3/V4> source;
public NativeArray<V3/V4> dest;
public void Execute() {
for (int i = 0; i < source.Length; ++i) {
dest[i] = source[i];
}
}
}
Какой вариант джобы будет работать быстрее?
Логика подсказывает нам, что V3, т.к. данных копировать нужно меньше, да и вообще размер будет намного меньше.
Давайте разберемся же, что там получается на выходе:
Для V3 варианта мы должны скопировать структуру значение за значением, т.е. 3 раза.
Для V4 варианта мы вроде должны скопировать 4 значения. Но тут вламывается векторизация и выходит, что вариант V4 будет работать примерно на треть быстрее, чем вариант V3.
Но не расстраивайтесь, можно все исправить: (да, можно исправить разными способами)
public struct V3 {
public float3 x;
public float _;
}
#burst #struct #structlayout #alignment #simd
👍11🤯9❤1
Господа! Осталось полчаса до старта нашего второго созвона;) Кто еще не зарегался - велкам! https://news.1rj.ru/str/unsafecsharp/39
Telegram
Unity: Всё, что вы не знали о разработке
Регаемся на мит, который мы проведем как и в прошлый раз в гугл мите:
https://unsafecsharp.timepad.ru/event/2439793/
Событие пройдет в эту субботу с 17:00 до 18:00 (в прошлый раз мы просидели полтора часа, так что имейте ввиду).
#event
https://unsafecsharp.timepad.ru/event/2439793/
Событие пройдет в эту субботу с 17:00 до 18:00 (в прошлый раз мы просидели полтора часа, так что имейте ввиду).
#event
👍4
Мы знаем, что структуры нужно инициализировать в конструкторе полностью:
Иногда полей много и можно написать гораздо короче:
#structs #lifehack
struct MyStruct {
public int field1;
public int field2;
...
public int fieldN;
public MyStruct(int field1) {
this.field1 = field1;
// тут нужно инициализировать все поля
this.field2 = default;
...
this.fieldN = default;
}
}
Иногда полей много и можно написать гораздо короче:
struct MyStruct {
public int field1;
public int field2;
...
public int fieldN;
public MyStruct(int field1) {
this = default;
this.field1 = field1;
}
}#structs #lifehack
👍44🤯20🔥7
Для дебага полезная штука - написать свой Proxy:
Где
Это сильно помогает при дебаге сложных штук.
#debug #code
[System.Diagnostics.DebuggerTypeProxyAttribute(typeof(DebugClass))]
public class YourClass { ... }
Где
DebugClass - это отдельный класс, который может содержать геттеры и поля. Еще у него должен быть конструктор, который будет принимать инстанс YourClass.Это сильно помогает при дебаге сложных штук.
#debug #code
👍12🤔2🔥1
Про CC коллекции.
CC (`Concurrent Collections`) коллекции - это набор коллекций данных, разработанных для работы в многопоточной среде. Одной из особенностей CC коллекций является их lock-free (без блокировок) реализация, которая позволяет не блокировать весь многопоточный поток при обращении к коллекции.
Все CC коллекции стараются обходиться без
Давайте разберем простой пример, чтобы было понятно как именно работают такие коллекции.
Допустим, что нам нужно написать коллекцию
А теперь в многопоточность.
Как реализовать такую коллекцию? Давайте не будем вообще создавать никаких массивов, а будем использовать односвязный список из нод.
Коллекция же имеет только ссылку на head-ноду. При добавлении элемента нам нужно создать ноду и каким-то образом ее запихнуть к последней, используем
#multithreading #threading #collections #lockfree
CC (`Concurrent Collections`) коллекции - это набор коллекций данных, разработанных для работы в многопоточной среде. Одной из особенностей CC коллекций является их lock-free (без блокировок) реализация, которая позволяет не блокировать весь многопоточный поток при обращении к коллекции.
Все CC коллекции стараются обходиться без
lock, т.е. в нормальном режиме работы - либо вообще без lock, либо в редких исключениях его использование.Давайте разберем простой пример, чтобы было понятно как именно работают такие коллекции.
Допустим, что нам нужно написать коллекцию
Stack<> (возьмем самую простую). В однопоточной реализации мы используем массив элементов + индекс, который говорит нам где мы находимся в данный момент. При Push мы просто кладем элемент по индексу и увеличиваем индекс, а при Pop просто уменьшаем индекс. Ну еще при Push нам нужно проверить размер массива и сделать новый, если это нужно.А теперь в многопоточность.
Как реализовать такую коллекцию? Давайте не будем вообще создавать никаких массивов, а будем использовать односвязный список из нод.
Node - это объект, который имеет указатель на предыдущий элемент и данные внутри себя.Коллекция же имеет только ссылку на head-ноду. При добавлении элемента нам нужно создать ноду и каким-то образом ее запихнуть к последней, используем
Interlocked.CompareExchange и заменяем head на наш элемент. При Pop делаем обратную операцию.#multithreading #threading #collections #lockfree
👍12🤯2
🥕Задачка
У вас есть строка вида «aabbbccde»
Вам нужно написать алгоритм, который уберет все дубликаты символов, которые стоят вместе, то есть останется только «abcde». Для строки «aabbccaabb» ответ будет «abcab».
Нужен алгоритм, использовать всякие regex нельзя;)
У вас есть строка вида «aabbbccde»
Вам нужно написать алгоритм, который уберет все дубликаты символов, которые стоят вместе, то есть останется только «abcde». Для строки «aabbccaabb» ответ будет «abcab».
Нужен алгоритм, использовать всякие regex нельзя;)
🥱10👍3🤔2
Хотите ли вы посмотреть на ME.BECS?
Anonymous Poll
32%
Очень жду
58%
А что это?
8%
Нет, не интересуюсь ECS
2%
Свой вариант
❤4
Давайте напишем счетчик, значение которого мы хотим увеличивать из разных потоков, а после того как все потоки закончат работу, мы выводим это число.
Вот вроде бы и все, но на самом деле - можно быстрее. Каким образом?
Т.е. мы должны знать количество потоков и порядковый номер потока, в котором работаем (В
Т.е. мы создаем
#multithreading #threading #collections #lockfree
public class Counter {
public int value;
public void Increment() => Interlocked.Increment(ref this.value);
}
Вот вроде бы и все, но на самом деле - можно быстрее. Каким образом?
public class Counter {
public int[] values;
public int Count {
get {
var count = 0;
for (int i = 0; i < this.values.Length; ++i) count += this.values[i];
return count;
}
}
public void Increment(int threadIndex) => ++this.values[threadIndex];
}
Т.е. мы должны знать количество потоков и порядковый номер потока, в котором работаем (В
Unity Jobs есть JobsUtility.ThreadIndex и JobsUtility.ThreadIndexCount). Т.е. мы создаем
Counter с массивом по количеству потоков и при каждой операции Increment мы передаем номер текущего потока. Тогда этот счетчик будет работать без оверхеда на добавление совсем. А когда операции закончились - мы суммируем все счетчики и возрващаем значение.#multithreading #threading #collections #lockfree
👍17🤯5
Используйте указание на конкретный тип в enum, если вы используете меньше, чем int:
Таким образом:
Будет запаковано как 4 байта. Но не забывайте, что любая математика с enum приводит к int 🙂
#enum #code #structlayout
enum MyEnum : byte {
Value1,
Value2,
Value3,
Value4,
}
Таким образом:
struct Test {
public MyEnum e1;
public MyEnum e2;
public MyEnum e3;
public MyEnum e4;
}
Будет запаковано как 4 байта. Но не забывайте, что любая математика с enum приводит к int 🙂
#enum #code #structlayout
👍19🔥4
Написал немного про поиск пути, буду выкладывать ссылки на статьи в телеграф, если нужны картинки или хочу описать что-нибудь более подробно.
https://telegra.ph/Unity-Poisk-puti-05-28
#pathfinding #algorithms
https://telegra.ph/Unity-Poisk-puti-05-28
#pathfinding #algorithms
Telegraph
Unity: Поиск пути
Давайте поговорим про поиск пути. Я не буду описывать реализацию алгоритмов, хочу лишь описать возможные варианты, а если вас устроит один из них - вы без труда найдете готовую реализацию или напишете сами. Для начала определимся какие обычно встречаются…
👍21🔥14🤪2
🥕Задачка
Предыдущая задачка была совсем простая, поэтому вот вам посложнее, но снова про строки;)
У вас есть строка вида «aabbbccde»
Вам нужно написать алгоритм, который переставит символы так, чтобы не осталось символов, идущих подряд. Если это невозможно, то вернуть null.
«aabbbccde» = «ababcbcde»
«aaccbbcccc» = null
«aabbaac» = «ababaca»
«aaabbbb» = «bababab»
Дополнительно:
Возможно ли эту задачу без аллокаций, если исходная строка приходит в виде массива char?
Предыдущая задачка была совсем простая, поэтому вот вам посложнее, но снова про строки;)
У вас есть строка вида «aabbbccde»
Вам нужно написать алгоритм, который переставит символы так, чтобы не осталось символов, идущих подряд. Если это невозможно, то вернуть null.
«aabbbccde» = «ababcbcde»
«aaccbbcccc» = null
«aabbaac» = «ababaca»
«aaabbbb» = «bababab»
Дополнительно:
Возможно ли эту задачу без аллокаций, если исходная строка приходит в виде массива char?
🤔4
🥕Как определить пересекаются ли OBB и OBB?
OBB - это Oriented Bounding Box, это такой Rect с поворотом, но в 3D 🙂
Попробуйте ответить на этот вопрос до того, как посмотрите мое решение. Напишите в комменты свой вариант решения (просто описание алгоритма) хотя бы для 2D. Интересно было бы сравнить.
Мой вариант решения тут:
https://telegra.ph/Kak-poschitat-peresechenie-OBB-i-OBB-05-30
OBB - это Oriented Bounding Box, это такой Rect с поворотом, но в 3D 🙂
Попробуйте ответить на этот вопрос до того, как посмотрите мое решение. Напишите в комменты свой вариант решения (просто описание алгоритма) хотя бы для 2D. Интересно было бы сравнить.
Мой вариант решения тут:
https://telegra.ph/Kak-poschitat-peresechenie-OBB-i-OBB-05-30
🔥8
Давайте немного про сеть поговорим.
Какие протоколы мы используем?
TCP (Transmission Control Protocol) и UDP (User Datagram Protocol) - это два наиболее распространенных протокола передачи данных. Основное отличие между ними заключается в том, что TCP является протоколом, обеспечивающим надежную передачу данных, а UDP - протоколом без установления соединения и без гарантии доставки данных.
TCP обеспечивает контроль над передачей данных, используя механизмы подтверждения и повторной передачи пакетов в случае потерь или ошибок. Он также гарантирует, что данные будут доставлены в правильном порядке и без дублирования. TCP используется для передачи данных, которые требуют высокой степени надежности. Например, мы хотим передать информацию об игроке от сервера клиенту.
UDP, с другой стороны, не обеспечивает надежной передачи данных и не гарантирует их доставку. Он используется для передачи данных, которые не требуют высокой степени надежности, например, для передачи последней позиции игрока, когда нам не важно где он находился до этого.
RUDP (Reliable User Datagram Protocol) - это протокол, который сочетает в себе преимущества TCP и UDP. Он обеспечивает надежную передачу данных, как TCP, но без установления соединения, как UDP. RUDP используется для передачи данных с гарантией доставки, но не гарантирует порядок пакетов.
В случае с TCP при получении пакетов 1, 3, 4, 5, TCP отдаст только пакет 1, а остальные пакеты придержит до тех пор, пока пакет 2 не будет доставлен. Таким образом можно замечать некую «тормознутость» при использовании TCP, когда пакетов нет-нет, а потом хоп, и вдруг сразу и много. С RUDP/UDP такого не происходит.
Закончу на моей любимой шутке: я бы рассказал вам шутку про UDP, но боюсь что она до вас не дойдет.
#network #theory
Какие протоколы мы используем?
TCP (Transmission Control Protocol) и UDP (User Datagram Protocol) - это два наиболее распространенных протокола передачи данных. Основное отличие между ними заключается в том, что TCP является протоколом, обеспечивающим надежную передачу данных, а UDP - протоколом без установления соединения и без гарантии доставки данных.
TCP обеспечивает контроль над передачей данных, используя механизмы подтверждения и повторной передачи пакетов в случае потерь или ошибок. Он также гарантирует, что данные будут доставлены в правильном порядке и без дублирования. TCP используется для передачи данных, которые требуют высокой степени надежности. Например, мы хотим передать информацию об игроке от сервера клиенту.
UDP, с другой стороны, не обеспечивает надежной передачи данных и не гарантирует их доставку. Он используется для передачи данных, которые не требуют высокой степени надежности, например, для передачи последней позиции игрока, когда нам не важно где он находился до этого.
RUDP (Reliable User Datagram Protocol) - это протокол, который сочетает в себе преимущества TCP и UDP. Он обеспечивает надежную передачу данных, как TCP, но без установления соединения, как UDP. RUDP используется для передачи данных с гарантией доставки, но не гарантирует порядок пакетов.
В случае с TCP при получении пакетов 1, 3, 4, 5, TCP отдаст только пакет 1, а остальные пакеты придержит до тех пор, пока пакет 2 не будет доставлен. Таким образом можно замечать некую «тормознутость» при использовании TCP, когда пакетов нет-нет, а потом хоп, и вдруг сразу и много. С RUDP/UDP такого не происходит.
Закончу на моей любимой шутке: я бы рассказал вам шутку про UDP, но боюсь что она до вас не дойдет.
#network #theory
😁20👍12🥱2
У
#editor #serializedproperty
SerializedProperty появился boxedValue. Не во всех случаях он работает, но во всяком случае для большого числа кейсов можно теперь читать и писать значение нормально 🙂#editor #serializedproperty
👍9🔥3🥰1
Написал чуть-чуть про аттрибут Il2CppSetOptionAttribute.
https://telegra.ph/UnityIL2CPPCompilerServices-05-30
#il2cpp #compilation #performance
https://telegra.ph/UnityIL2CPPCompilerServices-05-30
#il2cpp #compilation #performance
Telegraph
Il2CppSetOptionAttribute
Для того, чтобы ускорить выполнение кода в райнтаме при компиляции через IL2CPP, можно воспользоваться аттрибутом Il2CppSetOptionAttribute: https://pastebin.com/6gdiGwde Его не существует в Unity, поэтому его нужно объявить самостоятельно, Unity сама найдет…
👍10🔥3👎1
Vector3.Distance или (a - b).sqrMagnitude?Я часто встречаю в различных проектах первый вариант при поиске ближайшей цели, например. Уж не знаю почему так получается, но, наверное, люди не особо вникают в то как это работает.
sqrMagnitude внутри: x*x + y*y + z*zVector3.Distance внутри: sqrt(x*x + y*y + z*z)В
sqrMagnitude по теореме Пифагора мы вычисляем расстояние между точками, тут все просто.Но когда мы можем использовать
sqrMagnitude? Самое банальное: проверка расстояния, когда каждая точка вычисляет расстояние одинаково. Получается, что корень считать нам нет никакой нужды, если нам нужно найти ближайший объект или, например, определить находится ли юнит в радиусе для выстрела.#math #performance #basics
👍30🥱7⚡2
Иногда в редакторе нужно использовать
Для этого можно использовать простой хак:
Еще нужно не забыть убить этот
#editor #lifehack #serializedproperty #serializedreference
SerializedProperty у объекта, до которого просто никак не дойти. Допустим, я хочу вывести поля класса, а класс этот находится не в ScriptableObject и не в компоненте.Для этого можно использовать простой хак:
public class Temp : ScriptableObject {
[SerializedReference]
public object data;
}
var temp = Temp.CreateInstance<Temp>();
temp.data = yourInstance;
var so = new SerializedObject(temp);
var prop = so.FindProperty("data");
Еще нужно не забыть убить этот
Temp 🙂#editor #lifehack #serializedproperty #serializedreference
👍19🔥2