[Из песочницы] Скрипты для редактора в Unity3D
Сегодня поговорим о том, как писать скрипты для Unity Editor. Статья рассчитана на тех, кто уже знаком с Unity3D, что-то успел сделать, но еще не решился попробовать писать скрипты для эдитора.Если коротко — то в режиме эдитора скриптами можно сделать абсолютно всё тоже самое, что и в режиме игры.
Начнем с простого. Допустим, мы хотим в режиме эдитора создать 10 кубиков и расположить их в линию. Для начала давайте упростим задачу и забудем про эдитор, давайте сделаем так, чтобы эти кубики создавались при старте приложения.
public class CreateCubes: MonoBehaviour {
// Use this for initialization void Start () { Create10Cubes (); }
private void Create10Cubes () { Vector3 position = new Vector3(0, 0, 0); for (int i = 0; i < 10; i++) { GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.transform.position = position; position += new Vector3(5, 0, 0); } } } Теперь попробуем выполнить этот код в режиме эдитора, для этого нужно к коду добавить всего лишь один волшебный атрибут [ContextMenu()] к функции Create10Cubes():
так чтобы код выглядел вот так:
[ContextMenu («CreateCubes»)] private void Create10Cubes () { Vector3 position = new Vector3(0, 0, 0); for (int i = 0; i < 10; i++) { GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.transform.position = position; position += new Vector3(5, 0, 0); } }
Теперь, если мы нажмем правой кнопкой по заголовку скрипта, там появится пункт CreateCubes, при нажатии на который функция точно также будет выполнена.
Важное замечание: функция, помеченная атрибутом ContextMenu, не должна иметь параметров, вернее, если у нее будут параметры, вы не сможете вызвать таким способом.
Лично я применяю такой способ, когда нужно что-то сделать с группой объектов, например, выключить отбрасывание теней у всех детей объекта, у которых в названии встречается «withoutshadow»:
[ContextMenu («DisableCastShadows»)]
private void DisabeCastShadows ()
{
Renderer[] renderers = GetComponentsInChildren
Казалось бы, ну что такого, пробежался по сцене ручками — расставил кому нужно галочки — и все. Но проблема в том, что объектов в сцене может быть много и в процессе развития проекта сцена скорее всего будет меняться. Следить самому за всеми этими галочками — с ума можно сойти. Поэтому мы напишем маленький скриптик, который делает это за нас.
Для начала напишем полезный код, который выполняет нашу работу, а далее оформим это в отдельный виззард:
Задачи здесь две:
1) Найти интересующие нас объекты в сцене;2) Расставить нужные галочки.
Оформим код в виде отдельной команды, для того чтобы его можно было вызывать из любого места и он не зависел от того, в каком виззарде он будет вызываться. Внимание: файл, содержащий следующий код, необходимо поместить в папку под названием Editor, это нужно для того, чтобы этот код не попал в основной билд:
using UnityEngine; using UnityEditor; using System.Linq;
public class SetStaticOclluderFlagsCmd { private const int TransparentRenderQueue = 3000; public void Execute () { var scene =Object.FindObjectsOfType (typeof (GameObject)); var transparents = scene.Where (o =>IsStatic (o) && IsTransparent (o)).ToArray (); var occluders = scene.Where (o => IsStatic (o) && ! IsTransparent (o)).ToArray (); SceneModeUtility.SetStaticFlags (transparents, (int)StaticEditorFlags.OccluderStatic, false); SceneModeUtility.SetStaticFlags (transparents, (int)StaticEditorFlags.OccludeeStatic, true); SceneModeUtility.SetStaticFlags (occluders, (int)StaticEditorFlags.OccluderStatic, true); SceneModeUtility.SetStaticFlags (occluders, (int)StaticEditorFlags.OccludeeStatic, true); Debug.Log («SetStaticOclluderFlagsCmd done»);
} private bool IsStatic (Object obj) { GameObject gameObject = obj as GameObject; if (gameObject == null) return false; return GameObjectUtility.AreStaticEditorFlagsSet (gameObject, StaticEditorFlags.BatchingStatic); }
private bool IsTransparent (Object obj) { GameObject gameObject = obj as GameObject; if (gameObject == null ||gameObject.renderer == null) return false; return gameObject.renderer.sharedMaterials.Any (x => x.renderQueue == TransparentRenderQueue); } } Здесь мы предполагаем, что статичные объекты уже до этого каким то образом нашли (скорее всего ручками) и отметили их галочкой Static, а значит, в том числе и BatchingStatic.
По поводу реализации метода IsTransparent (). Я не силен в написании шейдеров и не могу гарантировать, что такая реализация корректна, могу сказать только что на стандартных шейдерах работает. Если кто подскажет более корректый способ реализации я обязательно его заменю
Теперь давайте оформим отдельный виззард, чтобы можно было удобно вызывать эту команду:
Тут нам пригодится класс EditorWindow.
using UnityEngine; using UnityEditor; public class OcclusionCullingUtilites: EditorWindow {
[MenuItem («Window/OcclusionCullingUtilites»)]
static void Init ()
{
GetWindow
void OnGUI () { if (GUILayout.Button («SetStaticFlags»)) { SetStaticOclluderFlagsCmd cmd = new SetStaticOclluderFlagsCmd (); cmd.Execute (); } } }
На этом пока закончим наш обзор, он получился далеко не полным.
В следующих статьях я планирую описать, как можно создавать кастомные инспекторы для ваших классов, как вмешиваться в процесс импорта ассетов, как можно поставить запекать тени на 20-ти уровнях по очереди и получить скриншоты с результатом себе на почту.