现在,我们已经创建了场景并添加了必要的支持组件,但现在场景中什么也没有,本节中,我们将要编写我们的控制器,利用ARCore提供给我们的功能来检测平面,当然,这是一个循序渐进的过程,我们首先要确保设备支持ARCore,并进行一些必要的检查以便我们后续的工作的开展。本节中,我们的目标是创建一个简单的AppController,主要是进行各种错误处理。当然主要的工作还是由ARCore来提供,我们场景中的ARCore Device中主要处理两大类问题,跟踪用户手机姿态和Session管理,跟踪处理手机的类型、姿态并实时的更新他们,Session管理AR系统的状态,它也包含了Session配置与一个第一人称的摄像头。
既然我们要编写控制器,那么肯定要用到程序语言,如今市面上少数也有几百种程序语言。当前,在AR领域,C#是最佳的编程语言,当然也还有很多如Javascript、C++、Java也都可以用来开发AR应用。我们将主要以C#作为我们的代码编写语言,而且C#也是Unity中主要的编程语言,而Unity又是广泛运用的AR开发引擎。
一、建一个C#类
在前节中,我们创建了一个场景,现在我们开始创建一个控制器,以便来开发和测试我们的应用,我们将从头开始一步一步的创建它。首先,我们要创建一个C#类。
在创建我们的类之前,我们先建一个文件夹来存放代码文件(这是一个非常好的习惯)。在Project ,在”Assets” 上右键选择”Create”, 然后 “Folder”,创建一个文件夹,取名叫“Scripts”,以后所有的代码文件我们都放在这个文件夹里。
在我们创建的“Scripts”文件夹上右键选择”Create”, 然后 “C# Scripts”,取名叫AppController.cs
好了,现在我们创建了AppController类,双击AppController.cs会在 IDE (integrated development environment) 中打开这个类,IDE可以在Edit->Preferences->External Tools->External Script Editor 中设置,默认是monodevelop,我们使用Visual Studio 2015来开发。
二、编写控制器
在打开的代码编辑器中,默认已经有一部分代码。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AppController : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
Start()方法会在挂载脚本的物体开始时执行一次,而Update ()方法则会在每帧都更新。首先,我们添加上ARCore的命名空间。
using GoogleARCore;
添加这个命名空间后,我们就能使用Google ARCore SDK提供的功能了。
代码第4行是定义了一个类,这个类继承自MonoBehaviour,不用太关心这个,大部分的Unity类都会继承自这个类,这个基类包含了很多Unity独特的方法和功能。
如前所述,Start()方法会在挂载脚本的物体开始时执行一次,所以在这里我们可以将设备检查工作的代码写在这里,为了更好的管理我们的代码,我们另建一个方法专门负责设备检查工作,例如让用户授权摄像头的使用、弹出检查结果提示、遇到错误退出等等,使我们的AR应用更友好和更健壮。我们新建一个方法OnCheckDevice()。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GoogleARCore;
public class AppController : MonoBehaviour {
// Use this for initialization
void Start () {
OnCheckDevice();
}
// Update is called once per frame
void Update () {
}
/// <summary>
/// 检查设备
/// </summary>
private void OnCheckDevice()
{
if(Session.Status == SessionStatus.ErrorSessionConfigurationNotSupported)
{
ShowAndroidToastMessage("ARCore在本机上不受支持或配置错误!");
Invoke("DoQuit", 0.5f);
}
else if (Session.Status == SessionStatus.ErrorPermissionNotGranted)
{
ShowAndroidToastMessage("AR应用的运行需要使用摄像头,现无法获取到摄像头授权信息,请允许使用摄像头!");
Invoke("DoQuit", 0.5f);
}
else if (Session.Status.IsError())
{
ShowAndroidToastMessage("ARCore运行时出现错误,请重新启动本程序!");
Invoke("DoQuit", 0.5f);
}
}
/// <summary>
/// 退出程序
/// </summary>
private void DoQuit()
{
Application.Quit();
}
/// <summary>
/// 弹出信息提示
/// </summary>
/// <param name="message">要弹出的信息</param>
private void ShowAndroidToastMessage(string message)
{
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject unityActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
if (unityActivity != null)
{
AndroidJavaClass toastClass = new AndroidJavaClass("android.widget.Toast");
unityActivity.Call("runOnUiThread", new AndroidJavaRunnable(() =>
{
AndroidJavaObject toastObject = toastClass.CallStatic<AndroidJavaObject>("makeText", unityActivity,message, 0);
toastObject.Call("show");
}));
}
}
}
OnCheckDevice()方法中,我们首先检查手机设备时否支持ARCore,然后检查是否获取到摄像头的授权,最后再确认一下有没有错误发生。在本方法中,我们ShowAndroidToastMessage()弹出检查中需要让用户知道的信息,如果应用不能再进行下去,我们则使用DoQuit()方法主动退出应用。
ShowAndroidToastMessage()方法主要功能就是在用户屏幕上显示信息。
在Update()中,我们将开始我们应用主流程,同样为了更清晰的管理我们的应用流程,我们另建一个方法来管理应用的生命周期。我们新建一个方法UpdateApplicationLifecycle()。在UpdateApplicationLifecycle()中,我们主要做两件事,一件事是检查当前设备是否是处于有效跟踪状态,如果当前设备目前不是正在跟踪,那么我们让应用休眠一小段时间再检查,如果当前设备目前正处于有效跟踪状态,那么我则不要让应用休眠,而是一直跟踪用户设备。当然,如果当前应用处于正在退出状态,那我们则不再进行下一步的操作,如果应用正常,那我们则进入到应用的主处理环节中。具体代码如下(注意新加了一个私有变量mIsQuitting,用来标识当前应用程序状态):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GoogleARCore;
public class AppController : MonoBehaviour {
private bool mIsQuitting = false;
// Use this for initialization
void Start () {
OnCheckDevice();
}
// Update is called once per frame
void Update () {
UpdateApplicationLifecycle();
}
/// <summary>
/// 检查设备
/// </summary>
private void OnCheckDevice()
{
if(Session.Status == SessionStatus.ErrorSessionConfigurationNotSupported)
{
ShowAndroidToastMessage("ARCore在本机上不受支持或配置错误!");
mIsQuitting = true;
Invoke("DoQuit", 0.5f);
}
else if (Session.Status == SessionStatus.ErrorPermissionNotGranted)
{
ShowAndroidToastMessage("AR应用的运行需要使用摄像头,现无法获取到摄像头授权信息,请允许使用摄像头!");
mIsQuitting = true;
Invoke("DoQuit", 0.5f);
}
else if (Session.Status.IsError())
{
ShowAndroidToastMessage("ARCore运行时出现错误,请重新启动本程序!");
mIsQuitting = true;
Invoke("DoQuit", 0.5f);
}
}
/// <summary>
/// 管理应用的生命周期
/// </summary>
private void UpdateApplicationLifecycle()
{
if (Session.Status != SessionStatus.Tracking)
{
const int lostTrackingSleepTimeout = 15;
Screen.sleepTimeout = lostTrackingSleepTimeout;
}
else
{
Screen.sleepTimeout = SleepTimeout.NeverSleep;
}
if (mIsQuitting)
{
return;
}
}
/// <summary>
/// 退出程序
/// </summary>
private void DoQuit()
{
Application.Quit();
}
/// <summary>
/// 弹出信息提示
/// </summary>
/// <param name="message">要弹出的信息</param>
private void ShowAndroidToastMessage(string message)
{
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject unityActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
if (unityActivity != null)
{
AndroidJavaClass toastClass = new AndroidJavaClass("android.widget.Toast");
unityActivity.Call("runOnUiThread", new AndroidJavaRunnable(() =>
{
AndroidJavaObject toastObject = toastClass.CallStatic<AndroidJavaObject>("makeText", unityActivity,message, 0);
toastObject.Call("show");
}));
}
}
}
至此,我们已经利用ARCore提供的功能搭建了我们的应用框架,下节我们将开始做平面检测及在合适的位置放置我们的模型。