Могу ли я # определить постоянное решение в рамках кода C # без настроек проекта? - PullRequest
0 голосов
/ 26 сентября 2018

Я знаю, что на это ответили несколько раз, например, для всего решения # define , Есть ли способ #define Constant на основе решения? и Какдля определения константы глобально в C # (например, DEBUG) .

Но в моем случае я не могу использовать ни один из предложенных методов:

Я пишу на разных «модулях»(или плагины, если хотите) для UnityProjects (вид пакета, обеспечивающего определенную функциональность).Идея состоит в том, что разработчик может загрузить определенный «модуль» для использования в своем проекте, импортировав UnityPackage со всеми сценариями и ресурсами в нем.

Но некоторые из этих модулей сами зависят от других модулей.Итак, что я попробовал, так это наличие класса Constants в каждом модуле с разделенными пространствами имен и определениями препроцессора.

Модуль A

#if !MODULE_A
#define MODULE_A   // BUT I WOULD NEED THIS GLOBAL NOT ONLY HERE
#endif

    namespace Module_A
    {
        public static class Constants
        {
            // some constants for this namespace here
        }
    }

МодульB

#if !MODULE_B
#define MODULE_B    // BUT I WOULD NEED THIS GLOBAL NOT ONLY HERE
#endif

#if !MODULE_A    // WILL BE NOT DEFINED OFCOURSE SINCE #define IS NOT GLOBAL
#error Module A missing!
#else

    namespace Module_B
    {
        public static class Constants
        {
            // some constants for this namespace here
        }

        // and other code that might require Module A
    }
#endif

Но, конечно, это не может работать так, поскольку #define s не глобальные, а только в текущем файле.

Задача

Для этогоСама идея модулей и простая «загрузка ваших модулей». Я не могу попросить пользователя сначала внести изменения в настройки проекта или решения, как, например, предложено в этом ответе , но вместо этого нужно использовать только (c #)ресурсы, которые импортируются с UnityPackage (по крайней мере, с моими текущими ноу-хау).

Есть ли способ как-то установить / определить эти константы для всего Unity-Project, импортировав только модуль UnityPackage?


Редактировать:

Я мог бы найти решение для определения 1 в Unity, используя Assets / msc.rsp .Но это все равно не сработает для нескольких модулей, так как им придется записывать в один файл.

Ответы [ 2 ]

0 голосов
/ 26 октября 2018

После многих поисков я наконец смог собрать удивительно простое решение, которым я хотел бы поделиться с вами:


InitializeOnLoad

У Unity есть атрибут [InitializeOnLoad].Он говорит Unity инициализировать соответствующий класс, как только

  • Unity запускается
  • После любой повторной компиляции сценариев => также после импорта нового пакета Unity со сценариями

статический конструктор

В их Запуск кода редактора при запуске они показывают, как объединить это с конструктором static.

С статические конструкторы :

Статический конструктор вызывается автоматически для инициализации класса перед созданием первого экземпляра или ссылками на любые статические члены.

Хотя обычно вам все равно придется создавать экземпляр класса, статический конструктор мгновенно «запускается / выполняется», когда класс инициализируется, что мы принудительно используем с помощью атрибута [InitializeOnLoad].


Определение сценариев символов

Дальнейшее Unity фактически имеет широких определений проекта в PlayerSettings.

enter image description here

И хорошая часть: у нас также есть доступ к ним через API сценариев:


Итак, что я сделал сейчас, это следующее

Модуль A

Этот модуль не имеет зависимостей, а просто определяет "глобальный"определить "в PlayerSettings.Я поместил этот скрипт где-нибудь, например, в Assets/ModuleA/Editor (важно имя последней папки).

using System.Linq;
using UnityEditor;

namespace ModuleA
{
    // Will be initialized on load or recompiling
    [InitializeOnLoad]
    public static class Startup
    {
        // static constructor is called as soon as class is initialized
        static Startup()
        {
            #region Add Compiler Define

            // Get the current defines
            // returns a string like "DEFINE_1;DEFINE_2;DEFINE_3"
            var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
            // split into list just to check if my define is already there
            var define = defines.Split(';').ToList();

            if (!define.Contains("MODULE_A")
            {
                // if not there already add my define 
                defines += ";MODULE_A";
            }

            // and write back the new defines
            PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, defines);

            #endregion
        }
    }
}

Модуль B

Этот модуль зависит от Module A.Таким образом, он сам определяет «глобальное определение» (поэтому более поздние модули могут проверять свои зависимости на Module B), но дополнительно он сначала проверяет, импортирован ли модуль A.Если Module A отсутствует, он выводит сообщение об ошибке на консоль отладки.

(Вы также можете выдать ошибку компилятора, используя #error SOME TEXT, но по какой-то причине это не может правильно распечатать URL, поэтому я решил для Debug.LogError)

Iпоместил этот скрипт где-нибудь, например, в Assets/ModuleB/Editor

#if MODULE_A
using System.Linq;
#endif
using UnityEditor;
#if !MODULE_A
using UnityEngine;
#endif

namespace ModuleB
{
    // Will be initialized on load or recompiling
    [InitializeOnLoad]
    public static class Startup
    {
        // static constructor is called as soon as class is initialized
        static Startup()
        {
#if !MODULE_A
            Debug.LogErrorFormat("! Missing Module Dependency !" +
                                 "\nThe module {0} depends on the module {1}." +
                                 "\n\nDownload it from {2} \n",
                "MODULE_B",
                "MODULE_A",
                "https://Some.page.where./to.find.it/MyModules/ModuleA.unitypackage"
                );
#else
            // Add Compiler Define

            var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
            var define = defines.Split(';').ToList();

            if (!define.Contains("MODULE_B"))
            {
                defines += ";MODULE_B";
            }

            PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, defines);

#endif
        }
    }
}

Так что позже в других скриптах Модуль B У меня есть два варианта (оба делают в основном то же самое)

  • Я могу либо проверить везде #if MODULE_A, чтобы точно проверить модуль, на который опирается этот скрипт

  • , либо вместо этого можно проверить #if MODULE_B, чтобы скорее проверить с однимстрока, если все зависимости выполнены, так как в противном случае я не определяю MODULE_B.


Таким образом, я могу полностью проверить все зависимости между определенными модулямиэто потрясающеЕдинственные два недостатка, которые я видел до сих пор:

  • Мы должны знать как выглядит определение (например, MODULE_A) для каждого модуля и изменяется ли оно вв будущем его также необходимо изменить во всех зависимых модулях
  • «Глобальное определение» не удаляется в случае удаления модуля из проекта

Но хорошо - чторешение идеально?

0 голосов
/ 27 сентября 2018

В общем случае, я бы решил эту проблему в C #, определив общий набор интерфейсов, которые содержали бы все ваши модули.Я думаю вы можете сделать это с Unity, поместив файлы из каждого модуля в одно и то же место, что позволит более поздним установкам перезаписывать те же файлы (очевидно, с тем же содержимым).Затем вы должны поместить элементы управления редактора, которые предоставляют свойства для хранения экземпляров этих интерфейсов, а затем подключить их в пользовательском интерфейсе.Вы должны проверить эти свойства на значение null, чтобы определить, какие из них отсутствуют.

Common.cs:

public interface IModuleA {}
public interface IModuleB {}

ModuleA.cs

public class ModuleA : IModuleA {}

ModuleB.cs

public class ModuleB : IModuleB
{
    public IModuleA ModuleAInstance {get; set;}

    private bool IsModuleAPresent() 
    {
        return !ModuleAInstance == null;
    }
}

Идеальный способ ее решения - использование менеджера пакетов и правильное внедрение зависимостей, но сделать это с Unity непросто.

...