Использование вложенного класса в качестве компонента Unity3D

Я использую Unity 5.4.2f2 Personal, и я написал скрипт на C#, подобный следующему:

using UnityEngine;
using System;

public class Outer : MonoBehaviour {

    // Some public outer fields

    public class Inner : MonoBehaviour {
        // Some public inner fields
    }

}

Я хотел бы иметь возможность прикрепить оба Outer а также Inner в GameObjects в моей сцене. К несчастью, Inner не отображается как доступный скрипт в Unity Inspector, даже если я добавлю [Serializable] к этому. Я знаю, что многие люди, вероятно, скажут, что с моей настройкой что-то не так, если я попытаюсь сделать вложенный класс видимым для внешнего мира, но ради этого вопроса я просто хочу знать, можно ли это сделать. Т.е. есть ли способ сделать вложенные классы пригодными для использования в качестве компонентов Unity?

5 ответов

Решение

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

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

[РЕДАКТИРОВАТЬ]

Я нашел документацию. И как я уже сказал: "Имя класса и имя файла должны быть одинаковыми, чтобы позволить компоненту сценария быть присоединенным к GameObject".

Общепринятый ответ является неправильным !

По крайней мере, это только полуправда:

Хорошо, да, это правда, если ваша цель - использовать и добавить этот компонент с помощью Add Componentкнопки инспектора , тогда НЕТ, он должен находиться в отдельном файле сценария с соответствующим именем класса!

Однако , если вы добавляете этот компонент только во время выполнения через скрипт, тогда ДА, вы действительно можете это сделать!

Сама Unity делает это, например, в Dropdown компонент (отрывок из исходного кода, декомпилированный с помощью Rider)

       public class Dropdown : Selectable, IPointerClickHandler, ISubmitHandler, ICancelHandler
{
    protected internal class DropdownItem : MonoBehaviour, IPointerEnterHandler, ICancelHandler
    {
        ...
    }
    
    ...
}

( Selectable наследуется от UIBehaviour который наследуется от MonoBehaviour)

Что позже во время выполнения выглядит так в Инспекторе из-за этого вложения

но однозначно возможно!

Чтобы доказать это, вы можете легко сделать то же самое, например

      public class TEST : MonoBehaviour
{
    private class NESTED : MonoBehaviour
    {
        [SerializeField] private int someInt;
        [SerializeField] private Transform someReference;
    }

    [ContextMenu("Add NESTED component")]
    private void AddNested()
    {
        gameObject.AddComponent<NESTED>();
    }
}

так что, как вы можете видеть даже во время редактирования вне режима воспроизведения, я могу просто добавить этот «скрытый» компонент через контекстное меню

и он все еще сериализуется, а также остается там после закрытия и перезагрузки сцены:

Я многому научился с тех пор, как задал этот вопрос, поэтому, думаю, добавлю дополнительную информацию. еще технически правильный, поэтому я Ответ @luizcarlosfx всеоставлю его как принятый.

Как Unity компилирует скрипты

Unity компилирует все сценарии в проекте, используя встроенную версию компилятора Mono (кроссплатформенный компилятор C#). Он присваивает уникальный GUID каждому файлу сценария (в связанном .metafile) так же, как и для любого другого ресурса. Это связано с некоторыми приятными улучшениями удобства использования: классы можно переименовывать без нарушения существующих ссылок на скрипты, потому что GUID не меняется, а скрипты можно группировать вместе в папках с другими связанными активами (например, все скрипты / звуки / текстуры «солдата» могут быть сгруппированы вместе).

Ограничения на именование скриптов Unity

Обратной стороной такого удобства использования является то, что Unity применяет некоторые ограничения именования:

  • Только один класс на файл с тем же именем, что и файл, потому что в противном случае Unity не знала бы, какой GUID использовать в ссылках на скрипты компонентов.
  • Нет вложенных классов
  • Классы не названы TheBehaviour в файле с именем MyBehaviour.cs
  • Наверное, нет классов (не тестировал).

Однако эти ограничения применяются только к общедоступным классам, производным от (или, возможно, от UnityEngine.Object?). Вы можете иметь в файле столько других частных или POCO, сколько хотите, потому что Unity никогда не будет сериализовать их, поэтому им не нужен собственный GUID. Общедоступные POCO, отмеченные значком [Serializable] могут иметь те же ограничения именования, что и общедоступные MonoBehaviours ... Я не тестировал это, поэтому я не уверен. Почему я не проверял это? Хорошо...

Как избежать этих ограничений с помощью управляемых плагинов

Если вы похожи на меня и имеете чистый опыт работы с .NET, то любые ограничения на то, какие классы могут быть определены в каких файлах, вероятно, вас раздражают. В этом случае вы можете хранить свои скрипты вне проекта Unity (я использую другую папку в том же репозитории, что и проект Unity) и создавать их вручную с помощью IDE, например Visual Studio или JetBrains Rider. Сгенерированные сборки (файлы .dll и .pdb) затем можно скопировать в ваш проект Unity (в идеале с помощью некоторого автоматического события после сборки) в качестве управляемых плагинов . Затем вы можете:

  • Определите столько классов для каждого файла, сколько хотите
  • Используйте любые соглашения об именах, которые хотите
  • Используйте вложенные классы и partial классы, какие угодно, такие же, как чистый .NET
  • Напишите модульные тесты для ваших POCO, которые не полагаются на Unity или Unity Test Framework (у которого есть замечательное побочное преимущество, позволяющее запускать модульные тесты параллельно; Unity может запускать только один тест за раз)
  • Вы даже можете использовать любую версию C#, которую хотите (хотя для некоторых языковых функций требуются функции среды выполнения, которых нет в версии Mono для Unity). Ограничения версии C# в Unity 5.x на самом деле именно то, что заставило меня в первую очередь заняться этим вариантом!

Это определенно более низкоуровневый подход к написанию сценариев, который требует большего знания C#, CLR, MSBuild и т.п. Однако, если вы не боитесь запачкать руки, то у Эшли Дэвиса есть отличная статья о настройке Visual Studio для сгенерированных управляемых плагинов и о плюсах и минусах этого подхода. Вы также можете использовать отличный пакет Unity3D NuGet для создания кроссплатформенных и перспективных ссылок на сборки Unity в проектах исходного кода.

Надеюсь, что это проясняет ситуацию, и еще раз спасибо за предыдущие ответы / комментарии!

Вы не можете добавить его через стандартные меню Unity. Но вы можете добавить это через скрипт.AddComponent<Внешний.Внутренний>();

Я не верю, что предыдущий ответ правильный.

Вы должны иметь возможность добавлять компонент, который является вложенным, создав сценарий редактора статическим методом, используя атрибут [MenuItem] для создания и привязки вашего MonoBehaviour.

Это работало в Unity 5 и выше.

Другие вопросы по тегам