7/24/2014

Почему я не использую SendMessage() в Unity

Дабы не быть голословным, я решил в поддержку моего предыдущего поста провести некоторые тесты и рассказать: "Почему я не использую SendMessage() в Unity. Нет, в нем нет ничего плохого, я не хочу никого учить, просто выскажу свое мнение на этот счет.
Поехали!


Ранее я заявлял, что не стоит в своих скриптах использовать SendMessage() и предложил свой вариант обращения к методам объектов игровой сцены, аргументируя это тем, что  SendMessage() очень уж медленный и небезопасный. Так а теперь обо всем по-порядку.
SendMessage() - ОЧЕНЬ! медленный.
Звучит не убедительно, не так ли? Ну чтож давайте проверим =)
Напишем небольшой тестик на основе скриптов из моего предыдущего поста.
       
/*
    IClickable.cs
*/
public interface IClickable
{
    /*
        Этот метод будет нашим подопытным, его
        мы и будем вызывать миллионы-миллионы раз!
    */
    void Active();
    
    /*
        А этот будет выводить на печать количество
        вызванных функций.
    */
 void Print();
}
Попробую писать компактней и убирать все лишнее.
       /*
    ClickableObjects.cs
*/
using UnityEngine;

public class ClickableObjects : MonoBehaviour, IClickable
{
    public virtual void Active(){}
 public virtual void Print(){}
}
Интерфейсы и классы предназначенные в прошлом посте для большей наглядности мы опустим. Теперь никакого IDragable и DragWindow.
       
/*
    SimpleButton.cs
*/
using UnityEngine;

public class SimpleButton : ClickableObjects
{
    private int iterations = 0;

 public override void Active()
 {
  this.iterations++;
 }
 public override void Print()
 {
  Debug.Log(iterations.ToString());
  iterations = 0;
 }
}
И, наконец, скрипт нашей камеры в котором и будет реализован алгоритм проверки.
       
/*
    GuiCamera.cs
*/
// заккоментировать Define в случае тестирования GetComponent()
#define SEND_METOD
using UnityEngine;

[ExecuteInEditMode]
[RequireComponent (typeof(Camera))]
public class GuiCamera : MonoBehaviour
{
    private Ray ray;
 private RaycastHit hit;
 private IClickable obj;
 private GameObject obj_send;
 private float dt = 0;

 private void Awake()
 {
  Application.targetFrameRate = 2000;
  QualitySettings.vSyncCount = 0;
 }

 private void Update()
 {
  dt+=Time.deltaTime;
  ray = this.camera.ScreenPointToRay(Input.mousePosition);
  if (Physics.Raycast(ray, out hit, 200f))
  {
   Metod ();
  }
 }

 private void Metod()
 {
  for(int i = 0; i < 20000; i++)
  {
   #if SEND_METOD
    SendMetod();
   #else
    GetMetod();
   #endif
  }
 }
 /* 
        для SendMessage() пришлось подопытным 
        объектам присвоить тег "Clickable",
        ато возникнут эксепшины, которые совсем
        нам не нужны.
     */
 private void SendMetod()
 {
     if(hit.transform.gameObject.tag == "Clickable")
     {
   obj_send = hit.transform.gameObject;
   if(obj_send != null)
   {
    obj_send.SendMessage("Active");
    if(dt>=1)
    {
     obj_send.SendMessage("Print");
     dt = 0;
    }
   }
  }
 }
 /*
  метод описанный в моей педыдущей заметке
  */
 private void GetMetod()
 {
  obj = (IClickable)hit.transform.GetComponent(typeof(IClickable));
  if(obj != null)
  {
   obj.Active();
   if(dt>=1)
   {
    obj.Print();
    dt = 0;
   }
  }
 }
}
Теперь собираем тестовый "агрегат". Создаем новый Unity проект. Удаляем камеру, кидаем на сцену EmptyObject и называем его TestCamera, к примеру. Меняем настройки камеры на необходимые нам. Добавляем на сцену 2 кубика и кидаем на них BoxCollider'ы. На один из кубиков кинем наш скрипт SimpleButton.cs и не забываем добавить тег "Clickable" и присвоить его этому кубику. Вроде бы всё! Теперь можно запускать тесты.Сначала для SendMessage():
780 000 - 820 000 вызовов за 1 секунду в редакторе. Неплохо в общем-то!
Закомментируем наш #define SEND_METOD и проверим результаты для GetComponent < T > ().
2 820 000 - 2 880 000 вызовов за 1 секунду! Хех, разница чувствуется.
А теперь, еще маленький эксперимент: создаем 5 клаасов c разными именами и кинем их на наш кубик. А теперь проверим еще раз:
660 000 - 680 000 вызовов для SendMessage().
Для GetComponent < T >() количество вызовов остается прежним, ну это итак понятно.
А теперь создадим еще один скриптик:
       /*
    TestScript.cs
*/
using UnityEngine;

public class TestScript : MonoBehaviour 
{
    void Active(int a) {}
}
Если его кинуть на наш кубик, то редактор зависнет =) Так что лучше этот эксперимент не повторять. Думаю коментарии излишни! Вот, наверное, поэтому я и не использую SendMessage(). Пересчитывать скорость вызова в миллисекунды не буду, думаю итак понятно как это сделать. И вообще мои тесты могут показаться спецефическими, не говоря уже о том, что я не приводил показатели на мобильных устройствах. Хотя там картина еще более очевидна. Всем спасибо за внимание!

Комментариев нет:

Отправить комментарий