[Из песочницы] Скрипты для редактора в 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); } }

a3d1b3e08f544f7fbacfbd7d46caccb8.png

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

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

Лично я применяю такой способ, когда нужно что-то сделать с группой объектов, например, выключить отбрасывание теней у всех детей объекта, у которых в названии встречается «withoutshadow»:

[ContextMenu («DisableCastShadows»)] private void DisabeCastShadows () { Renderer[] renderers = GetComponentsInChildren() .Where (x => x.name.Contains («withoutshadow»)) .ToArray (); foreach (var r in renderers) { r.castShadows = false; } } Вобщем способ хорош для одноразовых действий над кучей объектов — быстренько накидали нужный код в отдельном классе, кинули на нужный объект и тут же удалили этот класс к едрене фене.Теперь давайте решим следующую задачу: мы хотим запечь occlusion culling. Для этого нам необходимо пометить галочкой Occluder Static все объекты, которые бубдут загораживать другие объекты, и галочкой Occludee Static все, которые будут скрываться за Occluder`ами. То есть нам нужно вычленить все статичные объекты, непрозрачным объкетам поставить обе галки (на самом деле все), прозрачным — только Occludee, а Occluder выключить.

Казалось бы, ну что такого, пробежался по сцене ручками — расставил кому нужно галочки — и все. Но проблема в том, что объектов в сцене может быть много и в процессе развития проекта сцена скорее всего будет меняться. Следить самому за всеми этими галочками — с ума можно сойти. Поэтому мы напишем маленький скриптик, который делает это за нас.

Для начала напишем полезный код, который выполняет нашу работу, а далее оформим это в отдельный виззард:

7c7006dfab3040ce9221446f33a7cb9d.png

Задачи здесь две:

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-ти уровнях по очереди и получить скриншоты с результатом себе на почту.

© Habrahabr.ru