如何使 Unity 插件支持旧版本的 Unity?

How to make a Unity plugin support old versions of Unity?

我正在为 Unity 开发一个插件。比如说,在我的机器上安装了最新版本的 Unity(现在是 2019.1.8)。但是,我也希望插件支持旧版本的 Unity,最好是非常旧的版本(如 4.x 或 5.x)。这应该如何实现?

我想我需要在我的机器上安装不同版本的 Unity。例如。使用 Unity Hub 或每次我想在安装新版本之前保留一个版本时手动重命名 Unity 的安装文件夹(如此处所述:https://support.unity3d.com/hc/en-us/articles/210001066-Can-I-activate-more-than-one-version-of-Unity-on-the-same-machine-)。

在 Unity Asset Store 上发布插件时,我还必须上传多个 *.unitypackage 文件,Unity Asset Store 会根据安装的 Unity 版本向每个买家提供适当的文件买家机器上的编辑器。

由于 Unity 版本可能存在显着差异,因此我基本上必须同时开发多个项目。并且(尤其是在开发后期)必须手动将代码中的单个更改复制到项目的所有其他版本中。这是有道理的,因为在某些情况下,它不会简单地复制和粘贴,而是调整代码以解决旧版本中未存在、已重命名、已弃用等问题。

对我来说,这看起来是不可思议的开销。 Unity 插件的开发人员是否真正做到了这一切,还是有更简单的方法?如果我简单地使用最新版本的 Unity 构建并只上传一个 *.unitypackage 文件,那么只有一些最新版本的 Unity(通常最多大约 1 年)才能正确导入和使用它,对吧?

通常在您的代码中,您可以使用 c# pre-processors 以使代码编译或在某些全局定义上有条件地注释掉。

Unity 提供 Version and Platform specific pre-processors

From Unity 2.6.0 onwards, you can compile code selectively. The options available depend on the version of the Editor that you are working on. Given a version number X.Y.Z (for example, 2.6.0), Unity exposes three global #define directives in the following formats: UNITY_X, UNITY_X_Y and UNITY_X_Y_Z.

Starting from Unity 5.3.4, you can compile code selectively based on the earliest version of Unity required to compile or execute a given portion of code. Given the same version format as above (X.Y.Z), Unity exposes one global #define in the format UNITY_X_Y_OR_NEWER, that can be used for this purpose.

因此您可以准确控制哪个代码版本应用于哪个特定目标平台、Unity 版本、.Net 版本等


您可以包装您的代码,例如在

#if UNITY_2017_1_OR_NEWER
    /* Code that only compiles under newer version */
#elif UNITY_5
    /* Code that compiles for Unity 5.x.y */
#elif UNITY_4
    /* Code that compiles for Unity 4.x.y */
#else
    /* apparently some older stuff */
#endif

然后您可以将其全部打包到一个 *.unitypackage 中,用户甚至不会注意到。由于在构建的应用程序中删除了注释掉的内容,因此不会增加构建大小。

然而,发展结构是另一个问题。我认为,为了确保支持有效,您不会为每个 Unity 版本准备一个项目,然后将旧版本的代码复制粘贴到最新版本的 pre-processor 包装部分。

为了尽可能简单,我可能会使用像

这样的文件夹结构
YourPlugIn
|-- General
|-- Unity4
|-- Unity5
|-- Unity2017

并使用 partial 关键字来实现完整的行为,而不是启用和禁用单个代码块,例如

General/MyBehaviour.cs

public partial class MyBehaviour : MonoBehaviour
{
    // this is the script users should be dragging onto objects
}

Unity4/MyBehaviour_4.cs

#if UNITY_4
// Extends the general MyBehaviour for Unity 4.x.y
public partial class MyBehaviour
{
    public string InitialValue;

    private void Start()
    {
        Debug.Log(InitialValue);
    }
}
#endif

Unity5/MyBehaviour_5.cs

#if UNITY_5
// Extends the general MyBehaviour for Unity 5.x.y
public partial class MyBehaviour
{
    public int InitialValue;

    private void Start()
    {
        Debug.Log(InitialValue.ToString());
    }
}
#endif

Unity2017/MyBehaviour_2017.cs

#if UNITY_2017
// Extends the general MyBehaviour for Unity 2017.x.y
public partial class MyBehaviour
{
    public Vector3 InitialValue;

    private void Start()
    {
        Debug.Log(InitialValue.ToString());
    }
}
#endif

但是那么你不能使用任何例如UNITY_X_Y_OR_NEWER 定义是因为你会得到重复的 ;)

当然还有很多问题区域,例如之后重命名您的 类 或在较新的版本中将它们包装在接口等中..但我希望我的想法很清楚。


我还冒着风险声称只支持“最新”版本的 Unity 就足够了,而不是 Plug-In 向后兼容到 Unity 2.6 ...谁还会使用还是那些老东西?

目前在 HUB 中,提供安装的最新版本是 2017.1.5f1,所以我认为在那个版本之前提供向后支持就足够了,不再有任何进一步的支持。


关于版本控制的问题:Github 提供了一个相当完整的 .gitignore for Unity。在那里你可以看到你 应该 忽略的 Temp 文件夹还有很多.. 特别是 Library.

中,我还描述了一种快速且安全的方法,可以使用一个 git 命令删除所有生成的文件。就我个人而言,我喜欢 exclude 所有 Library/*.asset 文件从忽略(因此它们 版本控制)因为这里有些东西,例如存储自定义布局、当前构建目标等。