Unity: Всё, что вы не знали о разработке – Telegram
Unity: Всё, что вы не знали о разработке
1.74K subscribers
40 photos
101 links
Авторский канал о разработке в Unity от Alex Silaev (CTO в Zillion Whales). Mushroom Wars 2 моих рук дело.
Рассказываю об интересный кейсах, делюсь лайфхаками, решениями.
Download Telegram
CallerFilePathAttribute

Этот аттрибут позволяет получить путь к файлу, из которого происходит вызов метода:


void Sample([CallerFilePath] string file = null) {
UnityEngine.Debug.Log(file);
}


Еще обратите внимание, что есть CallerMemberName (из какого метода вызов) и CallerLineNumber (на какой строке).

#debug #csharp #code
4🔥345👍3
Player Loop

Вместо MonoBehaviour на сцене можно использовать инжект в текущий луп. Для этого нужно понимать в какой именно мы хотим добавить вызов своего метода. Всего есть 8 групп:

TimeUpdate = 0
Initialization = 1
EarlyUpdate = 2
FixedUpdate = 3
PreUpdate = 4
Update = 5
PreLateUpdate = 6
PostLateUpdate = 7


Использовать проще всего с инжектом при инициализации, но в принципе можно вызывать этот код когда угодно, как и удалять свою систему из обработки:


[RuntimeInitializeOnLoadMethod]
static void Inject() {
var loop = PlayerLoop.GetCurrentPlayerLoop();
var sys = new PlayerLoopSystem {
type = typeof(YourClassName),
updateDelegate = () => Debug.Log("Custom Update")
};
var index = 4; // PreUpdate (см список выше)
loop.subSystemList[index].subSystemList = loop.subSystemList[index].subSystemList.Append(sys).ToArray();
PlayerLoop.SetPlayerLoop(loop);
}


#unity #playerloop
3🔥51👍2
Unity.Mathematics

На самом деле многие не поняли зачем им использовать Unity.Mathematics, если у них есть Vector2/Vector3 и Mathf, например.
Давайте разберем самый простой пример:

var a = 10;
var b = 20;
var c = 30;
var result = a * b + c; // Считаем результат


У Unity.Mathematics есть math.mad, который внутри делает тоже самое:

void mad(int a, int b, int c) => a * b + c;


И самая большая ошибка считать, что никакой разницы в коде не будет. А работает это так: при компиляции кода, вызов math.mad мапится напрямую на simd-инструкцию, то есть код превращается из 2х инструкций в одну.
Пример с math.mad - это лишь пример того, где вы можете сэкономить, в целом есть всякие select еще, которые тоже все избегают использовать.

#unity #simd #math
🔥46😁21
ResourcesAPI

В юнити 6.2 появилась классная штука, которая позволяет переопределить дефолтные методы загрузки ресурсов. И это круто, если подобным образом они будут развиваться и дальше, то когда-нибудь можно будет переписать весь движок 🙂

https://docs.unity3d.com/6000.2/Documentation/ScriptReference/ResourcesAPI.html

#unity #api #resources
🔥27🤡3👍2😁1
NoAlias

Этот аттрибут используется для Burst и позволяет экономить на инструкциях.
Например:


int Method(ref int a, ref int b) {
b = 13;
a = 42;
return b;
}


В данном случае Burst не знает, пересекаются ли данные a и b (хранятся ли в памяти в одном месте или нет).
То есть тут будет 3 инструкции mov, 2 для установки значения, а третья - для чтения этого значения. Если же добавить аттрибут NoAlias, возврат данных будет без дополнительной загрузки:


int Method([NoAlias] ref int a, ref int b) {
b = 13;
a = 42;
return b;
}


Аттрибут можно использовать для NativeArray (контейнеров), которые не пересекаются, и в качестве возврата из методов [return: NoAlias]. При этом это имеет смысл, если метод не заинлайнен, т.к. при инлайне аттрибут не будет иметь смысла.

#noalias #burst #code
🔥201
Всем привет ;)

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

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

https://boosty.to/chromealex

Пишите в личку: @chromealex
5🔥77👍24🤡5👏4
Рукожопы (других слов нет) выпустили апдейт юнити, в котором отломали uitk в инспекторе.
Так что ждем хотфикс, а пока (если у вас вдруг ломается инспектор) можно отключить uitk и использовать imgui:
Project Settings -> Editor -> use IMGUI

#editor #unity
😁51👍9
Unity.Profiling.IgnoredByDeepProfilerAttribute

Аттрибут, который спасет вам время, если у вас много кода, который вы точно не хотели бы видеть в профайлере.

#attributes #profiling #unity
🔥54👍10
Довольно интересная задачка попалась на собеседовании в гугле:
Есть матрица (массив массивов) вида
[
[1, 0, 0, 0, 0, 1],
[0, 1, 0, 1, 1, 1],
[0, 0, 1, 0, 1, 0],
[1, 0, 0, 0, 1, 0],
[1, 0, 1, 1, 0, 0],
[1, 0, 0, 0, 0, 1]
]


Нужно реализовать метод, который найдет все острова. Островом считаются 1, которые не примыкают к границам. Диагонали не считаются за соединения.
То есть на выходе должна быть матрица такого вида:
[
[0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0]
]


Как вы бы решали такую задачу?
Доп вопросы:
- Возможно ли решить задачу без аллоцирования памяти? (Стэк константного размера не считается)
- Какая минимально возможная сложность по времени (О-нотация)?

#interview #task #algorithms
🔥11👍6😨3
Простые лайфхаки

Часто встречаю в разных проектах подобные конструкции (на самом деле обычно они страшнее, но я убрал все лишнее):

var result = false;
if (Method1() == true) result = true;
if (Method2() == true) result = true;
if (Method3() == true) result = true;
...
return result;


На самом деле этот код можно записать проще:

var result = false;
result |= Method1();
result |= Method2();
result |= Method3();
...
return result;


Аналогично можно делать и обратную историю:

var result = true;
result &= ...


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

#lifehack #basics #code
🔥34👍7🤡7😐65
Удобное API

Вы наверняка сталкивались с такой проблемой:

void Method<T>(System.Action<T> callback) {...}

То есть мы хотим вызвать метод, который вернет нам колбэк в какой-то момент. Например, что действие завершено. Все вроде хорошо, но если мы начнем это использовать, то сталкнемся с другой проблемой:

Method<MyObj>(static (obj) => {
// И вот тут нам явно не хватает данных при static
});

Отсюда 2 выхода: либо мы используем аллокации при замыкании, либо реализуем некий второй параметр, в который мы сможем положить любые данные:


void Method<T1, T2>(T1 t1, System.Action<T1, T2> callback) {...}


С точки зрения метода будем считать, что T1 - это замыкание, которое мы передаем в метод:


Method<int, MyObj>(123, static (closure, obj) => { ... });


Вроде уже неплохо, но что если у нас вместо простого int будет что-то сложнее, например, кортеж ValueTuple:


Method<ValueTuple<int, int>, MyObj>((123, 234), static (closure, obj) => { ... });


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

И тут тоже есть элегантное решение:


struct ClosureAPI<TClosure> {
public TClosure data;
public MyContainer container;
public Method<T>(System.Action<T, TClosure> callback) {
this.container.Method(this.data, callback);
}
}

ClosureAPI<TClosure> Closure<TClosure>(TClosure data) {
return new ClosureAPI<TClosure>() {
data = data,
container = this,
};
}

void Method<T1, T2>(T1 t1, System.Action<T1, T2> callback) {...}


Таким образом снаружи это будет выглядеть как:

container.Closure((123, 234)).Method<MyObj>(static (data, obj) => { ... });


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

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

#zeroalloc #architecture #code
🔥22🤔74