第3章
AR内容交互
3.1 动态加载AR模型
在第2章中我们实现了通过Vufoira默认方式识别并显示三维模型。默认的加载模型是通过将3D物体直接放置在场景中并作为识别目标的子物体来实现的,这种实现方式的问题是,当场景中有很多识别目标后,需要一次性加载的模型内容会占用很大的内存。因此,在实际项目案例中需要动态加载识别目标对应的模型,在识别丢失之后删除模型,这样可以保证同一时刻内存的占用是手机可以承受的。
本节将通过预制件实例化的方式实现AR模型的动态加载和显示。
1.创建预制件
将ImageTarget下调整好的模型拖到最上层,和ImageTarget在层级视图中保持平级关系。
在Assets目录中创建Resources文件夹,并将层级视图Hierarchy中的模型Sphere拖至Resources文件夹中,然后删除层级视图Hierarchy中的Sphere模型。
2.为需要加载的预制件添加脚本
在工程视图Project中创建一个新的C#脚本,命名为TrackObject.cs,该脚本是为了更方便地找到识别后显示的物体,将其拖到需要加载的预制件上。
在工程视图Project中找到DefaultTrackableEventHandler.cs这个脚本,按住Ctrl+D可以复制一份相同的脚本,选中新复制出来的脚本并按F2快捷键将脚本改名为MyTrackable EventHandler.cs。然后双击打开,将类名修改为MyTrackableEventHandler,找到监听识别成功和识别丢失的两个方法OnTrackingFound和OnTr ackingLost,默认代码如下:
//识别成功时调用的方法 private void OnTrackingFound() { Renderer[] rendererComponents = GetComponentsInChildren<Renderer>(true); Collider[] colliderComponents = GetComponentsInChildren<Collider>(true); // Enable rendering: foreach (Renderer component in rendererComponents) { component.enabled = true; } // Enable colliders: foreach (Collider component in colliderComponents) { component.enabled = true; } Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " found"); } //识别丢失时调用的方法 private void OnTrackingLost(){ Renderer[] rendererComponents = GetComponentsInChildren<Renderer>(true); Collider[] colliderComponents = GetComponentsInChildren<Collider>(true); // Disable rendering: foreach (Renderer component in rendererComponents) { component.enabled = false; } // Disable colliders: foreach (Collider component in colliderComponents) { component.enabled = false; } Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " lost"); }
在默认的这两个方法实现中,OnTrackingFound函数在识别图像成功之后被调用, OnTrackingLost函数在识别丢失之后被调用。该脚本利用了查找当前识别目标所有子物体的渲染器Renderer组件,通过其打开或关闭来实现3D物体的显示和隐藏。现在我们通过动态加载的方式来显示3D模型,需要删除OnTrackingLost和OnTrackingFound两个函数中的内容,并将代码修改为动态加载的方式实现:
//识别成功时调用的方法 private void OnTrackingFound() { //动态加载模型并实例化到场景中 GameObject obj = GameObject.Instantiate(Resources.Load("Sphere")) as GameObject; //生成之后设定它的父级为识别图物体 obj.transform.parent = this.transform; //设定物体与识别图在同一位置,这里可以根据需求设定 obj.transform.position = this.transform.position; } //识别丢失时调用的方法 private void OnTrackingLost() { //从子物体中查找所有TrackObject组件 TrackObject to=GetComponentInChildren<TrackObject>(); //如果找到显示物体,则销毁物体并释放无用资源 if (to ! = null) { Destroy(to.gameObject); Resources.UnloadUnusedAssets(); } }
3.效果实现
选择ImageTarget,在Inspector面板上选择Remove Component,并将DefaultTrackable EventHandler脚本移除,同时把MyTrackableEventHandler脚本添加到ImageTarget上。
最后,点击运行以测试最终效果,如下图所示。
以上为动态加载AR中3D物体的步骤和方法。
3.2 模型脱卡功能的实现
在常见的AR项目中,识别到图像后会将3D物体叠加到识别目标之上并具有追踪效果,但是当识别目标丢失后,我们希望能够将3D模型停留在屏幕中心,并能够和用户进行交互,比如点击模型伴随动画切换、播放语音讲解等一系列功能。这个功能就是我们本节将要为大家介绍的脱卡功能。
脱卡实现的原理是,将3D物体从识别目标下移出,不再将识别图作为3D物体的父对象,这样就能够实现模型不跟随识别物体的效果。接下来我们将模型用一个新建的Camera来渲染,也就是脱离识别图之后的物体是完全在另外一个Camera坐标系下的。
其实,脱卡功能还有更简单的实现方式,比如在ARCamera下创建空物体作为脱离之后模型的目标位置,然后在脱离识别图后将模型移动到空物体下。
在本节中,我们使用另外一种思路来实现脱卡功能。
1.创建工程并设置参数
新建Unity工程,分别导入Vuforia SDK插件包和识别图数据包,然后根据第1章中的设置方法,将需要添加的物体添加到场景,并调整参数以正常识别并显示3D物体。
选择ImageTarget,将其自身的DefaultTrackableEventHandler移除,并将该脚本复制一份命名为MyTrackableEventHandler,然后附加到ImageTarget上。然后创建TrackObject.cs和Rotate.cs两个文件。
双击打开Rotate.cs文件,并在该脚本Update()方法中添加一行代码以使物体旋转,代码如下:
//Rotate.cs //刷新函数,每一帧被调用一次 void Update () { //让物体绕Y轴自转 transform.Rotate(new Vector3(0f,1f,0f)); } //TrackObject.cs //TrackObject类用来查找和确定识别后显示的物体 public class TrackObject : MonoBehaviour { //脱卡之后模型的位置 public Vector3 LostPosition; }
2.脚本修改
修改MyTrackableEventHandler.cs中的OnTrackingFound和OnTrackingLost两个方法。
//识别成功时调用的方法 private void OnTrackingFound() { //获取所有TrackObject物体并将它们都销毁 TrackObject[] objects = FindObjectsOfType<TrackObject>(); foreach(TrackObject to in objects) { Destroy(to.gameObject); } //卸载无用资源 Resources.UnloadUnusedAssets(); //加载需要显示的模型并实例化到场景中 GameObject obj= GameObject.Instantiate(Resources.Load(objectName))as GameObject; //将父物体设置为ImageTarget obj.transform.parent = this.transform; //设置物体位置 obj.transform.position = this.transform.position; } //识别目标丢失时调用 private void OnTrackingLost() { //从子物体中获取所有TrackObject组件 TrackObject to = GetComponentInChildren<TrackObject>(); if (to ! = null) { //将3D物体的父对象从识别目标下移出 to.transform.parent = this.transform.parent; //设置物体的layer值,这里用另一个Camera来渲染脱卡之后的模型 to.gameObject.layer = 10; //将物体的位置设置为TrackObject脚本中设置好的位置 to.transform.position = to.LostPosition; } }
3.场景搭建
选择ImageTarget,在Inspector面板中找到MyTrackableEventHandler脚本,设定以下参数。
然后将Cube拖到与ImageTarget平级的层级,如下图所示。
接下来在Hierarchy面板中右击创建一个新的Camera,选择其Inspector面板Layer标签中的Add Layer命令创建一个新的层。
在Layer 10的位置创建一个新的图层OnShow。再调整刚才新建的Camera的Clear Flags属性如下。
选择Depth only,然后在Culling Mask下选择OnShow层,如下图所示。
4.效果实现
在Assets文件夹中新建一个名为Resources的文件夹,将Cube拖到该文件夹中生成prefab预制件,并删除在层级视图Hierarchy中的Cube物体。
选中ARCamera子物体下的Camera对象,在其Inspector面板中找到Culling Mask属性并取消勾选OnShow。
接下来,点击运行便可测试脱卡效果的实现。
3.3 手势控制
目前市场上流行的AR产品以儿童教育类产品居多,这些产品中大多会有手势交互的功能,本节就将讲解如何在AR产品中加入手势交互的功能。设想以下场景,在识别一个动物的卡片后,会出现该动物的3D立体影像,在识别丢失后,动物模型会停留在屏幕中心,并可以通过手势控制其旋转和缩放。
我们需要实现模型脱卡的功能,这个功能的实现已经在上一节做了详细讲解,本节不再赘述。
关于手势控制,最原始的方式是通过Unity 3D自带的Input类进行处理,但是为了快速实现我们想要达到的效果,可以借助一些插件来实现。用于手势处理的插件有Finger Gesture、LeanTouch和EasyTouch等,本节主要通过EasyTouch这个插件来讲解手势控制功能(本节使用的插件版本为EasyTouch 5)。
接下来,我们就逐步讲解EasyTouch的使用方法。
1.查看EasyTouch案例场景
正确导入EasyTouch插件之后,在Project视图中找到QuickGesture案例场景,这个场景中展示了插件中大部分手势的案例,该场景打开后的界面如下图所示。
上图中的场景展示了下列手势:
● Touch:触摸手势。
● Tap:点击手势。
● Long tap:长按手势。
● Drag:拖动手势。
● Enter Over Exit:滑过手势。
● Double tap:双击手势。
● Swipe:滑动手势。
● Twist:扭转手势。
● Pinch:两个或多个手指挤压,常用于缩放。
2.EasyTouch初始化
EasyTouch初始化的方式非常简单,只需在场景中创建一个空的GameObject,将其命名为EasyTouch,然后在Inspector面板上点击AddComponent添加EasyTouch.cs脚本文件即可。
3.旋转模型
初始化模型的工作完成之后,接下来演示旋转模型的实现方式。查看之前案例场景中Horizontal Swipe下的物体,你会发现当鼠标在该物体上左右滑动时,就可以让物体发生水平方向的旋转,我们查看被旋转物体上的组件信息,发现该物体拥有一个名为QuickSwipe的组件。
因此,只要给需要旋转的物体添加QuickSwipe组件,即可让物体根据手势滑动的方向进行旋转。Quick Swipe组件的参数如下图所示。
上图中的Swipe direction选项表示判断手势滑动的方向,例如Horizontal表示水平方向,Vertical表示竖直方向。Action中的选项表示当手势触发之后,所影响的物体会发生哪些类型的动作,可以选择的动作有Rotate(旋转)、Translate(移动)和Scale (缩放),在此我们选择Rotate选项即可。Affected axes表示被影响的坐标轴,如果是水平方向渲染,则选择Y轴。Sensibility表示灵敏度,可以根据灵敏度调整旋转的速度,Inverse axis表示反向。
4.缩放模型
在手机App中,一般是通过两个手指在触摸屏幕上的距离远近变化来控制模型的大小,因此我们采用EasyTouch中的另外一种Pinch事件。在上述案例场景中我们可以看到这个事件,但是在Unity编辑器中无法通过鼠标模拟多点触控,EasyTouch的解决方案是:首先用Ctrl+鼠标左键确定第一个手指的位置,然后通过Alt+鼠标左键来控制第二个手指的位置与第一个手指的位置之间的距离。
(1)Ctrl +鼠标左键确定第一个手指的位置。
(2)Alt +鼠标左键滑动控制模型缩放。
在实现缩放控制的时候,EasyTouch为我们提供的接口也特别简单。与旋转模型类似,只需要给被旋转的物体加上一个QuickPinch组件,但是不要忘记给物体再添加一个碰撞器组件。
5.拖动模型
拖动模型是指,当鼠标或手指选择屏幕中的某个物体之后,拖动手指,物体会跟随手指进行移动,如果不借助插件,则实现步骤如下。
(1)通过Unity中的Input类获取当前鼠标的位置或触摸点的位置,返回的坐标信息为屏幕坐标。
(2)将获取的屏幕坐标转换为世界坐标,这个步骤可以通过Camera中的ScreenToWorldPoint方法来实现。
(3)将模型的位置移动到转换后的世界坐标位置。
上述实现过程比较麻烦,因此我们直接采用EasyTouch中的QuickDrag组件实现。我们只需要给被拖动的物体添加QuickDrag组件。如果要实现两个手指拖动,则添加TwoDragMe这个脚本组件就可以了。
本节关于手势控制的内容已经介绍完毕,其实EasyTouch还拥有其他一些手势控制和功能,大家可以参考它的帮助文档。