一、Unity中的条件编译
Unity 对 C# 语言的支持包括使用指令,这些指令允许您根据是否定义了某些脚本符号,选择性地包含或排除代码的编译。有关这些指令在 C# 中如何工作的更多信息,请参阅微软关于 C# 预处理器指令 的文档。
(一)预定义符号
Unity 提供了一系列预定义的符号,您可以在脚本中使用它们,选择性地包含或排除代码段的编译。例如,在 Windows 独立平台的项目构建中定义的符号是 UNITY_STANDALONE_WIN
。您可以使用一种特殊类型的 if
语句检查此符号是否已定义:
#if UNITY_STANDALONE_WINDebug.Log("Standalone Windows");
#endif
#if
和 #endif
前的井号(#)表示这些语句是编译过程中处理的指令,而不是在运行时处理的指令。在上述示例中,Debug
行仅在项目的 Windows 独立构建中包含在编译中。在 Unity 编辑器或其他目标构建中编译时,它会被完全省略。这与使用常规的 if
语句不同,后者可能仅在运行时绕过某些代码段的执行。
(二)多条件检查
您可以使用 #elif
和 #else
指令来检查多个条件:
#if UNITY_EDITORDebug.Log("Unity Editor");
#elif UNITY_IOSDebug.Log("Unity iOS");
#elseDebug.Log("Any other platform");
#endif
(三)预定义符号的作用范围
Unity 有多个预定义符号,允许您根据所选平台、编辑器版本和其他系统环境场景,选择性地编译或省略代码。
二、Unity 脚本符号参考
(一)平台符号
Unity 根据创作和构建目标平台自动定义某些符号,具体如下:
定义 | 功能 |
---|---|
UNITY_EDITOR | 用于在游戏代码中调用 Unity 编辑器脚本的脚本符号。 |
UNITY_EDITOR_WIN | 用于 Windows 上编辑器代码的脚本符号。 |
UNITY_EDITOR_OSX | 用于 macOS 上编辑器代码的脚本符号。 |
UNITY_EDITOR_LINUX | 用于 Linux 上编辑器代码的脚本符号。 |
UNITY_EMBEDDED_LINUX | 用于嵌入式 Linux 的脚本符号。 |
UNITY_QNX | 用于 QNX 的脚本符号。 |
UNITY_STANDALONE_OSX | 用于专门为 macOS(包括 Universal、PPC 和 Intel 架构)编译或执行代码的脚本符号。 |
UNITY_STANDALONE_WIN | 用于专门为 Windows 独立应用程序编译/执行代码的脚本符号。 |
UNITY_STANDALONE_LINUX | 用于专门为 Linux 独立应用程序编译/执行代码的脚本符号。 |
UNITY_STANDALONE | 用于为任何独立平台(Mac OS X、Windows 或 Linux)编译/执行代码的脚本符号。 |
UNITY_SERVER | 用于为专用服务器(macOS、Windows 或 Linux)编译/执行代码的脚本符号。 |
UNITY_IOS | 用于为 iOS 平台编译/执行代码的脚本符号。 |
UNITY_ANDROID | 用于 Android 平台的脚本符号。 |
UNITY_TVOS | 用于 Apple TV 平台的脚本符号。 |
UNITY_VISIONOS | 用于 VisionOS 平台的脚本符号。 |
UNITY_WSA | 用于通用 Windows 平台的脚本符号。 |
UNITY_WSA_10_0 | 用于通用 Windows 平台的脚本符号。 |
UNITY_WEBGL | 用于 Web 的脚本符号。 |
UNITY_ANALYTICS | 用于在游戏代码中调用 Unity Analytics 方法的脚本符号。 |
UNITY_ASSERTIONS | 用于断言控制流程的脚本符号。 |
UNITY_64 | 用于 64 位平台的脚本符号。 实际上不建议使用,因为它在所有 64 位架构上并不适用,且同一平台上的不同 CPU 架构可能共享相同的编译程序集。要基于架构条件执行代码,请使用标准的 if 语句检查 IntPtr.Size ,在 32 位进程中为 4,64 位进程中为 8。示例请参阅“指令的替代方案”。 |
(二)Unity 编辑器版本符号
Unity 根据您当前使用的 Unity 编辑器版本自动定义某些脚本符号。
假设版本号为 X.Y.Z(例如 2019.4.14),Unity 以以下格式暴露三个全局脚本符号:UNITY_X
,UNITY_X_Y
和 UNITY_X_Y_Z
。
以下是 Unity 2019.4.14 中暴露的脚本符号示例:
定义 | 功能 |
---|---|
UNITY_2019 | 用于 Unity 2019 发行版本的脚本符号,在每个 2019.Y.Z 版本中均可见。 |
UNITY_2019_4 | 用于 Unity 2019.4 主要版本的脚本符号,在每个 2019.4.Z 版本中均可见。 |
UNITY_2019_4_14 | 用于 Unity 2019.4.14 次要版本的脚本符号。 |
您还可以基于编译或执行代码段所需的最早 Unity 版本有选择地编译代码。按照上述相同的版本格式(X.Y),Unity 以 UNITY_X_Y_OR_NEWER
的格式暴露一个全局 #define
,您可以使用它来实现此目的。
(三)其他符号
Unity 定义的其他符号包括:
定义 | 功能 |
---|---|
CSHARP_7_3_OR_NEWER | 当使用支持 C# 7.3 或更高版本的编译脚本时定义。 |
ENABLE_MONO | Mono 的脚本后端 #define 。 |
ENABLE_IL2CPP | IL2CPP 的脚本后端 #define 。 |
ENABLE_VR | 当目标构建平台支持 VR 时定义。 注意:这并不意味着当前启用了 VR 或安装了支持 VR 所需的插件和包。 |
NET_2_0 | 当在 Mono 和 IL2CPP 上针对 .NET 2.0 API 兼容级别构建脚本时定义。 |
NET_2_0_SUBSET | 当在 Mono 和 IL2CPP 上针对 .NET 2.0 子集 API 兼容级别构建脚本时定义。 |
NET_LEGACY | 当在 Mono 和 IL2CPP 上针对 .NET 2.0 或 .NET 2.0 子集 API 兼容级别构建脚本时定义。 |
NET_4_6 | 当在 Mono 和 IL2CPP 上针对 .NET 4.x API 兼容级别构建脚本时定义。 |
NET_STANDARD_2_0 | 当在 Mono 和 IL2CPP 上针对 .NET Standard 2.0 API 兼容级别构建脚本时定义。 |
NET_STANDARD_2_1 | 当在 Mono 和 IL2CPP 上针对 .NET Standard 2.1 API 兼容级别构建脚本时定义。 |
NET_STANDARD | 当在 Mono 和 IL2CPP 上针对 .NET Standard 2.1 API 兼容级别构建脚本时定义。 |
NETSTANDARD2_1 | 当在 Mono 和 IL2CPP 上针对 .NET Standard 2.1 API 兼容级别构建脚本时定义。 |
ENABLE_WINMD_SUPPORT | 当在 IL2CPP 上启用 Windows Runtime 支持时定义。有关详细信息,请参阅Windows Runtime 支持。 |
ENABLE_INPUT_SYSTEM | 当在播放器设置中启用输入系统包时定义。 |
ENABLE_LEGACY_INPUT_MANAGER | 当在播放器设置中启用传统输入管理器时定义。 |
DEVELOPMENT_BUILD | 当您的脚本在启用了开发构建选项的播放器中运行时定义。 注意:此定义仅反映构建时是否启用了开发构建选项。要确定您的脚本是否正在以开发构建模式运行,请使用 Debug.isDebugBuild 。__DEVELOPMENT_BUILD__ 并不足以确定当前是否正在以开发构建运行,因为大多数平台允许在不重新构建项目的情况下在开发和非开发构建之间切换。然而,在某些平台上,Unity 不支持在编辑器中切换开发和非开发构建,需要在构建完成后进行切换。例如,在 Windows 上,您可以选择“创建 Visual Studio 解决方案”选项,以选择是否在 Visual Studio 中进行开发或非开发构建。Visual Studio 中的切换不会重新编译您的脚本,因此不会重新评估脚本定义。您还可以通过将游戏构建中的 UnityPlayer.dll 与开发构建中的 UnityPlayer.dll 互换,来从最终的游戏构建切换到开发构建,以调试实时游戏构建。 |
UNITY_CLOUD_BUILD | 当项目使用 Unity 构建自动化构建时定义。 |
注意:DEBUG
符号在 C# 和 Unity 中是预定义的,使用指令 #if DEBUG
等同于 #if UNITY_EDITOR || DEVELOPMENT_BUILD
。
三、自定义脚本符号
除了 Unity 内置的脚本符号外,您还可以定义自己的自定义脚本符号。定义自定义脚本符号的位置决定了它们的适用范围。您可以在以下位置定义自定义符号:
- 资产文件,适用于项目中所有编辑器和播放器代码的符号,无论活动的构建配置文件是什么。
- 平台配置,适用于当特定平台或其构建配置文件处于活动状态时的所有编辑器和播放器代码的符号。
- 构建配置文件,适用于当特定构建配置文件处于活动状态时的所有编辑器和播放器代码的符号。
- 代码中,适用于活动平台或单个播放器构建,具体取决于使用的 API。
(一)适用于整个项目的自定义符号
您可以通过响应文件资产为整个项目定义自定义脚本符号,方法如下:
- 将文件命名为
csc.rsp
并将其放置在项目的Assets
文件夹根目录中。 - 在以
-define:
开头的行中定义脚本符号,后跟一个或多个用分号分隔的脚本符号。 - Unity 会在启动时读取此文件,并在编译任何代码之前应用它。例如,如果在
csc.rsp
文件中包含单行-define:UNITY_DEBUG;UNITY_TEST
,则符号UNITY_DEBUG
和UNITY_TEST
将作为全局定义的脚本符号包含在项目中的所有 C# 脚本中。
注意:对 .rsp
文件的更改在 Unity 重新编译脚本之前不会生效。您可以通过更新或重新导入单个脚本文件来触发重新编译。
(二)适用于特定平台的自定义符号
您可以通过以下步骤为特定平台定义自定义脚本符号:
- 打开 Player Setting。
- 导航到 Other Settings > Script Compilation 部分中的 Script Define Symbols。
- 通过选择
+
按钮并在文本字段中输入符号名称,将新的脚本符号添加到列表中。使用-
按钮删除现有列表项。 - 编辑完成后,选择 Apply 以应用更改。Unity 会使用新的符号重新编译项目中的脚本。
注意:复制定义
按钮会将当前自定义脚本符号列表中的符号作为分号分隔的字符串复制到剪贴板。
(三)适用于构建配置文件的自定义符号
您可以通过以下步骤为构建配置文件定义自定义脚本符号:
- 在 Build Profile 窗口中选择要为其定义符号的构建配置文件。
- 导航到 Build Data 部分中的 Scripting Defines。(只有在自定义的Build Profile中才能看到)。
- 通过选择
+
按钮并在文本字段中输入符号名称,将新的脚本符号添加到列表中。使用-
按钮删除现有列表项。对列表的修改会自动保存和应用。
注意:您可以使用 -activeBuildProfile
命令行参数启动编辑器,以使指定的构建配置文件及其自定义脚本符号从启动时即生效。
(四)从代码中定义自定义符号
您可以使用以下 API 来定义脚本符号:
PlayerSettings.SetScriptingDefineSymbols
BuildPlayerOptions.extraScriptingDefines
Build.Player.ScriptCompilationSettings.extraScriptingDefines
BuildPlayerOptions.extraScriptingDefines
和 Build.Player.ScriptCompilationSettings.extraScriptingDefines
仅适用于播放器构建,因此在定义适用于编辑器脚本的脚本符号时,请使用 PlayerSettings.SetScriptingDefineSymbols
。这是配置播放器设置中平台特定脚本符号的代码等效方法。
重要:使用 SetScriptingDefineSymbols
从代码创建的符号在编辑器重新获得控制并重新编译脚本之前不会生效。例如,如果您在编辑器脚本中使用 SetScriptingDefineSymbols
创建脚本符号,然后在下一行调用 BuildPipeline.BuildPlayer
,则前一行创建的新符号尚未生效。在这种情况下,作为 BuildPlayer
执行一部分运行的任何编辑器代码都将在没有新符号的情况下运行,播放器可能无法按预期构建。
(五)批处理模式下的自定义符号
当编辑器以批处理模式运行时,没有机制可以触发脚本的重新编译。如果您需要在以批处理模式运行的编辑器中定义特定的符号,它们必须通过 csc.rsp
资产文件在启动时就到位。
(六)脚本符号继承
如果您在多个位置定义了自定义脚本符号,Unity 会将所有适用于当前构建配置的符号加在一起。符号是从每个作用域继承的,而不是被覆盖,具体如下:
- 项目范围符号(来自
csc.rsp
) - 平台特定符号(来自播放器设置)
- 构建配置文件符号(来自构建数据)
仅当与活动构建配置文件匹配时,才会包含平台和构建配置文件符号。
例如,假设您的项目在以下位置定义了以下自定义脚本符号:
位置 | 定义的符号 |
---|---|
csc.rsp | SYMBOL_A |
Windows Player Setting | SYMBOL_B |
WindowsBuildProfile1 | SYMBOL_C |
WindowsBuildProfile2 | SYMBOL_D |
在此示例配置中,当不同的构建配置文件处于活动状态时,以下表格显示了哪些符号适用于您的编辑器和播放器代码:
Active Build Profile | Active Symbols |
---|---|
Android | SYMBOL_A |
Windows | SYMBOL_A , SYMBOL_B |
WindowsBuildProfile1 | SYMBOL_A , SYMBOL_B , SYMBOL_C |
WindowsBuildProfile2 | SYMBOL_A , SYMBOL_B , SYMBOL_D |
您可以使用代码中的 #if
指令来测试这种行为。有关更多信息,请参阅 测试条件编译。
三、测试条件编译
以下示例展示了如何测试您的条件编译代码。它还根据为目标构建选择的平台在控制台中打印一条消息。
(一)示例代码
using UnityEngine;
using System.Collections;public class PlatformDefines : MonoBehaviour {void Start () {#if UNITY_EDITORDebug.Log("Unity Editor");#endif#if UNITY_IOSDebug.Log("Unity iOS");#endif#if UNITY_STANDALONE_OSXDebug.Log("Standalone OSX");#endif#if UNITY_STANDALONE_WINDebug.Log("Standalone Windows");#endif}
}
(二)测试步骤
- 打开 Build Profiles 窗口(菜单:File > Build Profiles)。
- 检查您要测试代码的平台是否为活动平台配置文件。如果不是,请选择您首选的平台并点击 Switch Profile。
- 创建一个脚本并复制粘贴上述示例代码。
- 在 Game View Toolbar,点击 Play 按钮进入播放模式。通过检查 Unity 控制台中与所选平台相关的消息,确认代码是否正常工作。例如,如果您选择 iOS,则控制台中会出现
Unity Editor
和Unity iOS
的消息。
参考:
-
https://docs.unity3d.com/Manual/scripting-symbol-reference.html
-
https://docs.unity3d.com/Manual/custom-scripting-symbols.html