Розширення для Unity editor: базовий сетап проєкту

Всім привіт! Кожен з нас при старті проєкту постійно створює один і той же набір папок, файлів налаштувань та об’єктів на сцені. Можливо, це ваша власна ініціатива, а може є така домовленість всередині компанії.

Сьогодні я хочу поділитися невеличкою тулзою, яку кожен може зробити та підлаштувати під свої потреби, щоб назавжди забути про рутинні кліки та перетягування, які повторюються від проєкту до проєкту, а часом і від сцени до сцени. Починаємо!

*Традиційний дизклеймер: рішення та код можуть бути не найкращими, тож для когось це буде гайд, а для когось план «як робити не треба». Також головна ціль топіка — показати, що і як просто можна створити засобами юніті. Тож не судіть строго)*

Отже, розробка будь-чого починається з цілі: створити розширення для едітора, яке дозволить одним кліком сетапити дефолтну структуру проєкту. Для мене це чотири папки: «Scripts», «Images», «Scenes» та «Models». Також я тримаю саму сцену у певній ієрархії. Зазвичай вона виглядає ± так: «Managers», «Canvases», «Cameras», «Environment» і «Scene». Легко побачити, що кожен об’єкт має своє місце чайлдом одного з цих.

Тож, почнемо писати код!

Перш за все, ми повинні створити клас, який буде хендлити всі функції:

namespace CustomToolsMenu

{

    public static class ToolsMenu

    {

    }

}

So far so good. Далі додамо функції, які створюватимуть папки:

[MenuItem("Tools/Setup/Create default folders")]
public static void CreateDefaultFolders()
{
    CreateDirectories("Scripts", "Images", "Scenes", "Models");
    Refresh();
}

private static void CreateDirectories(params string[] directories)
{
    string fullPath = dataPath;
    
    foreach (string newDirectory in directories)
    {
        CreateDirectory(Combine(fullPath, newDirectory));
    }
}

Тож, функція CreateDefaultFolders є частиною стандартної менюшки юніті:

Вона викликає функцію-хелпер CreateDirectories та оновлює юнітівську структуру за допомогою UnityEditor.AssetDatabase. Refresh(). Зупинимося на CreateDirectories — це саме та функція, у які відбувається магія.

За допомогою UnityEngine.Application.dataPath ми отримуємо шлях до нашої папки Assets та зберігаємо його у змінній. Наступним кроком проходимося по масиву directories і через System.IO.Path.Combine отримуємо повний шлях до нової папки, а за допомого System.IO.Directory.CreateDirectory створюємо папку. Ось і вся магія, яка поміщається у 16 строк :)

Тепер поговоримо про структуру сцени. Нам треба не просто створити об’єкти, а ще додатково перевірити, чи існують вже інші з такою ж назвою на сцені.

Отже, аналогічним методом створюємо функцію у вкладинці Unity:

[MenuItem("Tools/Setup/Create default scene setup")]
public static void CreateDefaultSceneSetup()
{
    CreateSceneObjects("Managers", "Canvases", "Cameras", "Environment", "Scene");
    ParentDefaultObjects();
}

Тепер пропоную роздивитися CreateSceneObjects:

private static void CreateSceneObjects(params string[] names)
{
    foreach (string name in names)
    {
        if (FindDuplicates(name))
            continue;

        GameObject temporary = new GameObject($"---{name}---");
        temporary.transform.position = Vector3.zero;
    }
}

private static bool FindDuplicates(string name)
{
    GameObject duplicatedGameObject = GameObject.Find($"---{name}---");
    return duplicatedGameObject != null;
}

Аналогічним до створення папок чином ми проходимо всіма параметрами. Далі за допомогою FindDuplicates перевіряємо, чи є вже такий об’єкт на сцені, і створюємо контейнер чи пропускаємо ітерацію.

Загалом пошук за ім’ям краще ніколи не використовувати, але ми викликатимемо цю функцію в едіторі лише один раз під час створення сцени, тож кращого рішення я не знайшов. Далі ми онуляємо позицію об’єкту, і все!

Вже можна радіти: Unity створює об’єкти на сцені та у папках за нас. Але для мене це було замало, тож я почав шукати рішення: як рухати вже існуючі об’єкти, типу камери та світла, у створені контейнери.

Копаючись у величезному кількості матеріалів та власних ідей, я зміг придумати лише наступне:

private static void ParentDefaultObjects()
 {
    Camera mainCamera = Camera.main;

    if (mainCamera != null) 
        mainCamera.transform.parent = GameObject.Find("---Cameras---").transform;

    Light globalLight3D = GameObject.FindObjectOfType<Light>();

    if (globalLight3D != null) 
        globalLight3D.transform.parent = GameObject.Find("---Environment---").transform;
}

Отже, за допомогою класу Camera ми можемо отримати головну камеру, далі знайти на сцені відповідний контейнер та виставити parent.

Проте для світла ми так не можемо зробити. Тим паче для 2D. Зітхнувши та змирившись з помилками у консолі я повернувся до GameObject.FindObjectOfType — знаходимо його за допомогою об’єкту з компонентом Light, і переносимо до контейнера Environment.

Тепер ми маємо повністю готове розширення, яке виконує всю рутинну роботу за нас! Проте не працює для Light2D :(

Тепер сюди можна додати assembly definition, package.json з базовою інформацію про цю тулзу, перекинути у будь-яку папку та імпортувати в Unity як package. Це дозволить переносити розширення між проєктами та мати можливість редагувати його, за необхідності.

На цьому на сьогодні все, діліться власними варіантами у коментарях. До зустрічі!

Підписуйтеся на Telegram-канал @gamedev_dou, щоб не пропустити найважливіші статті і новини про геймдев

👍ПодобаєтьсяСподобалось2
До обраногоВ обраному2
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Підписатись на коментарі