故障排除

本部分介绍了使用 Unity 时可能出现的常见问题。按照平台分别在下方列出。

平台故障排除

Desktop

OSX 10.6.4 中的 Geforce 7300GT

  • OSX10.6.4 中的 Geforce 7300 不能正确显示材质,延迟渲染被禁用。这是因为视频卡驱动程序存在错误。

在 Windows x64 上,当脚本抛出 NullReferenceException 时,Unity 崩溃。

脚本编辑

有没有办法让 MonoDevelop 不显示欢迎页面?

  • 有。在 MonoDevelop 首选项跳转到视觉风格 (Visual Style) 部分,取消选中“启动时加载欢迎页面 (Load welcome page on startup)"。

为什么我已经选择 Visual Studio 作为脚本编辑器,脚本依然在 MonoDevelop 中打开?

  • 这主要出现在 VS 报告无法打开您的脚本时。这一现象最常见的原因是外部插件(例如 Resharper)在启动时弹出对话框,请求用户输入 - 这将导致 VS 报告无法打开脚本。

图形

低帧率和/或视觉瑕疵。

  • 可能是由于视频卡驱动程序没有更新至最新版本。请确保已经从视频卡厂商获得最新的官方驱动 。

阴影

我完全看不到阴影!

  • 阴影是 Unity Pro 的特有功能,因此,没有 Unity Pro 自然也无法取得阴影。当然,简单的阴影方法依旧可用,如使用投影仪 (Projector)
  • 阴影也需要某些图形硬件支持,查阅质量设置 (Quality Settings) 中是否完全禁用了阴影。
  • 目前 Android 和 iOS 移动平台不支持阴影。

某些对象不投射或接受阴影

对象的渲染必须启用接受阴影 (Receive Shadows) 才能在物体表面渲染阴影。同时,对象必须启用投射阴影 (Cast Shadows) 以便在其他对象上投射阴影(默认为启用这两种功能)。

只有不透明的对象才能投射和接受阴影。这意味着使用内置透明或颗粒阴影的对象将不会投射阴影。大部分情况下,可以使用透明抠图 (Transparent Cutout) 着色器处理围栏、植被等对象。如使用自定义编写的几何渲染队列。使用顶点光照 (VertexLit) 着色器的对象不接受阴影,但可以投射阴影。

只有像素发光 (Pixel lights) 能投射阴影。如果需要确定无论在场景中使用多少种其他灯光,灯光始终投射阴影,可以将其设置为 Force Pixel 渲染模式(请参阅灯光 (Light) 参考页)。

iOS

iOS 设备故障排除

对于 iOS 来说,在某些情况下,游戏可以在 Unity 编辑器中完美运行,但在实际设备上却无法运行甚至不能启动。这些问题通常是由代码或内容质量引起的。本部分将介绍最常见的几种情况。

游戏停止响应一段时间后,Xcode 状态栏显示"中断 (interrupt)"。

导致这一现象有多种原因,主要原因包括:

  1. 脚本错误,如使用未初始化的变量等。
  2. 使用第三方 Thumb 编译本地库。这种库在 iOS SDK 连接器中触发已知问题并可能产生随机崩溃。
  3. 使用具有值类型的泛型类型作为参数(例如, List<int>、List<SomeStruct>、List<SomeEnum> 等)用于序列化的脚本属性。
  4. 在启用当托管代码剥离 (managed code stripping) 时,使用反射。
  5. 本机插件接口出错(托管代码的方法签名不匹配本机代码的函数签名)。

XCode 调试控制台的信息有助于检测此类故障(Xcode 菜单:查看 (View) > 调试区 (Debug Area) > 激活控制台 (Activate Console))。

Xcode 控制台显示 "Program received signal: “SIGBUS” 或 EXC_BAD_ACCESS 错误

此消息通常在应用程序收到 NullReferenceException 时出现在 iOS 设备。有两种方式找出发生错误的位置:

托管堆栈跟踪:

从 3.4 版开始 Unity 包含基于软件的空引用 (NullReferenceException) 处理。AOT 编译器包含每次访问对象的一种方法或变量时,快速查看所有空引用。这一功能影响了脚本的性能,这也是为什么只在开发构建中启用该功能的原因(在基础版许可证书中,用户可以在构建设置 (Build Setting) 对话框中打开“开发构建 (development build)” 选项,但 iOS 专业版许可证书用户需要另外启用“脚本调试 (script debugging)” 选项)。 如果上述内容都正确,而错误实际出现在 .NET 代码中,那么您将不会再看到 EXC_BAD_ACCESS, 相反,.NET 异常文本将要 Xcode 控制台中显示(或者代码在 "catch" 语句处理)。典型的输出可能是:

Unhandled Exception: System.NullReferenceException: A null value was found where an object instance was required.
  at DayController+$handleTimeOfDay$121+$.MoveNext () [0x0035a] in DayController.js:122 

这表明错误发生在 DayController 类的 handleTimeOfDay 方法中,是一个协同程序。 此外,如果它是脚本代码,就会提示出错误所在的行号(例如:"DayController.js:122 ")。问题语句可能如下:

 Instantiate(_imgwww.assetBundle.mainAsset);

如果脚本在没有首先检查下载是否正确的前提下访问资源包,可能发生这种错误。

本机堆栈跟踪:

本机堆栈跟踪对于故障调查是一项更为强大的工具,但是,使用它需要一些专业技术。通常在发生此类本机(硬件内存访问)故障之后通常不能继续。要进行本机堆栈跟踪需要在 Xcode 调试控制台输入 bt all。仔细检查显示的堆栈跟踪,它们可能包括了错误来源信息。您可能会看到这样的信息:

...
Thread 1 (thread 11523): 
#0 0x006267d0 in m_OptionsMenu_Start () 
#1 0x002e4160 in wrapper_runtime_invoke_object_runtime_invoke_void__this___object_intptr_intptr_intptr () 
#2 0x00a1dd64 in mono_jit_runtime_invoke (method=0x18b63bc, obj=0x5d10cb0, params=0x0, exc=0x2fffdd34) at /Users/mantasp/work/unity/unity-mono/External/Mono/mono/mono/mini/mini.c:4487
#3 0x0088481c in MonoBehaviour::InvokeMethodOrCoroutineChecked ()
...

首先应该找到 "Thread 1" 的堆栈跟踪,这是主线程。堆栈跟踪的第一行将指向发生错误的位置。在这个示例中,跟踪显示 NullReferenceException 发生在 "OptionsMenu" 脚本中的 "Start" 方法。仔细查看这一方法实现便可发现故障的原因。很明显,NullReferenceExceptions 发生在 Start 方法,对初始化顺序做出了错误的假设。 在某些情况下,调试控制台中只可以看到一小部分堆栈跟踪:

Thread 1 (thread 11523): 
#0 0x0062564c in start ()

这表明在应用程序发布期间,已经去除本地符号。通过以下步骤可获得完整的堆栈跟踪信息:

  • 在设备上删除应用程序。
  • 清除所有目标。
  • 构建并运行。
  • 再次获得如上所述的堆栈跟踪信息。

将外部库链接到 Unity iOS 应用程序时,EXC_BAD_ACCESS 开始发生。

这通常在外部库由ARM Thumb 指令集编译时发生。目前这种库不兼容 Unity。不用 Thumb 指令重新编译库,即可轻松解决这一问题。库的 Xcode 工程重新编译可使用以下步骤:

  • 在 Xcode, 从菜单中选择 "查看 (View)" > "导航器 (Navigators)" > "显示工程导航器 (Show Project Navigator)"
  • 选择 "Unity-iPhone" 工程,激活"构建设置 (Build Settings)" 选项卡
  • 在搜索字段输入:"Other C Flags"
  • 在此处添加 -mno-thumb 标志并重新构建库。

如果该库的源不可用,应联络提供 商,取得库的 non-thumb 版本。

Xcode 控制台显示 "WARNING -> applicationDidReceiveMemoryWarning()",应用程序随即崩溃

(有时会看到 Program received signal: 0 之类的信息。) 该警告信息通常不会使程序彻底崩溃,它仅仅提示 IOS 设备内存不足,要求释放更多的内存空间。通常,邮件 (Mail) 等后台进程可以释放一些内存空间,从而使应用程序继续运行。但是,如果应用程序继续使用内存或要求更多,操作系统最终将杀掉应用程序,其中可能包括您的程序。Apple 不会提示最低要求多大的内存空间。但是经验显示如果有不少于 50% MB 的 RAM 空间(如第 2 代 ipad ~200-256 MB 的内存),运行程序时就不会出现重大的内存使用问题。 最主要的衡量标准是应用程序会占用多大的 RAM 空间。应用程序占用的 RAM 空间主要由四大部分组成:

  • 应用程序代码( OS 需要加载并将应用程序代码保存在 RAM 中,但在必要时会丢弃部分代码)
  • 本地内存(引擎使用本地内存将其状态、资源等保存到 RAM)
  • 托管内存(用于 Mono 运行时间,保存 C# 或 JavaScript 对象)
  • GLES 驱动程序内存池:纹理、帧缓存、编译着色器等。

应用程序内存使用可通过三种 Xcode 工具进行追踪:活动监视器 (Activity Monitor)对象分配器 (Object Allocations)VM 追踪器 (VM Tracker)。您可以从 Xcode 运行菜单: 作品 (Product) > 配置 (Profile) 中启动,然后选择特定工具。活动监视器 (Activity Monitor) 工具显示包括实际内存在内的所有过程统计信息,这可以看作是应用程序使用的 RAM 总量。请注意: OS 和 设备的 HW 版本组合可能明显影响内存使用数量,因此,在对比不同设备获得的数量时,应谨慎对待。

注意:内部分析器只显示 .NET 脚本分配的内存。内存使用总量可通过 Xcode 工具确定,如上图所示。该数字包括一部分应用程序的二进制,一些标准的框架缓冲区、Unity 引擎的内部状态缓冲区、.NET 运行时间内存(数值由内部分析器显示)、GLES 驱动程序内存和其他杂项。

其他工具显示应用程序的所有分配,并同时包含本地内存和托管内存统计信息(务必查看 Created and still living 框,获得应用程序的当前状态)。重要的统计信息是净字节 (Net bytes) 字段。

若要保持低内存使用率:

  • 通过使用强大的 iOS 剥离选项(高级版许可证书功能),以减少应用程序的二进制文件的大小,同时避免过多地依赖不同 .NET 库。请参阅播放器设置 (player settings)播放器大小优化 (player size optimization) 手册页面,了解更多信息。
  • 减少内容大小。使用 PVRTC 压缩纹理并使用更低多边形模型。请参阅有关减少文件大小手册页面,了解更多信息。
  • 脚本不必要过多分配内存。使用内部分析器 (internal profiler) 跟踪 mono 内存大小和使用情况。
  • 注意:在 Unity 3.0 中,场景加载实现有显著变化,现在所有的场景资源都被预加载。这将在实例化游戏对象时带来更少的打嗝。如果需要对游戏设置中的加载和卸载拥有更精细的资源控制,可以使用Resources.LoadObject.Destroy

查询 OS 的可用内存量看起来是一个好想法,它可以评估应用程序执行的好坏程度。然而,可用内存统计不一定可靠,因为 OS 使用了很多动态缓冲区和缓存。唯一可靠的方法是保持跟踪应用程序的内存消耗和使用情况,将其作为主要度量标准。要注意上述工具中的图形如何随着时间的推移而变化,尤其是在加载新的关卡之后。

从 Xcode 启动时游戏运行正常,但在设备上手动启动加载第一个关卡时发生崩溃

这可能有几个原因。您需要检查设备的日志,以获得更多详情。将设备连接到 Mac,启动 Xcode 并从菜单选择窗口 (Window) > 组织器 (Organizer)。在组织器 (Organizer) 左侧工具栏选择设备,然后点击控制台 (Console) 选项卡并仔细审阅最新消息。此外,您可能需要研究崩溃报告。可以在这里找到如何获取崩溃报告:http://developer.apple.com/iphone/library/technotes/tn2008/tn2151.html

Xcode 组织器 (Organizer) 控制台显示 "killed by SpringBoard" 信息。

iOS 应用程序渲染第一帧和过程输入存在时间限制。如果应用程序超过此限制,将通过 SpringBoard 杀死应用。例如,这可能会发生在第一个场景太大的应用程序。为了避免这个问题,最好是创建一个小的初始场景,只显示开机屏幕,以 yield 等待一或两帧,然后才开始加载实际场景。可以使用下面的简单代码:

 
function Start () {
    yield;
    Application.LoadLevel("Test");
}

Type.GetProperty() / Type.GetValue() 引起设备崩溃

目前 Type.GetProperty()Type.GetValue() 仅支持 .NET 2.0 子集配置文件。可以在播放器设置 (Player Settings) 中选择 .NET API 的兼容性级别。

注意: Type.GetProperty()Type.GetValue() 可能与托管代码剥离不兼容,并且可能需要排除在外(可以在剥离过程中提供 自定义的非剥离类型列表实现该操作)。如需更多详细信息,请参阅iOS 播放器大小优化指南

游戏崩溃,并出现错误消息:"ExecutionEngineException: Attempting to JIT compile method 'SometType`1<SomeValueType>:.ctor ()' while running with --aot-only."。

iOS 的 Mono .NET 实现基于 AOT(提前编译为本机代码)技术,这种技术有其局限性。它仅仅编译那些泛型类型的方法(作为一种通用的参数值类型),其显式使用于其他代码。当这些方法只能通过反射或从本机代码使用时(例如,序列化系统),那么,他们会在 AOT 编译期间跳过。通过在脚本代码某处通过添加虚拟方法,AOT 编译器可以暗示包括代码。这可以引用丢失的方法,以便让它们提前编译。

void _unusedMethod()
{
    var tmp = new SomeType<SomeValueType>();
}

注意:值类型是基本类型、枚举和结构。

使用 System.Security.Cryptography 和托管代码剥离组合时,设备出现各种崩溃

.NET 加密服务很大程度上依赖于反射,因此它与托管代码剥离不兼容,因为这涉及到静态代码分析。有时解决崩溃最简单的方案是在剥离过程排除整个 System.Security.Crypography 命名空间。

剥离过程可以通过添加自定义 link.xml 文件到 unity 工程的资源 (Assets) 文件夹中自定义。这项功能指定应从剥离排除的类型和命名空间。请参阅 iOS 播放器大小优化指南,了解更多详细信息。

link.xml

<linker>
       <assembly fullname="mscorlib">
               <namespace fullname="System.Security.Cryptography" preserve="all"/>
       </assembly>
</linker>

在使用 System.Security.Cryptography.MD5 和托管代码剥离时,应用程序崩溃

您可以考虑上面列出的建议,或者添加特定类的其他引用至脚本代码,解决这一问题:

object obj = new MD5CryptoServiceProvider();

"Ran out of trampolines of type 0/1/2" 运行时间错误

该错误通常出现在使用大量递归泛型时。您可以暗示 AOT 编译器分配更多type 0、type 1 或 type 2 trampolines。此外,AOT 编译器命令行选项可以在播放器设置 (Player Settings)的其他设置 (Other Setings) 部分指定。对于 type 1 trampolines,指定 nrgctx-trampolines=ABCD,其中 ABCD 是新的 trampolines 所需的值(如 4096)。对于 type 2 trampolines,指定 nimt-trampolines=ABCD,对于 type 0 trampolines,指定 ntrampolines=ABCD

升级之后 Xcode Unity iOS 运行时间故障,错误信息为 "You are using Unity iPhone Basic. You are not allowed to remove the Unity splash screen from your game"

随着最新版本的 Xcode 发布,PNG 压缩和优化工具发生了变更。这些变更可能在 Unity iOS 运行时间检查闪屏修改时引起主动错误信息。如果遇到此类问题,请尝试更新 Unity 至最新的公开可用版本。如果这项操作不起作用,可以尝试以下变通措施:

  • 在 Unity 中构建时彻底替换 Xcode 工程(而非附加)
  • 在设备上删除已经安装的工程
  • 在 Xcode(作品 (Product)->清除 (Clean))中清除工程
  • 清除 Xcode 的导出数据 (Derived Data) 文件夹(Xcode->首选项 (Preferences)->位置 (Locations)

如果依然没有帮助,请尝试禁用 Xcode 中的 PNG 再次压缩:

  • 打开 Xcode 工程
  • 在这里选择 "Unity-iPhone" 工程
  • 选择其中的"构建设置 (Build Settings)" 选项卡
  • 找到 "压缩 PNG 文件 (Compress PNG files)" 选项并将其设置为否 (NO)。

App Store 提交失败,错误信息为"iPhone/iPod Touch: application executable is missing a required architecture. At least one of the following architecture(s) must be present: armv6"

如果应用程序已经存在,并且之前在 armv6 支持的前提下提交,那么在更新这些应用程序时,您可能收到此类信息。Unity 4.x 和 Xcode 4.5 不再支持 armv6 平台。若要解决提交问题,只需将 Unity 播放器设置 (Player Settings) 中的目标 OS 版本 (Target OS Version) 设置为 4.3 或者更高版本。

WWW 下载在 Unity 编辑器和 Android 能正常运行,但在 iOS 无法运行

最常见的错误是假设 WWW 下载始终在分离的线程中下载。在某些平台上可能确实如此,但是,您不应以偏概全。追踪 WWW 状态的最佳方式是使用 yield 语句或在 Update 方法中检查状态,而应使用正在被占用的 while 循环。

通过从脚本中调用的本机函数使用 Cocoa 时,出现错误信息:"PlayerLoop called recursively!"

UI 的部分操作将导致 IOS 立即重绘窗口(最常见的例子是用 UIViewController 将 UIView 添加至 UIWindow)。如果从脚本调用本地函数,它将在 Unity 的 PlayerLoop 中发生,导致在 PlayerLoop 中被递归调用。在这种情况下,您应该考虑使用 waitUntilDone 设置为 false 的 performSelectorOnMainThread 方法。它会通知 iOS 安排操作,以在 Unity 的 PlayerLoop 调用之间运行。

分析器或调试器无法看到 iOS 设备上运行的游戏

  • 检查是否构建了开发版本,并勾选启用脚本调试 (Enable Script Debugging) 和自动连接分析器 (Autoconnect profiler)(如有)复选框。
  • 设备上运行的应用程序将发送多播广播至 UDP 端口 54997 上的 225.0.0.222。检查网络设置允许此应用通行。然后,分析器将连接到范围为 55000 - 55511 的端口上的远程设备,以从设备获取分析器数据。这些端口需要打开以便 UDP 访问。

DLL 缺失

如果应用程序可以在编辑器上正常运行,但在 iOS 工程中出错,这可能是由于缺失 DLL(例如 I18N.dll,I19N.West.dll)引起。在这种情况下,请尝试从 Unity.app 中复制这些 dll 至工程的资源/插件 (Assets/Plugins) 文件夹。DLL 在 unity app 中的位置为:

 Unity.app/Contents/Frameworks/Mono/lib/mono/unity 

然后,应该检查工程的剥离级别,以确保在优化构建时,DLL 中的类不会被移除。请参阅 iOS 优化页面了解 iOS 剥离级别的更多信息。

Xcode 调试控制台报告:ExecutionEngineException: Attempting to JIT compile method '(wrapper native-to-managed) Test:TestFunc (int)' while running with --aot-only

当托管函数委托转到本机函数,但是必要的包装器代码未在构建应用程序时生成,很可能收到此信息。可以通过暗示哪些方法将作为委托转到本机代码,从而辅助 AOT 编译器。可以通过添加 "MonoPInvokeCallbackAttribute" 自定义属性完成这一操作。目前只有静态方法可以作为委托转到本机代码。

示例代码:

using UnityEngine;
using System.Collections;
using System;
using System.Runtime.InteropServices;
using AOT;

public class NewBehaviourScript : MonoBehaviour {

	[DllImport ("__Internal")]
	private static extern void DoSomething (NoParamDelegate del1, StringParamDelegate del2);

	delegate void NoParamDelegate ();
	delegate void StringParamDelegate (string str);

	[MonoPInvokeCallback (typeof (NoParamDelegate))]
	public static void NoParamCallback()
	{
		Debug.Log ("Hello from NoParamCallback");
	}

	[MonoPInvokeCallback (typeof (StringParamDelegate))]
	public static void StringParamCallback(string str)
	{
		Debug.Log (string.Format ("Hello from StringParamCallback {0}", str));
	}

	// Use this for initialization
	void Start () {
		DoSomething(NoParamCallback, StringParamCallback);
	}
}

Xcode 抛出编译错误:"ld : unable to insert branch island. No insertion point available. for architecture armv7","clang: error: linker command failed with exit code 1 (use -v to see invocation)"

这个错误通常意味着单一模块中代码过多。这主要是由于构建时使用了过多的脚本代码或大型外部 .NET 程序集。启用脚本调试可能使这一情况更加糟糕,因为它添加了不少附加指示至每个函数,因此,它更容易达到这一极限。

播放器设置 (player settings) 中启用托管代码剥离可能有助于解决这一问题,尤其是设计大型外部 .NET 程序集时。但是如果问题依然存在,那么最佳解决方案是将用户脚本代码分解成多个程序集。而这个问题最简单的解决方法是将一些代码移动至插件 (Plugins) 文件夹。这个位置的代码将放入不同程序集。同时,请参考这些脚本编译指南:

脚本编译

Android

Android 开发故障排除

"No Platforms Found" 错误信息:

  1. 卸载当前的 Android SDK,并确保您没有一个以上的副本。
  2. 更新Java 至稳定的版本。切记最新版本不应存在冲突,但是用户可能在之前遇到过问题。
  3. 下载最新 ADT 包的最新副本,并按照下列指示安装。此地址应可以正确配置开发工具:http://developer.android.com/sdk/index.html。
  4. 使用安装的 Android SDK 配置 Unity。

Unity 无法在设备上安装应用程序

  1. 确认电脑可以识别并连接设备。请参阅发布构建页面了解更多详细信息。
  2. 查看 Unity 控制台的错误信息。这通常有助于诊断问题。

如果在构建期间遇到 "Unable to install APK, protocol failure" 这一错误信息,则表明设备连接至低功率的 USB 端口(可能是键盘端口或其他外围设备)。若出现这一错误,请尝试将设备连接到电脑上的 USB 端口。

启动应用程序之后立即崩溃

  1. 确保没有使用设备不支持的本地行为 (NativeActivity)
  2. 尝试移除所有本地插件。
  3. 尝试禁用剥离。
  4. 使用 adb logcat 获取设备崩溃报告。

构建 DEX 失败

这种错误将产生类似下面的消息:-

Building DEX Failed!
G:\Unity\JavaPluginSample\Temp/StagingArea> java -Xmx1024M 
-Djava.ext.dirs="G:/AndroidSDK/android-sdk_r09-windows\platform-tools/lib/" 
-jar "G:/AndroidSDK/android-sdk_r09-windows\platform-tools/lib/dx.jar" 
--dex --verbose --output=bin/classes.dex bin/classes.jar plugins
Error occurred during initialization of VM
Could not reserve enough space for object heap
Could not create the Java virtual machine.

通常是因为计算机上安装了错误的 java 版本。升级 java 到最新版本即可解决这一问题。

游戏在播放视频时很快崩溃

确保没有启用手机上的设置 (Settings)->开发人员选项 (Developer Options)->不保持活动 (Don't keep activities)。视屏播放器有自己的活动状态,如果视频播放器被激活,将导致常规游戏活动被销毁。

按下休眠键时退出程序

更改 AndroidManifest.xml 中的 <activity> 标签,来包含 <android:configChanges> 标记,具体说明请点击此处查看。

活动标签示例如下所示:-

<activity android:name=".AdMobTestActivity"
                  android:label="@string/app_name"
                  android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

Page last updated: 2013-06-30