Version: 2019.4
特殊文件夹和脚本编译顺序
程序集定义 (Assembly Definition) 属性

程序集定义

通过使用程序集定义,可以将项目中的脚本组织到程序集内。在文件夹中创建一个程序集定义资源后,Unity 会从该文件夹中的所有脚本编译一个单独的托管程序集。除非子文件夹有自己的程序集定义,否则将包含子文件夹中的脚本。这些托管程序集充当 Unity 项目中的单个库。

程序集定义资源是文本文件,文件扩展名为 .asmdef,其中包含定义程序集定义 (Assembly Definition) 属性的 JSON 字符串。应选择此资源文件以在 Unity Inspector 窗口中设置这些属性。还可以使用外部编辑器来直接编辑 JSON。请参阅程序集定义文件格式以了解关于 JSON 语法的信息。

程序集定义的优点

将代码分成多个具有良好定义的依赖项的程序集时,Unity 仅在您更改脚本时重新构建依赖的程序集,因此可以减少编译时间。当项目中包括特定于平台和 Unity 版本的代码时,程序集定义还有助于管理此类项目中的依赖项。

如果没有程序集定义,Unity 会将项目中的所有 C# 脚本编译到某一个预定义的托管程序集内。然后,当更改任何脚本时,Unity 必须重新编译整个项目中的每个脚本。这意味着,向项目添加更多脚本时,从更改代码到看到实际变化之间的时长会更长。

__注意:__尽管不是必需,但只要在项目中使用程序集定义,就应该对项目中的所有代码都这样做。否则,在预定义的程序集之一中更改脚本时,Unity 仍必须重新编译项目中的所有代码,因为预定义的程序集自动依赖您使用程序集定义创建的任何程序集。

下图说明了如何将一个项目拆分为多个程序集:

预定义的程序集与手动定义的程序集
预定义的程序集与手动定义的程序集

默认情况下,Unity 几乎将所有项目脚本都编译到 Assembly-CSharp.dll 托管程序集。示例中显示的一个项目被分为五个单独的程序集。Main.dll 程序集依赖于 Stuff.dllThirdParty.dllStuff.dll 依赖于 Library.dll,依此类推。因此,当 Main.dll 中的代码更改时,Unity 无需重新编译任何其他程序集。此外,由于 Main.dll 包含更少的脚本,所以它的编译速度比 Assembly-CSharp.dll 更快。同样,如果 Stuff.dll 中的代码改变,Unity 只需重新编译 Main.dllStuff.dll,而不必重新编译 ThirdParty.dllLibrary.dll

__注意:__要找出 Unity 在何处编译特定的 C# 文件,请在 Project 窗口中选择该脚本文件,然后在 Inspector 中查看 Assembly Information 列表:

Assembly-CSharp-Editor.dll 预定义程序集内的脚本
Assembly-CSharp-Editor.dll 预定义程序集内的脚本

程序集定义引用资源

通过使用程序集定义引用资源,可以将其他文件夹添加到现有的程序集定义资源中。

使用程序集定义

将程序集定义资源添加到 Unity 项目中的文件夹可以定义程序集。编译后,程序集将包含文件夹及其子文件夹中的所有脚本(除非子文件夹具有自己的程序集定义)。可在 Inspector 中定义程序集的名称。

程序集定义是一种 Unity 资源类型。可在 Project 窗口中选择现有的程序集定义来查看或更改其属性。请参阅文件格式以了解关于程序集定义资源文件格式的信息。

要创建程序集定义资源,请执行以下操作:

  1. 在 Project 窗口中,选择要放置程序集定义的文件夹
  2. 选择 Assets > Create > Assembly Definition 以新建程序集定义
  3. 选择创建的新程序集定义
  4. 在 Inspector 窗口中设置其属性

要创建程序集定义引用资源,请执行以下操作:

  1. 在 Project 窗口中,选择要将现有程序集定义资源添加到的文件夹
  2. 选择 Assets > Create > Assembly Definition Reference 以创建程序集定义引用资源
  3. 选择创建的新程序集定义引用
  4. 设置程序集定义属性,使其引用要添加其他文件夹的程序集定义资源
程序集定义引用编辑器
程序集定义引用编辑器

每个文件夹中,只能创建一个程序集定义或程序集定义引用。

如果在已经具有程序集定义或程序集定义引用的文件夹的子文件夹中创建程序集定义或程序集定义引用,则 Unity 会将子文件夹中的所有脚本及其子项编译到子文件夹中定义的程序集内,而不是父文件夹中定义的程序集内。

Use GUID

Use GUID 选项可以控制 Unity 如何序列化对程序集定义资源的引用。启用 Use GUID 后,Unity 会将这些引用另存为资源的 GUID,而不是程序集定义名称。最好的做法是使用 GUID 而不是名称,因为这意味着可以更改程序集定义资源的名称,而不必更新引用它的其他程序集定义或程序集定义引用文件。

版本定义

使用版本定义 (Version Defines) 可以处理当前项目中不同资源和资源包之间的依赖关系。如果要通过 Package Manager 包Asset Store 包来共享项目,这会很有用。

要设置版本,请选择加号 (+)。可以根据需要,向程序集定义中添加任意数量的版本定义。要删除某个定义,请在列表中将其选中,然后单击减号 (-)。

添加版本定义时,将显示以下属性:

Inspector 中的版本定义 (Version Defines)。此示例中,有两个激活的版本定义。
Inspector 中的版本定义 (Version Defines)。此示例中,有两个激活的版本定义。
属性 描述
Resource 选择要设置定义的包或模块。该列表包含项目中所有激活的包和模块。
Define 设置定义名称。仅在 Expression 返回 true 的情况下,才设置此定义。
Expression 所选择的模块或包的语义版本范围。必须使用数学间隔符号。不支持通配符。此版本范围与 Microsoft 的 .NET 包管理器(即 NuGet)相同。
Expression outcome 显示 Expression 表示的数学方程式。

向后兼容性和隐式依赖关系

为了维持与 Unity 中的现有预定义编译系统的兼容性,预定义的程序集将会引用通过程序集定义资源创建的每个程序集。这一点类似于预定义程序集引用项目中与激活的构建目标兼容的所有预编译程序集(例如插件或 .dll)。

下图说明了预定义的程序集、使用程序集定义资源创建的程序集以及预编译的程序集之间的默认依赖关系。

程序集依赖关系
程序集依赖关系

图中的数字表示程序集之间的引用,可以按以下方式进行控制:

  1. 默认情况下,预定义的程序集将会引用使用程序集定义资源创建的程序集。可以通过在 Inspector 中为程序集定义资源禁用 Auto Referenced 选项来禁止这种关系。请参阅程序集定义 (Assembly Definition) 属性
  2. 预定义的程序集以及使用程序集定义资源创建的程序集均自动引用预编译的程序集(插件)。要禁止此默认行为,请禁用 Plugin Inspector 中的 Auto Referenced 属性。请参阅 Plugin Inspector 以了解更多信息。
  3. 关闭插件的 Auto Referenced 时,可以在 Inspector 中为程序集定义资源显式引用它。为此,请启用该资源的 Override References 选项,然后添加插件的引用。请参阅程序集定义 (Assembly Definition) 属性

注意:如果为预编译的程序集或使用程序集定义资源创建的程序集禁用 Auto Referenced 选项,则预编译的程序集中的类将不再能够引用或使用这些程序集中的类。无法声明预编译的程序集的显式引用。

特殊文件夹

Unity 对具有某些特殊名称的文件夹中的脚本进行处理的方式不同于其他文件夹中的脚本。但是,在这些文件夹之一中或上级的一个文件夹中创建程序集定义资源时,该文件夹将失去特殊处理。在使用 Editor 文件夹时可能会注意到该变化,这些文件夹可能分散在整个项目中(取决于代码的组织方式以及所用的资源包)。

Unity 通常都会将名为 Editor 的文件夹中的所有脚本编译到预定义的 Assembly-CSharp-Editor 程序集中(无论这些脚本位于何处)。但是,如果在下级有一个 Editor 文件夹的文件夹中创建程序集定义资源,则 Unity 不再将这些 Editor 脚本放入预定义的 Editor 程序集中。实际上,这些脚本会进入通过程序集定义创建的新程序集中(脚本可能不属于该程序集)。要管理 Editor 文件夹,可以执行以下操作:

  • 将程序集定义资源添加到每个受影响的 Editor 文件夹,并设置该程序集的 Platform 属性,以便仅用于 Editor 平台
  • 将所有特定于 Editor 的代码移到程序集定义未涵盖的中心位置。

测试程序集

测试程序集包含对项目中其他代码进行测试的代码。因此,不必将测试程序集包含在项目的发布版中,其他非测试程序集也不应依赖它们。如果要在 Unity 中使用测试程序集,请参阅有关如何新建测试程序集 (How to create a new test assembly) 的 Test Framework 包文档。

API

使用 UnityEditor.Compilation 命名空间中的 CompilationPipeline 类可获取 Unity 为项目构建的所有程序集(包括基于程序集定义资源创建的程序集)的相关信息。

例如,以下脚本使用 CompilationPipeline 来列出项目中的所有当前播放器程序集:

using UnityEditor;
using UnityEditor.Compilation;

public static class AssemblyLister
{

    [MenuItem("Tools/List Player Assemblies in Console")]
    public static void PrintAssemblyNames()

    {
        UnityEngine.Debug.Log("== Player Assemblies ==");
        Assembly[] playerAssemblies =
            CompilationPipeline.GetAssemblies(AssembliesType.Player);

        foreach (var assembly in playerAssemblies)
        {
            UnityEngine.Debug.Log(assembly.name);
        }
    }
}

程序集定义文件格式

程序集定义和程序集定义引用资源是 JSON 文件。可以在 Unity Editor 中编辑资源,也可以使用外部工具修改 JSON 内容。

程序集定义是具有以下字段的一种 JSON 对象:

字段 类型
name string
references(可选) 字符串数组
includePlatforms(可选) 字符串数组
excludePlatforms(可选) 字符串数组
allowUnsafeCode(可选) bool
autoReferenced(可选) bool
overrideReferences(可选) bool
precompiledReferences(可选) 字符串数组
defineConstraints(可选) 字符串数组
optionalUnityReferences(可选) 字符串数组

在同一程序集定义文件中,无法同时使用 includePlatformsexcludePlatforms 字段。

可以使用程序集名称或资源的 GUID 在 references 字段中指定程序集定义资源。要使用资源 GUID,该值应该采用 GUID:Asset GUID 格式,例如,“GUID:0ec2b662ccc592241854c1b507df8a89”。 可使用 AssetDatabase.AssetPathToGUID 来获取资源的 GUID。

可以使用 CompilationPipeline.GetAssemblyDefinitionPlatforms() 函数来获取平台名称字符串。(必须为当前的 Editor 安装对平台的支持。)

程序集定义引用是具有以下字段的一种 JSON 对象:

字段 类型
reference string

可以使用程序集名称或资源的 GUID 来指定引用的程序集定义资源。要使用资源 GUID,该值应该采用 GUID:Asset GUID 格式,例如,“GUID:0ec2b662ccc592241854c1b507df8a89”。AssetDatabase.AssetPathToGUID can be used to retrieve the GUID of an asset.

程序集定义 JSON 示例

MyLibrary.asmdef

{
    "name": "MyLibrary",
    "references": [ "Utility" ],
    "includePlatforms": ["Android", "iOS"]
}

MyLibrary2.asmdef

{
    "name": "MyLibrary2",
    "references": [ "GUID:0ec2b662ccc592241854c1b507df8a89" ],
     "excludePlatforms": ["WebGL"]
}
特殊文件夹和脚本编译顺序
程序集定义 (Assembly Definition) 属性