Comments 23
А можно подменять свойство, что приведет к автоматическому ускорению с использованием старого кода, например, так. В примере получается х2 ускорение + еще х2 ускорение (в результате получается х4 от исходного) если использовать кешированную переменную в классах-наследниках, потому что дергание свойства — это по сути вызов метода с вложенными проверками, которые можно убрать.
+2
Да, это вполне вариант для старого кода. Его производительность тоже рассмотрена в статье (вариант свойства с флагом).
Такой вопрос — а зачем вам дополнительная проверка _cachedTransform == null в Awake()?
И спасибо за участие в обсуждении)
Такой вопрос — а зачем вам дополнительная проверка _cachedTransform == null в Awake()?
И спасибо за участие в обсуждении)
0
Его производительность тоже рассмотрена в статье
Тут акцент был именно на слове «new» перед transform — мы переопределяем поведение этого свойства на свое — закешированное, нет необходимости переписывать старый код под CachedTransform, можно использовать «штатный» transform.
проверка _cachedTransform == null в Awake()
Об этом я уже написал:
если использовать кешированную переменную в классах-наследниках, потому что дергание свойства — это по сути вызов метода с вложенными проверками, которые можно убрать.
Те это что-то типа гарантии что трансформ закеширован на старте и дальше во всех методах этого класса и классов-наследников можно напрямую щупать _cachedTransform — будет еще двойное ускорение по сравнению с закешированным «штатным» transform.
Но тут есть тонкость — если компонент повесить на GO и выключить GO в редакторе до старта — awake не отработает пока GO не будет активирован. Те компонент вроде как и существует, но Awake не пройден. Те можно получить ситуацию с неопределенным _cachedTransform, если повесить компонент на выключенный GO + добавить его через инспектор в другой компонент на включенном GO и подергать паблик-методы, связанные с кешированным трансформом. Тут уже нужно самому следить за логикой поведения, либо игнорировать _cachedTransform и работать только через transform.
0
Еще 2 замечания по поводу репы на гитхабе:
1. Не указана лицензия использования. Ее отсутствие автоматически запрещает использование кода.
2. Примеры в описании ошибочны — нельзя трогать rigidbody в Update, только в FixedUpdate, не нужно учить плохому.
1. Не указана лицензия использования. Ее отсутствие автоматически запрещает использование кода.
2. Примеры в описании ошибочны — нельзя трогать rigidbody в Update, только в FixedUpdate, не нужно учить плохому.
+2
Что означает 0% в WebGL на графике «Сравнение производительности»?

Судя по графикам, получение свойства через
Судя по графикам, получение свойства через
GetComponent<T>()
в Unity5 настолько быcтрый, что значительно быстрее получения из словаря. Может, разница настолько мала, что нету смысла стараться писать всякие ужасные конструкции и остановится на лаконичности и надежности кода и оптимизировать другие вещи? Тем более, что ни один из более быстрых способов не проверяет компонент на существование. 0
Для WebGL цифры из профайлера довольно странные, на 1000 вызов тестовых методов c 0% занимает 0.00 ms, а если увеличивать количество вызовов, то это приводит к рандомным вызовам сборщика мусора, который ломает статистику. Так что сложно сказать, как это адекватно померить.
Да, так и есть, быстрее GetComponent только изначальное сохранение компонентов при инициализации или вообще не в рантайме, последние варианты именно об этом.
Конечно, тут нужно смотреть на конкретику и использовать профайлер — кэшировать данные обычно имеет смысл для объектов, которые часто создаются либо используются или их количество реально большое.
Да, так и есть, быстрее GetComponent только изначальное сохранение компонентов при инициализации или вообще не в рантайме, последние варианты именно об этом.
Конечно, тут нужно смотреть на конкретику и использовать профайлер — кэшировать данные обычно имеет смысл для объектов, которые часто создаются либо используются или их количество реально большое.
0
Для WebGL цифры из профайлера довольно странные, на 1000 вызов тестовых методов c 0% занимает 0.00 ms,
Может, вы замеряете каждый отдельный метод, а потом считаете их сумму? Что-то вроде:
loop {
start = now();
GetComponent(Class);
sum = now() - start();
}
Если использовать не
perfomance.now()
, а Date.now()
, то все, что меньше миллисекунды в целом будет считаться как 0. 0
И еще — в случае перекрёстных ссылок, получается, что где-то будет использоваться редактор, где-то аттрибут, где-то аксессор. Я могу ошибаться, но меня и моих коллег, вероятно, раздражало бы различная инициализация ссылок на одинаковые по смыслу объекты
0
Спасибо за статью! Правда хочу заметить что, Unity вроде как уже не имеет приставки 3D. Хотя трудно сказать, на сайте приписки 3д нет но есть в адресе. Интересно как юридически называется.
-2
На самом деле не трудно www.wipo.int/branddb/en/showData.jsp?ID=USTM.86128413
0
В каждом новом проекте приходится вычищать такие вот кеши, которые увеличивают сложность и ухудшают читаемость ради преждевременной оптимизации. А варианты с отдельным классом под кеши это еще и дополнительная связанность, даже в самой юньке наконец то догадались об этом и все кеши сделали обсолет.
0
Если бы была хотя бы попытка потестировать эти «кеши», то было бы понятно, что кешированием там и не пахло — по скорости оно было соизмеримо с постоянным GetComponent(typeof(T)). Юнитеки просто почистили апи от бесполезных свойств. Кеширование они прикрутили только к transform, но все-равно — ручное кеширование в несколько раз быстрее текущего «кешированного» варианта.
0
>>Например, в вашей игре есть пулемет, который стреляет пулями, каждая из которых является отдельным объектом (что само по себе неправильно, но это же сферический пример)
А почему неправильно? Я всегда использую абстрактный класс-скрипт с функцией SetDamage, от которого наследую и определяю SetDamage для каждого объекта, который может получить урон (соответственно, каждый объект и получает такой скрипт-потомок). Пуля же, сталкиваясь с объектом, просто вызывает эту функцию и урон наносится. Способ сам придумал, хотелось бы понять, почему так лучше не делать. Пули в пуле, если что, непонятно, почему им объектами быть не следует.
А почему неправильно? Я всегда использую абстрактный класс-скрипт с функцией SetDamage, от которого наследую и определяю SetDamage для каждого объекта, который может получить урон (соответственно, каждый объект и получает такой скрипт-потомок). Пуля же, сталкиваясь с объектом, просто вызывает эту функцию и урон наносится. Способ сам придумал, хотелось бы понять, почему так лучше не делать. Пули в пуле, если что, непонятно, почему им объектами быть не следует.
+1
Если не требуется отображать сами пули (то есть пользователь их не видит в силу скорости или этим можно пренебречь), то достаточно системы партиклей и использование Raycast. Если их нужно видеть в 2д (и чуть сложнее в 3д) — то также может быть достаточно рейкастов и проверки их коллизий (но тут нужно сравнивать производительность, не берусь сходу утверждать).
В любом случае, если требуется создавать и уничтожать объекты много и часто — нужно как минимум использовать пул.
В любом случае, если требуется создавать и уничтожать объекты много и часто — нужно как минимум использовать пул.
+1
Спасибо за статью. Почитать о таком тщательном взгляде на проблему (даже спустя три года) было любопытно. Особенно полезным оказался раздел про пользовательские атрибуты. Давно собирался разобраться в том, как их реализовывать.
Интересно насколько (в затратах времени на спаун объектов) поменялось быстродействие вашего приложения после добавления оптимизаций, описанных в статье. И насколько этот подход использовался в приложении дальше.
Интересно насколько (в затратах времени на спаун объектов) поменялось быстродействие вашего приложения после добавления оптимизаций, описанных в статье. И насколько этот подход использовался в приложении дальше.
0
Кстати, немного оффтоп — у себя в домашнем проекте использую самописный getComponent<>() на стероидах. Позволяет не только получить компонент, но создать его и/или проверить на наличие если он должен быть. Не смотря на некоторую громоздкость, семантически точно описывает ситуацию, впитывает в себя механические проверки и действия.
Код
class XUtils
{
// . . .
public enum AccessPolicy {
JustFind,
ShouldExist,
CreateIfNo,
ShouldBeCreated
}
public static T_Type getComponent<T_Type>(GameObject inGameObject,
AccessPolicy inComponentAccessPolicy = AccessPolicy.JustFind)
where T_Type : Component
{
T_Type theComponent = inGameObject.GetComponent<T_Type>();
if (theComponent) {
check(AccessPolicy.ShouldBeCreated != inComponentAccessPolicy);
} else {
switch(inComponentAccessPolicy) {
case AccessPolicy.CreateIfNo:
case AccessPolicy.ShouldBeCreated:
theComponent = inGameObject.AddComponent<T_Type>();
break;
case AccessPolicy.ShouldExist:
check(false);
break;
}
}
return theComponent;
}
// . . .
}
Пример использования
private void Awake() {
//. . .
rigidBody = XUtils.getComponent<RigidBody2D>(
this, XUtils.AccessPolicy.ShouldExist
);
//. . .
}
0
Only those users with full accounts are able to leave comments. Log in, please.
Особенности кэширования компонентов в Unity3D