最新文章
泰课在线 | 微信拼团成功后如何获取课程?
08-09 17:57
Unity教程 | 使用ARKit为iOS开发AR应用
07-31 17:23
Unity Pro专业版7折订阅四选一工具包之VR开发与艺术设计
07-28 11:47
网友使用虚幻UE4实现CAVE 多通道立体渲染的沉浸式环境
07-27 11:57
VR晕动症调查:未来5年内大部分VR晕动症将得到解决
07-27 11:26
AMD CEO:未来3-5年最重要 希望5年达1亿VR用户
07-27 10:44
Unity建立全局异常处理机制
在开发Unity项目时,我们会发现Unity本身的异常处理机制较为出色。其框架底层会自动捕获异常,所以常见的异常(如空引用、除零操作等)一般不会导致整个进程崩溃。当代码在try段中发生异常,在catch段处理后,在Unity编辑器的日志窗口会打印红色错误日志;在已发布的项目中,由于异常仍被Unity捕获,进程也不会崩溃。然而,这些未被我们察觉的异常很可能会导致程序在功能表现上出现问题。
因此,我们需要在发生任何未知异常时,获取异常的相关信息和用户的反馈信息,并且在必要时结束整个程序。毕竟,即使某些未知异常看似无关紧要,但仍可能影响部分功能的正常运行。
好在Unity对这方面的封装十分完善,我们可以借助其自身的异常处理机制来满足需求。
Unity的日志回调机制
注册日志回调函数
RegisterLogCallback是Application类中的静态方法,用于将LogCallback类型的函数注册给Unity的日志回调委托。当Unity输出任何日志时,都会调用该方法。其函数签名如下:
public static void RegisterLogCallback( Application.LogCallback handler );
在handler函数中,我们可以截获各种类型的日志,包括Debug.Log输出的日志以及Unity捕获异常时打印的异常日志。通过判断日志类型,我们可以对未知异常进行处理,例如结束程序并弹出友好的错误反馈窗口。
日志回调委托的形式
LogCallback委托的形式如下:
public delegate void LogCallback( string condition, string stackTrace, LogType type );
其中:
condition:日志的具体内容。stackTrace:相关的堆栈调用信息。type:日志的类型,日志类型分为以下五种:public enum LogType { Error = 0, Assert = 1, Warning = 2, Log = 3, Exception = 4, }下面对这五种日志类型进行详细说明:
- Error:错误日志,
Debug.LogError输出的日志属于此类型。 - Assert:Unity本身的异常,通常是致命的,会导致整个进程崩溃。
- Warning:警告日志,
Debug.LogWarning输出的日志属于此类型。 - Log:普通日志,
Debug.Log输出的日志属于此类型。 - Exception:被Unity捕获的未知异常,也就是我们自己代码产生的异常,是我们处理的重点对象。
- Error:错误日志,
完善异常处理代码
以下是一个完整的异常处理类ExceptionHandler的代码示例:
using UnityEngine;
using System;
using System.IO;
using System.Diagnostics;
using System.Collections;
public class ExceptionHandler : MonoBehaviour
{
// 是否作为异常处理者
public bool IsHandler = false;
// 是否在异常发生时退出程序
public bool IsQuitWhenException = true;
// 异常日志保存路径(文件夹)
private string LogPath;
// Bug反馈程序的启动路径
private string BugExePath;
void Awake()
{
LogPath = Application.dataPath.Substring( 0, Application.dataPath.LastIndexOf( "/" ) );
BugExePath = Application.dataPath.Substring( 0, Application.dataPath.LastIndexOf( "/" ) ) + "\\Bug.exe";
// 注册异常处理委托
if ( IsHandler )
{
Application.RegisterLogCallback( Handler );
}
}
void OnDestroy()
{
// 清除注册
Application.RegisterLogCallback( null );
}
void Handler( string logString, string stackTrace, LogType type )
{
if ( type == LogType.Error || type == LogType.Exception || type == LogType.Assert )
{
string logPath = LogPath + "\\" + DateTime.Now.ToString( "yyyy_MM_dd HH_mm_ss" ) + ".log";
// 打印日志
if ( Directory.Exists( LogPath ) )
{
File.AppendAllText( logPath, "[time]:" + DateTime.Now.ToString() + "\r\n" );
File.AppendAllText( logPath, "[type]:" + type.ToString() + "\r\n" );
File.AppendAllText( logPath, "[exception message]:" + logString + "\r\n" );
File.AppendAllText( logPath, "[stack trace]:" + stackTrace + "\r\n" );
}
// 启动bug反馈程序
if ( File.Exists( BugExePath ) )
{
ProcessStartInfo pros = new ProcessStartInfo();
pros.FileName = BugExePath;
pros.Arguments = "\"" + logPath + "\"";
Process pro = new Process();
pro.StartInfo = pros;
pro.Start();
}
// 退出程序,bug反馈程序重启主程序
if ( IsQuitWhenException )
{
Application.Quit();
}
}
}
}
处理机制说明
当接收到LogType.Error、LogType.Exception、LogType.Assert类型的日志输出请求时,程序会将日志信息打印到本地文件,同时主程序退出,并启动bug反馈程序。这里的bug反馈程序是一个WinForm窗口程序,仿照了类似QQ的报错界面。
Bug反馈程序
Bug反馈程序会接收一个参数,即日志文件的路径。这样,在反馈界面中,用户输入的引起异常的原因可以添加到错误日志中。如果用户选择发送错误报告,该错误日志将被上传到服务器。需要注意的是,如果不提供参数,直接运行bug反馈程序将无法成功。
测试异常处理机制
编写测试脚本
在Unity中新建一个测试脚本Test.cs,内容如下:
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
public GameObject TestObj;
void OnGUI()
{
if ( GUILayout.Button( "点我就会抛出一个异常" ) )
{
TestObj.transform.position = Vector3.one;
}
}
}
这里的TestObj不进行赋值,因此在OnGUI方法中的调用会引发空引用异常。
测试步骤
- 将项目发布。
- 将bug反馈程序拷贝到exe的同级目录。
- 运行
Test.exe,点击屏幕左上角的按钮。
此时,程序会捕获到异常,主程序退出,bug反馈程序启动。我们可以查看错误日志信息,这样的机制避免了手动在每个可能出现异常的地方使用try-catch语句,所有异常都能被捕获并记录详细信息,方便我们进行修改。同时,用户的反馈也是解决这些bug的重要依据。
不同平台的处理
对于不同平台,处理方式可能有所不同。例如,在移动端可能无法提供外部的bug反馈程序,但只要能够捕获到未知异常,我们可以在程序中直接弹出窗口提示报错,让用户填写相关错误反馈,点击确定后将反馈信息提交到后台,同时程序可以选择自动重启。如果某些由异常引发的界面或内容上的功能表现问题无关紧要,也可以不进行重启操作,因为只要不是Assert类型的异常,Unity一般不会崩溃。