Unity3D tips and tricks 2


    После невероятного успеха первой части (22 человекам понравилось и было целых 9 комментов, 3 из которых мои) — автор (я) решил написал продолжение повести о захватывающих приключениях в мире Unity3d.

    Постарался подобрать свеженькие фишки.

    1. Easy Buttons

    Смысл хинта (кратко): Можно 1 строкой добавить вызов любого метода из редактора. При выделении объекта в редакторе появляется кнопка с названием метода, при клике на неё — метод выполняется.

    Скачиваем, закидываем в проект, добавляем в коде:

    image

    Видим в редакторе:

    image

    После нажатия — результат выполнения:

    image

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

    Код можно скачать здесь.

    2. Conditional Hide

    Смысл: можно легко скрыть ненужные поля. Например у вашего объекта только при использовании самонаведения надо задавать параметры радиус и тп. В остальных случаях их надо скрыть (или задисаблить). Это очень легко сделать.

    image

    [Header("Auto Aim")]
    public bool EnableAutoAim = false;
    
    [ConditionalHide("EnableAutoAim", true)]
    public float Range = 0.0f;
    [ConditionalHide("EnableAutoAim", true)]
    public bool AimAtClosestTarget = true; 
    [ConditionalHide("EnableAutoAim", true)]
    public DemoClass ExampleDataClass = new DemoClass();
    [ConditionalHide("EnableAutoAim", true)]
    public AnimationCurve AimOffsetCurve = new AnimationCurve();

    Теперь поля будут показываться только при соблюдении условия EnableAutoAim. Можно так же не скрывать, а просто дисаблить поля, а так же использовать 2 условия.

    Благодарности этому парню, там же можно скачать исходный код атрибута.

    Штука очень удобная, упрощает процесс редактирования.

    Обращу внимание, что первый и второй хинты работают по разному (первый перекрывает CustomEditor для всех объектов, что в некоторых случаях не очень хорошо, второй же CustomPropertyDrawer для своего аттрибута).

    3. Скриншоты под разные разрешения одной кнопкой

    Стандартные средства Unity для скриншотов довольно унылы — просто делает скриншот текущего разрешения. Но ведь сторам надо кучу скринов, для разных разрешений, разных соотношений сторон (16:9, 4:3, еще какие-то), еще и с какого-нибудь iPad pro с большим разрешением. Свежий пример — пару недельной давности — надо было такие:

    3.5 Inch (iPhone 4): 640 x 960 px
    4 Inch (iPhone 5): 640 x 1136 px
    4.7 Inch (iPhone 6): 750 x 1334 px
    5.5 Inch (iPhone 6 Plus): 1242 x 2208 px
    iPad (9.7 + 7.9 Inch): 2048 x 1536 px
    iPad Pro (12.9 Inch): 2732 x 2048

    Под рукой такого зоопарка девайсов обычно не бывает. Выход — простенький скриптик — где создается текстура, туда рендериться камера (или камеры), и сохраняется.

    Взять можно тут. А тут пример как переделать на много камер.

    Теперь вы владеете мастерством создания скриншотов одной кнопкой.

    4. Отправка LogError в Performance Reporting

    Предистория: раньше был сервис Parse.com, если в игре возникала ошибка (не Exception, а что нибудь мелкое — например нет спрайта для оружия, или что-то еще), то в кода вызывался Debug.LogError(). Он перехватывался, отправлялся на Parse.com, там заносился в базу, если было много ошибок — скрипт сразу заводил баг-рипорт, чтобы мы обратили внимание. Но ⊂(° ʖ̯°)⊃ из Facebook купили parse и закрыли его, обломав кучу народу. Программе стало некуда отправлять рассказы о неприятностях, которые произошли с ней.

    Есть всякие решения (отправлять такие вещи в статистику, использовать еще что-то) — но для любителей велосипедов предлагаю такое решение — отправлять в Performance Reporting (тем более что это самое логичное место где смотреть какие ошибки есть в программе).

    Итак, в Unity есть Performance Reporting — но туда попадают только Exceptions. LogError не попадает. Если создавать Exception вместо LogError — то да, ошибка отправится и мы её увидим в Performance Reporting, но прекратиться дальнейшее выполнение скрипта. А там может особо ничего криминального и можно спокойно выполнять его дальше. Если создать Exception и самому его отловить — то скрипт не сыпанется (вы это отловите), но и в Performance Reporting ничего не будет отправлено, и мы про это не узнаем. Выход, который я предлагаю — подцепиться на LogError, при получении ошибки создать исключение в другом классе (пусть сыпется), тогда ошибка будет отправлена в Performance Reporting. Собственно код:

    Код
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class ErrorHandler : MonoBehaviour {
    
        public string output = "";
        public string stack = "";
    
    	void Awake() {
    		DontDestroyOnLoad(gameObject);
    	}
    
        void OnEnable() {
            Application.logMessageReceived += HandleLog;
        }
    
        void OnDisable() {
            Application.logMessageReceived -= HandleLog;
        }
    
        void HandleLog(string logString, string stackTrace, LogType type) {
    		if (type == LogType.Error) {
            	output = logString;
            	stack = stackTrace;
    			Invoke("ThrowException", 0f);
    		}
        }
    
    	public void ThrowException() {
    		throw new Exception(output + "\n" + stack);
    	}
    }
    

    Да, код далек от идеала и делался на скорую руку как замена парсу, со своими обязанностями вроде справляется. То, что при куче ошибок подряд теоретически может работать не совсем верно — понимаю, но на практике такого пока не было.

    На загрузочной сцене сделайте объект, на него повешайте этот скрипт. Он сам пометит объект чтобы он не удалялся при загрузке сцен, и подпишется на Debug. При получении LogError скрипт создаст исключение, которое Unity отправит в Performance Reporting. Где вы о нем и узнаете.

    Спасибо тем кто дочитал до конца. Надеюсь что-то из приведенного пригодится.

    Комментарии 16

      0
      Про Button не знал, прикольно.

      Со скриншотами есть способ удобнее. Просто через LateUpdate не так удобно это всё делать, лучше, на мой взгляд делать это корутиной c yield return new WaitForEndOfFrame();

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

        а что, кроме parse BaaS больше нету? firebase вот мелькает, была даже какая-то штука заточенная под игры (кто напомнит название)?

          0
            0
            Да я знаю что есть альтернативы, сам пересмотрел кучу — пробовали PlayFab и другие, выбрали brainCloud в итоге. Просто PlayFab (да и brainCloud) не удобны для такой задачи что я описал. Они заточены на то, чтобы хранить данные каждого пользователя, я же не буду по профилям лазить и искать у кого что-то сбойнуло. Для этого я сделал то что описал выше.
              0

              Понял. Я просто думал там есть какое-то общего назначения решение для логов.

          0

          Реакция на КДПВ: А если обозвать myDick как long double, длиннее станет или нет?


          Про скриншоты: Мне казалось, что если у нас есть сцена, то и скриншот это фактически рендер сцены на битмап, и не более того. Отсюда всего один шаг до создания кастомного битмапа для рендера на него сцены. Или просто юнити это такой тридэ, который забыл уже, что бывают плоские битмапы? :)

            0
            Картинка из прошлой статьи, там уже обсудили что важно значение которое записано в переменной.

            Ну там скрипт — создать текстуру нужного размера, отрендерить туда (одну или все камеры), сохранить. Все это по нажатию кнопки.
              0
              Вспомнился баян:
              double penetration;
              
              0
              Вместо [Button] можно использовать встроенный [ContextMenu («Do Something»)], хотя кнопку нажать быстрее.
                0
                Да, можно конечно.
                0
                А еще для скриншотов можно затолкать нужные разрешения в конфиги окна «game» — юнити использует активный. Правда публичного апи для этого нет, но когда это останавливало? Я сделал так, в результате одной кнопкой снимаются все шоты разом в отдельную папку.
                  0
                  А как к примеру iPad Pro (12.9 Inch): 2732 x 2048 получить? У меня разрешение на экране ниже, вероятно вашим способом не получится. Или можно как-то?
                    0
                    А если попробовать с нужным разрешением? Если честно, не могу сказать, потому что 4к монитор и все влазит. Теоретически, Application.CaptureScreenshot умеет делать шоты с коэффициентом увеличения, те делает это через рендер в текстуру. Т.е. вроде как не должно стать проблемой разрешение больше текущего на активном мониторе.
                      0
                      Попробовал — работает, по крайней мере на macos. Пушнул в develop небольшой фикс с разрешением iPadPro 12.9".
                        0
                        Необходим 4K монитор, или можно на ноуте с более низким разрешением получить? Если второе — то отлично, значит еще один способ получения скринов есть.
                          0
                          Я выставлял принудительно 1920х1080 — снимки прошли корректно даже для iPadPro13".

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

                  Самое читаемое