Unity-增强现实基础知识-全-
Unity 增强现实基础知识(全)
原文:
zh.annas-archive.org/md5/c06360fa4163d3be84244087f442df02译者:飞龙
前言
增强现实(AR)通过数字化增强内容,使人们能够有意义地与真实世界互动。这本书将帮助你开始使用 Unity 3D 游戏引擎和 Unity Technologies Inc.提供的 AR Foundation 工具包开发自己的 AR 应用程序。使用本书中介绍的技术和课程,你将能够为各种目标设备创建自己的 AR 应用程序和游戏。
AR 技术现在在移动消费设备上普遍可用——智能手机和平板电脑,包括 iOS(ARKit)和 Android(ARCore),以及新一代的可穿戴智能眼镜。在这本书中,我专注于指导你如何为移动设备开发 AR 应用程序,但技术和项目也可以应用于可穿戴设备。
在本书结束时,你将能够构建和运行自己的 AR 应用程序,这些应用程序将为现实世界添加信息层,使人们能够与真实和虚拟对象互动,并创新地吸引你的用户。
本书面向的对象
如果你想要开发自己的 AR 应用程序,我建议使用 Unity 和 AR Foundation,因为它是功能最强大和最灵活的平台之一。对创建 AR 项目感兴趣的开发者可以使用这本书来加速他们在学习曲线上的进步,并通过各种有趣和有趣的项目获得经验。这本书补充了 Unity 的自身文档和其他资源,并提供了实用的建议和最佳实践,让你能够快速启动并高效工作。
你不需要是 Unity 专家就能使用这本书,但一些熟悉度将帮助你更快地入门。如果你是初学者,我建议你首先运行 Unity Learn 上找到的一到两个入门教程(learn.unity.com/)。对于移动设备(iOS 和/或 Android)的开发经验也可能有所帮助。
话虽如此,我从一开始就着手,慢慢地引导你沿着学习曲线前进,随着你经验的积累,速度会逐渐加快。如果你想要了解更多内容并深入探索特定主题,我会提供大量的外部资源链接。对于有经验的读者来说,他们可以跳过他们已经知道的指令和解释。
本书涵盖的内容
第一章,为 AR 开发做准备,简要定义了 AR 后,帮助你为 AR 开发做好准备,安装 Unity 3D 游戏引擎和 AR Foundation 工具包,并确保你的系统为开发 Android(ARCore)和/或 iOS(ARKit)移动设备做好准备。
第二章,你的第一个 AR 场景,直接进入构建和运行 AR 场景,从 Unity 的 AR Foundation 样本项目中提供的示例开始,然后逐步过渡到从头开始构建自己的简单场景,了解 ARSession 组件、预制件,以及一点 C#编码。
第三章,改进开发者工作流程,教你关于故障排除、调试、远程测试和 Unity MARS 的知识,这些可以使你的开发工作流程更加高效。
第四章,创建 AR 用户框架,你将开发一个用于构建 AR 应用程序的框架,该框架管理用户交互模式、用户界面面板和 AR 入门图形,我们将将其保存为模板,以便在本书的其他项目中重复使用。
第五章,使用 AR 用户框架,你将使用上一章创建的 AR 用户框架构建一个简单的 AR 平面放置应用程序,包括主菜单、放置对象模式和 UI。本章还讨论了一些高级问题,例如使 AR 可选、确定设备支持以及为项目添加本地化。
第六章,画廊:构建 AR 应用程序,是两个章节项目的一部分。在这里,你将开发一个图片画廊应用程序,让你能够在现实世界的墙上挂上虚拟相框照片。在这个过程中,你将了解 UX 设计、管理数据和对象、菜单按钮和预制件。
第七章,画廊:编辑虚拟对象,是画廊项目的第二部分,你将学习如何在 AR 场景中实现与虚拟对象的交互,包括选择和突出显示、移动、调整大小、删除、碰撞检测以及更改相框中的照片。
第八章,行星:跟踪图像,展示了如何构建一个教育 AR 应用程序,该应用程序使用太阳系“行星卡片”的图像跟踪,实例化虚拟行星在你的桌子上悬浮和旋转。
第八章,自拍:制作搞笑表情,你将学习如何使用设备的正面摄像头制作有趣和娱乐性的面部滤镜,包括 3D 头像、面部面具(可选材质纹理)以及太阳镜和胡须等配件。它还涵盖了 ARCore 和 ARKit 特有的高级功能,这些功能可能不被 AR Foundation 本身普遍支持。
要充分利用这本书
首先,你需要一台能够运行 Unity 的 PC 或 Mac。最低要求并不困难;几乎任何今天的 PC 或 Mac 都足够了(见docs.unity3d.com/Manual/system-requirements.html)。
如果你正在为 iOS 开发,你需要一台运行 OSX 且安装了当前版本 XCode 的 Mac,以及一个 Apple 开发者账户。如果你正在为 Android 开发,你可以使用 Windows PC 或 Mac。
没有能够运行您应用程序的设备,开发 AR 是不切实际的。您应该有一个支持 Apple ARKit 的 iOS 设备(在网上搜索;苹果似乎没有发布列表——例如,您可以在这里查看:ioshacker.com/iphone/arkit-compatibility-list-iphone-ipad-ipod-touch),或者一个支持 ARCore 的 Android 设备(developers.google.com/ar/discover/supported-devices)。
在第一章**,设置 AR 开发环境中,我向您介绍了安装 Unity Hub、Unity 编辑器、目标设备的 XR 插件、AR Foundation 工具包和其他软件,以帮助您设置环境。本书中的项目是用 Unity 2021.1 编写和测试的。
由于技术正在快速发展,我尽量关注现有的稳定工具和技术。关于软件版本和安装说明,自然,事情可能会变化,我建议您将我的说明作为指南,但也查看在线文档(通常提供链接)以获取最新的说明。
如果您正在使用本书的数字版,我们建议您亲自输入代码或从本书的 GitHub 仓库(下一节中有一个链接)获取代码。这样做将帮助您避免与代码复制和粘贴相关的任何潜在错误。
下载示例代码文件
您可以从 GitHub 下载本书的示例代码文件:github.com/PacktPublishing/Augmented-Reality-with-Unity-AR-Foundation。如果代码有更新,它将在 GitHub 仓库中更新。
我们还从我们丰富的图书和视频目录中提供了其他代码包,可在github.com/PacktPublishing/找到。查看它们吧!
下载彩色图像
我们还提供了一份包含本书中使用的截图和图表彩色图像的 PDF 文件。您可以从这里下载:static.packt-cdn.com/downloads/9781838982591_ColorImages.pdf。
使用的约定
本书使用了多种文本约定。
文本中的代码:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个示例:“利用 Unity Input System 包,我们将首先添加一个新的SelectObject输入动作。”
代码块设置如下:
public void SetPlacedPrefab(GameObject prefab)
{
placedPrefab = prefab;
}
当我们希望您注意代码块中的特定部分时,相关的行或项目将以粗体显示:
using UnityEngine;
using UnityEngine.InputSystem;
public class GalleryMainMode : MonoBehaviour
{
void OnEnable()
{
UIController.ShowUI("Main");
}
}
粗体: 表示新术语、重要单词或屏幕上看到的单词。例如,菜单或对话框中的单词以粗体显示。以下是一个示例:“在新场景对话框中,选择ARFramework模板。”
小贴士或重要注意事项
看起来像这样。
联系我们
我们始终欢迎读者的反馈。
一般反馈: 如果你对本书的任何方面有疑问,请通过客户关怀给我们发邮件,并在邮件主题中提及书名。
勘误: 尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,我们将不胜感激,如果您能向我们报告这一点。请访问www.packtpub.com/support/errata并填写表格。
盗版: 如果你在互联网上以任何形式遇到我们作品的非法副本,如果你能提供位置地址或网站名称,我们将不胜感激。请通过版权联系我们,并提供材料的链接。
如果您有兴趣成为作者: 如果您在某个领域有专业知识,并且您有兴趣撰写或为本书做出贡献,请访问authors.packtpub.com。
第一部分 – 开始使用增强现实
本节提供了使用 Unity 开发 AR 应用程序和游戏的基本介绍,包括 AR 技术概念以及如何使用 Unity 编辑器。我们涵盖了 Unity XR 插件架构、AR Foundation 工具包以及其他生产力工具。在本节结束时,你将准备好开始使用 Unity 创建自己的 AR 应用程序。
本节包括以下章节:
-
第一章,为 AR 开发做准备
-
第二章,你的第一个 AR 场景
-
第三章,改进开发者工作流程
第一章:第一章:为 AR 开发设置
增强现实(AR)被广泛认为是下一代计算平台,其中数字内容无缝融入现实世界体验。这本书将帮助你开始使用 Unity 3D 游戏引擎和 Unity 提供的 AR Foundation 工具包开发自己的 AR 应用程序。
在本章中,你将通过使用 Unity 3D 游戏引擎设置你的计算机以进行 AR 开发来迈出第一步。我们将首先简要定义增强现实,从而为这个行业以及 AR 技术的一些基本概念设定背景。然后,我们将安装 Unity 软件、AR Foundation 工具包,并确保你的系统已经设置好以开发 Android 和/或 iOS 移动设备。最后,我们将构建并运行一个测试场景,以验证一切是否按预期工作。
我们将涵盖以下主题:
-
定义增强现实
-
Unity 入门,包括安装和使用 Unity
-
为 AR 开发准备你的项目,包括 XR 插件、AR Foundation、输入系统以及通用渲染管线
-
为移动开发设置(Android ARCore 和 iOS ARKit)
经验丰富读者的注意事项
如果你已经熟悉 Unity,已经在你的系统上安装了它,并且已经设置好为 iOS 或 Android 移动设备构建,你可能可以快速浏览本章中穿插的与这些主题相关的细节。
技术要求
首先,你需要一台能够运行 Unity 的 PC 或 Mac。最低要求并不困难;几乎任何今天的 PC 或 Mac 都足够使用(见docs.unity3d.com/Manual/system-requirements.html)。
如果你正在为 iOS 开发,你需要一台运行 OSX 的 Mac,并安装了当前版本的 XCode,以及一个 Apple 开发者账户。如果你正在为 Android 开发,你可以使用 Windows PC 或 Mac。我们将在本章中进一步讨论这个问题。
没有能够运行你的应用程序的设备,开发 AR 是不切实际的。对于本章(以及本书整体),你需要一台支持 Apple ARKit 的 iOS 设备(由于 Apple 似乎没有发布列表,你可以在网上搜索;例如,ioshacker.com/iphone/arkit-compatibility-list-iphone-ipad-ipod-touch)或一台支持 ARCore 的 Android 设备(developers.google.com/ar/discover/supported-devices)。
因为本章主要关于根据您的需求安装工具和包,请仔细阅读本章内容以了解额外的技术要求,并学习如何安装它们。本书的 GitHub 仓库可以在github.com/PacktPublishing/Augmented-Reality-with-Unity-AR-Foundation找到。
定义增强现实
根据 Merriam-Webster 词典,单词增强的意思是“使更大、更多、更大或更强烈”,而现实被定义为“真实存在的质量或状态。”考虑到这一点,我们意识到“增强现实”就是利用数字内容来改善我们的现实世界,为我们的体验添加更好的信息、理解和价值。
增强现实最常与视觉增强相关联,其中计算机生成的图形与实际现实世界的视觉相结合。例如,当使用手持手机或平板电脑时,AR 将图形与屏幕上的视频结合(我称之为视频透视AR)。使用可穿戴 AR 眼镜,图形直接添加到您的视觉场中(光学透视AR)。
但 AR 不仅仅是计算机图形叠加。在他的备受赞誉的 1997 年研究报告《增强现实概述》(www.cs.unc.edu/~azuma/ARpresence.pdf)中,Ronald Azuma 提出了 AR 必须满足以下特征:
- 结合现实与虚拟:虚拟对象被感知为与您周围物理空间共享的现实世界对象。
-
实时交互:AR 是实时体验的,不是预先录制的。例如,结合真实动作与计算机图形的电影特效不算作 AR。
- 注册在 3D 中:图形必须注册到现实世界的 3D 位置。例如,一个抬头显示(HUD)只是简单地在视觉场中叠加信息,这不属于增强现实。
要在 3D 中注册虚拟对象,AR 设备必须能够跟踪其在 3D 空间中的位置并将周围环境映射到场景中以放置对象。存在多种用于位置和方向跟踪(统称为姿态跟踪)的技术和技巧,以及环境特征检测,包括以下内容:
-
地理定位:GPS 提供对地球上的位置的低分辨率跟踪(GPS 精度以英尺或米为单位)。这通常足以在城市中导航和识别附近的商业机构,例如,但不适用于更具体的定位。
-
图像跟踪:设备摄像头的图像可以用来匹配预定义或实时 2D 图像,如二维码标记、游戏卡或产品包装,以显示跟踪图像姿态(相对于相机空间的 3D 位置和方向)的 AR 图形。
-
运动跟踪:使用设备的相机和其他传感器(包括惯性测量仪运动传感器),您可以在 3D 中计算您的位置和方向,并检测环境中的视觉特征。在学术上,您可能会看到这被称为 同时定位与地图构建(SLAM)。
-
环境理解:当检测到环境中的特征,如 X-Y-Z 位置深度点时,它们可以被聚类以识别水平和垂直平面,以及 3D 中的其他形状。这些可以被您的应用程序用于对象放置和与真实世界对象的交互。
-
人脸和物体跟踪:增强现实自拍图片使用相机检测人脸并映射一个可用于添加面部面具或其他(通常是幽默的)增强功能的 3D 网格。同样,其他形状的物体可以被识别和跟踪,这可能适用于工业应用。
在这本书中,我们将使用 Unity 的 AR Foundation 工具包在真实项目中使用许多这些技术,以便您学习如何构建各种 AR 应用程序。我们还将学习 Unity 和 AR 软件提供的许多其他细节和能力,所有这些都将用于提高您图形的质量和真实感,并为您的用户提供引人入胜的交互体验。
就像所有技术一样,AR 可以被用于更好的或更坏的目的。在 2016 年 Keiichi Matsuda 的 Hyper-Reality 艺术视频中,可以看到一个关于假设的令人不安的未来,其中 AR 无处不在,就像今天的移动媒体技术一样令人着迷(hyper-reality.co/)。希望您能帮助构建一个更好的未来!

图 1.1 – Keiichi Matsuda 的超现实视频(经许可使用)
在这本书中,我们使用 Unity 3D 游戏引擎进行开发(unity.com/),以及 AR Foundation 工具包。AR Foundation 在 Google ARCore、Apple ARKit、Microsoft HoloLens、Magic Leap 等提供的特定于设备的系统功能之上提供了一个设备无关的 SDK。有关进一步阅读和了解移动手持增强现实的好介绍,请查看以下链接:
-
ARCore 基本概念:
developers.google.com/ar/discover/concepts -
在 Unity 中开始 AR 开发:
developers.google.com/ar/discover/concepts
让我们用 Unity 开始开发 AR 应用程序。首先,您需要在您的开发计算机上安装 Unity。
使用 Unity 开始
要使用 Unity 开发 AR 应用程序,您需要在您的开发机器上安装 Unity。在本节中,我们将通过使用 Unity Hub 步步讲解安装过程,创建一个新的 Unity 项目,并介绍使用 Unity 编辑器界面的基础知识。
安装 Unity Hub
Unity Hub 是一个桌面应用程序,充当开发者使用 Unity 的工作流程中可能需要的许多资源的门户。目前,我们将使用 安装 菜单来安装 Unity 编辑器的一个版本。然后,我们将使用 项目 菜单来创建和管理我们的 Unity 项目。为此,请按照以下步骤操作:
-
请从
unity3d.com/get-unity/download下载并安装 Unity Hub 程序。通常,您总是希望使用 Unity Hub 来安装 Unity 的版本,而不是直接下载 Unity 版本安装程序。 -
如果您还没有激活 Unity 用户许可证,可能需要这样做。对于 学生 和 社区 计划,这是免费的;您可以在以后决定升级到 高级 或 专业 计划。所有许可证计划都包括相同的 Unity 版本;免费计划不会禁用任何功能。付费计划增加了对专业云服务的访问权限,这些服务非常有用,但对于项目开发并非必需。
-
使用以下截图所示的 下载 Unity Hub 按钮(您可能需要首先同意 服务条款):
![图 1.2 – 安装 Unity Hub 而不是直接下载 Unity]()
图 1.2 – 安装 Unity Hub 而不是直接下载 Unity
-
安装并打开 Unity Hub 后,您将看到 学习 和 社区 菜单。
点击 学习 将您带到 Unity Learn 项目和教程(包括各种项目资产的下载)。这些教程可以从 5 分钟的快速教程到需要 15 小时才能完成的项目不等!
社区菜单提供了许多其他 Unity 托管资源的链接,包括 Unity Now 会议演讲、Unity 博客、问答、Q&A 和 论坛。
现在,让我们安装一个 Unity 编辑器的版本。
安装 Unity 编辑器
当开始一个新项目时,我喜欢使用这本书 GitHub 仓库的最新 .README 文件。现在安装 Unity 编辑器,操作如下:
-
选择 安装 选项卡,然后按 添加 打开 添加 Unity 版本 窗口。
-
从这里,您可以选择要安装的 Unity 版本。
注意 – Unity 版本
在当前的 Unity 版本编号系统中,主版本号(例如,Unity 2020.x.x)大致与日历年份相关联。最稳定的版本是标记为 LTS 的版本,即 长期支持;例如,Unity 2020.3.14f1 (LTS)。LTS 版本会定期接收维护和安全更新,但没有新功能。低于 LTS 的点版本(例如,Unity 2021.1.15f1)被认为是技术版本,它们在开发新功能和修复错误时相对稳定。对于更冒险的用户,Beta 和 Alpha 预发布版包括前沿功能,但风险也更高。
-
一旦您选择了希望安装的 Unity 版本,点击下一步以查看添加模块到您的安装选项。在这里,您需要知道您期望针对哪些平台和设备进行项目开发。
模块软件可能相当大,安装可能需要时间,因此请只选择您知道您很快就会需要的模块。您总是可以在以后回来并根据需要添加(或删除)模块。具体来说,如果您正在为 Android 和 ARCore 开发 AR 项目,请选择Android 构建支持。如果您针对 iOS 和 ARKit,请选择iOS 构建支持。同样,如果您针对其他设备,如 HoloLens 或 Magic Leap,请选择相应的模块。
-
根据您选择的模块,您可能需要点击下一步并接受额外的用户许可协议。然后,点击完成以下载和安装软件。
小贴士 – Unity 的安装位置
使用 Unity Hub 窗口右上角的齿轮图标可以打开首选项窗口。在常规首选项选项卡下,您可以选择您的用户编辑器在计算机上安装的文件夹。由于这些文件可能占用相当大的磁盘空间,您可能不想使用默认位置。
如果您在 Unity Hub 中遇到任何问题或希望参与讨论,请访问 Unity 社区讨论论坛的相关部分,网址为 forum.unity.com/forums/unity-hub.142/。
现在,您已准备好创建您的第一个 Unity 项目。
创建和管理 Unity 项目
您将使用 Unity Hub 创建新的 Unity 项目。项目在系统中的特定文件夹中创建,包含基于您选择的起始模板的默认设置和内容的子文件夹。项目使用特定的 Unity 版本打开,并继续与该特定版本关联。要开始新项目,请完成以下步骤:
-
打开 Unity Hub,选择项目选项卡,然后点击新建按钮。注意新建按钮下方的向下箭头,它允许您选择用于新项目的 Unity 版本,该版本与您当前安装的版本不同。
-
D:\Documens\UnityProjects:![图 1.3 – 在 Unity Hub 中使用 URP 模板创建新项目![图 1.03 新项目]()
图 1.3 – 在 Unity Hub 中使用 URP 模板创建新项目
注意 - 我们正在使用通用渲染管线
Unity 提供了多个替代渲染管线。传统的“内置”渲染管线由于在较新的 可脚本渲染管线(SRP)系统之前出现,因此对旧版第三方资产的支持更好(
unity.com/srp),但基于较新 SRP 的管线性能更优且更灵活。这些包括用于使用高端图形硬件进行高质量渲染的 高级渲染管线(HDRP),以及非常快速且在移动设备上也能提供优秀渲染质量的 通用渲染管线(URP)。我建议使用 URP 开始新的 AR 项目。小贴士 - 避免在项目名称中使用空格
在撰写本文时,某些 ARCore 功能中存在一个错误,要求您的项目路径名称中不包含空格,包括项目名称以及树中的所有文件夹名称。
-
在按下 创建 后,Unity 可能需要一些时间来创建您的新项目、导入默认资产以及执行其他设置步骤,然后才能打开 Unity 编辑器窗口。
小贴士 - 升级 Unity 项目
Unity Hub 的一个优点是它能够管理多个版本的 Unity 和所有您的 Unity 项目。我倾向于使用最新的官方版本开始新项目,尽管不可避免地,新的 Unity 编辑器版本将会发布。一般来说,最好坚持使用您创建项目时使用的 Unity 版本。如果您需要升级到新版本,请谨慎且故意地这样做。
通常,升级到新的小版本(例如,从 Unity 2021.2.3 升级到 2021.2.16)是安全的。升级到点版本(例如,从 Unity 2021.2.x 升级到 2021.3.x)通常是可以的,但您可能会遇到意外问题。在我的项目中,升级到新的大版本是一个不寻常的事件。在任何这些情况下,确保在以不同版本的 Unity 打开项目之前,您的项目已经备份(例如,在 GitHub 上),并安排时间解决意外问题。
Unity 包含自动化工具,以便在 Unity 中打开项目时将其升级到新版本。您的资产将被重新导入。虽然支持升级到新版本,但不支持降级到旧版本。
当我在 Unity 中创建新项目时,我做的第一件事之一是在 构建设置 中设置 目标平台 为我知道我将要用于开发和测试项目的第一个平台。尽早这样做有优势,因为您添加到项目的任何新资产都将导入并为您目标平台进行处理。您现在不必须这样做,但我确实建议您执行以下步骤。我们将在本章后面的平台特定主题部分中详细介绍。
在 Unity 中打开您的项目后,请按照以下步骤操作:
-
通过访问文件 | 构建设置来打开构建设置窗口。
-
在平台选择面板中,选择您的目标平台。例如,如果您为 Android ARCore 开发,请选择Android,如果您为 Apple ARKit 开发,请选择iOS。
如果您需要的平台未列出或已禁用,您可能忘记在安装此版本的 Unity 时添加平台构建模块。现在使用Unity Hub添加模块。
小贴士 – 您可以通过 Unity Hub 添加目标平台模块
如果您缺少对目标平台的支持,请打开Unity Hub,点击安装,然后,对于您正在使用的特定 Unity 版本,点击 3 点上下文菜单并选择添加模块。从那里,您可以使用复选框添加新模块。
-
您现在不需要担心其他构建设置。按下切换平台按钮。重新导入项目资产可能需要几分钟时间。
到目前为止,您的 Unity 编辑器应该已经打开了一个新的 Unity 项目,显示默认的 URPSampleScene。您可以自由地探索编辑器窗口和场景对象。一开始可能看起来令人畏惧,但我们将回顾用户界面,以帮助您更加熟悉。
介绍 Unity 编辑器界面
当您第一次打开 Unity 编辑器时,您会注意到它有许多不同的窗口面板,包含不同的内容。让我们一起来探索这些。
以下截图显示了带有通用渲染管道模板的Unity 编辑器的SampleScene。窗口按照默认布局排列。这个“建设中”的场景展示了 Unity 许多令人惊叹的渲染功能,这些功能可能在 AR 项目中相关或不相关。但让我们先专注于 Unity 本身:

图 1.4 – 打开 URP 示例场景的 Unity 编辑器
Unity 编辑器以单独的标签页窗口布局排列。编辑器窗口是一个包含特定类型信息和控制的 UI 面板。可以通过窗口主菜单打开更多窗口。让我们回顾先前的截图中的每个窗口,并介绍一些其他基本术语,因为您正在了解 Unity:
-
层次结构窗口(1):当前场景 GameObject 的树形视图。以父子对象的层次树视图显示与场景相同的内容。
您可能已经注意到,在检查场景和层次结构窗口时,安全帽GameObject 当前被选中并突出显示。Unity 的GameObject是场景中的一部分对象。
-
场景视图窗口(2):这显示了当前场景的 3D 视图。在场景窗口的顶部有一个图标工具栏,用于控制场景的工作视图。
-
检查器窗口(3):所选 GameObject 的组件和属性。
游戏对象附有组件,这些组件定义了游戏对象的运行时行为。Unity 包含许多内置组件,您可以使用 C#编程语言编写自己的组件。每个组件可能有单独的属性;即控制组件的设置。
例如,您可以看到安全帽有变换和网格渲染器组件。
游戏对象始终有一个变换组件。游戏对象也可能有一个 3D 网格、渲染器和材质,这些决定了它在场景中的渲染方式。您还可以添加许多其他组件来扩展对象的行为、物理和交互。
-
(
Assets/文件夹,位于项目根目录下)。资产包括可能添加到场景中对象的文件,例如图像、音频、视频、材质和脚本。场景本身作为资产保存。复杂预定义的游戏对象也可以作为资产保存,称为预制件。
-
控制台窗口(4,位于项目标签页之后隐藏):显示应用程序的错误和信息消息。
-
游戏视图窗口(2,位于场景标签页之后隐藏):显示用户视图,由场景中的相机游戏对象渲染。
-
主菜单:位于编辑器窗口顶部,是一个可以访问 Unity 许多功能的菜单。向项目中添加额外的包可能会添加更多的菜单项。
-
主工具栏:位于编辑器窗口顶部和主菜单下方,是一个分为三个部分的图标工具栏。在左侧,有用于编辑场景视图的工具(包括移动工具、旋转工具和缩放工具)。在中间,有播放模式控制(包括播放和暂停)。最后,在右侧,有额外的控制项,包括一些允许您访问您的 Unity 账户和云服务的控制项。
花点时间探索主菜单项:
-
文件菜单用于创建、保存和加载场景以及访问您的构建设置。
-
编辑菜单用于在项目中选择和编辑对象,访问项目特定的设置和首选项,以及其他与编辑器相关的工具。
-
(
Assets/文件夹)。 -
游戏对象菜单允许您向当前场景添加新对象。
-
组件菜单提供了一个分类列表,您可以在场景中当前选定的游戏对象上添加这些组件。
-
窗口菜单是您可以找到并打开提供更多功能的额外窗口的地方。将新包导入 Unity 可能会添加新的菜单栏项。
信息 - 在 AR 项目中使用播放模式
在大多数 Unity 项目中,您可以按 Play 按钮(在主工具栏中)进入 播放模式 并在编辑器中运行您的场景,在您的桌面上运行而不是在设备上。对于增强现实场景来说,这并不简单,因为它需要一个启动阶段,软件会扫描环境中的物理世界特征,然后使用物理设备的传感器进行位置跟踪。有几种解决方案可以简化您的迭代开发工作流程,我们将在 第三章**,改进开发工作流程 中讨论。
您可以根据自己的需求和偏好个性化并重新排列编辑器窗口布局。可以使用编辑器右上角的 Layout 选择菜单保存和加载布局。本书中的截图使用的是与 Unity 默认布局不同的布局。
好了,别再说了——这是一本实践手册,所以让我们立刻动手,尝试一下 Unity 编辑器。
Unity 编辑器使用基础
在本节中,我们将构建一个包含 3D 立方体的简单场景,以便我们能够更好地解释如何使用 Unity:
-
通过选择主菜单中的 File | New Scene 创建一个新的场景。
-
将出现一个 New Scene 窗口(仅适用于 Unity 2020+),让您选择场景模板。选择名为 Basic (Built-in) 的模板。然后,按 Create。
您会立即注意到新场景包含两个默认的 GameObject:主摄像机和方向光。
-
通过点击 GameObject | 3D Object | Cube 将 3D 立方体添加到场景中。这样,立方体就会被添加到场景中,并在 Scene 和 Hierarchy 窗口中可见。
-
确保立方体位于场景的原点;也就是说,
(0, 0, 0)X-Y-Z 坐标。在 Hierarchy 窗口中选中 Cube,在 Inspector 窗口中设置其 Transform | Position | X、Y 和 Z 的值都为 0。 -
让我们旋转立方体。在相同的
-20。场景现在可能看起来如下:

图 1.5 – 带有 3D 立方体的新场景
到目前为止,我鼓励您熟悉 Scene 视图控制。例如,在 Windows 上使用三按钮鼠标,在窗口中 右键单击 来旋转视图,Alt + 左键单击 来围绕视图的“中心”旋转视图,中心单击 鼠标来移动视图。要靠近或远离(缩放),请使用 Alt + 右键单击 或使用鼠标滚轮。请注意,Scene 窗口右上角的定向 Gizmo 表示当前视图,显示 X、Y 和 Z 轴。有关更多信息(包括单按钮或双按钮鼠标),请参阅 docs.unity3d.com/Manual/SceneViewNavigation.html。
提示——RGB == XYZ
记住,在 Gizmo 中的红色、绿色和蓝色颜色分别对应 X、Y 和 Z 轴是有用的。
我们通过在检查器窗口中编辑其数值来修改了立方体的变换。您也可以通过直接在场景窗口中操作对象来变换对象。例如,在主工具栏中,选择旋转工具。当立方体被选中时,您现在应该看到场景中对象上的旋转控件。您可以抓住控件(X、Y 或 Z)中的一个并拖动它来绕该轴旋转对象,如下面的截图所示:
![图 1.6 – 选择旋转工具
![img/Figure_1.06-cube-rotate-tool.jpg]
图 1.6 – 选择旋转工具
有关在场景窗口中直接变换对象的更多信息,请参阅 Unity 手册(docs.unity3d.com/Manual/PositioningGameObjects.html)。
这是一个非常简短的介绍,以帮助您开始。作为一个习惯,您应该在完成某事之后总是保存您的作品。让我们按照以下步骤保存场景:
-
从主菜单中选择文件 | 另存为,这将打开保存场景窗口。
-
导航到
Scenes/子文件夹(在您的项目Assets文件夹中)。 -
给场景起一个名字,例如
My Cube,然后按保存。小贴士 – 感到困惑或不知所措?一次只迈出一小步
就像任何专业开发和创意应用一样,Unity 提供了大量您可以做的事情,并且它提供了许多工具来帮助您实现目标。如果您感到困惑或不知所措,一个很好的策略是只关注您现在需要的菜单项和窗口,忽略其余部分。我们将通过简单的分步说明向您介绍这个过程。随着您经验的积累和自信的提升,您将扩大您熟悉的范围,并了解它们是如何相互关联的。说实话,每次我在项目上工作时,我都会学到关于 Unity 的新知识。
当然,这只是 Unity 的简要介绍。如果您需要了解更多信息,请前往Unity Learn,那里有一些优秀的入门教程(使用unity.com/learn/get-started链接或Unity Hub中的学习标签)。
此外,还可以查看Unity 手册的入门主题(docs.unity3d.com/Manual/UnityOverview.html)。
组织您的项目资产
您可以在项目窗口中访问您的项目资产。我喜欢将我自己创建的项目资产保存在自己的顶级文件夹中,与其他可能从第三方来源(如 Unity Asset Store)导入的资产分开。
同样,Unity 的 URP 项目模板包括SampleScene和示例资产。我建议将 URP 示例资产移动到自己的文件夹中,以保持它们与您的应用程序资产分离。您可以通过以下步骤完成此操作:
-
创建一个名为
URP-examples的Assets文件夹。在URP-examples。 -
将每个示例文件夹拖放到
URP-examples中,即ExampleAssets、Materials、Scenes、Scripts、TutorialInfo和Readme文件。 -
将
Presets和Settings文件夹留在根Assets/文件夹中。 -
创建一个名为
_App的Assets文件夹。我喜欢在文件夹名称前加上下划线,以便它保持在列表的顶部。 -
在
_App/内部创建名为Materials、Prefabs、Scenes和Scripts的子文件夹。这些子文件夹目前将保持为空,但我们在阅读本书的过程中将向它们添加内容。
在 Unity 中,按照文件类型组织您的资产是一种常见的约定,但您可能有自己做事的方式。Unity 不依赖于这些文件夹名称或资产文件位置。(尽管如此,Unity 有一些具有特殊含义的保留文件夹名称;请参阅docs.unity3d.com/Manual/SpecialFolders.html)。您的项目窗口现在可能看起来如下所示:

图 1.7 – 重新组织项目资产文件夹
我认为我们现在可以继续前进,继续设置您的系统并安装您为 AR 开发所需的包。我们将首先向您的项目中添加 AR 设备插件,然后对基础包做同样的操作。
为 AR 开发准备您的项目
当您开发和构建用于增强现实的项目时,Unity 需要知道您要针对的设备和平台。这是一个多步骤的过程,包括将设备插件添加到您的项目中,并在构建设置中设置目标平台。我们将在本章中现在讨论设备插件,并在稍后讨论构建设置。
下面的图示显示了 Unity XR 技术架构。如您所见,在堆栈底部是各种 AR(和 VR)提供程序插件:

图 1.8 – Unity XR 技术堆栈
在堆栈的底部是XR 插件,这些是独立的提供程序包,它们实现了与特定设备的软件接口。插件允许 Unity 通过将 Unity XR 子系统与操作系统和运行时 API 连接起来与设备通信。通常,您不会直接使用插件,而是使用更高级的工具包,例如 AR Foundation(我们将在下一节中安装)。一些插件由 Unity Technologies 提供和维护;其他是由供应商支持的第三方插件。
在前面的图中,在插件顶部是XR 子系统,它们构成了XR 插件框架。这把一系列功能抽象成单独的 API。当应用程序运行时,它可以查询当前运行时设备的性能,并根据需要启用或禁用应用程序中的功能集。在XR 子系统之上是AR 基础工具包(以及XR 交互工具包),它为你的 Unity 应用程序提供主要的 AR API。我们将在这本书的项目中广泛使用 AR 基础。
现在,让我们安装这个项目所需的 XR 插件。
安装 AR 设备的 XR 插件
为了准备我们的项目进行 AR 开发,我们将通过XR 插件管理窗口安装目标设备的 AR 设备插件。在 Unity 中打开你的项目后,按照以下步骤操作:
-
通过从主菜单中选择编辑 | 项目设置来打开项目设置窗口。
-
在左侧的设置菜单中,选择XR 插件管理。
-
点击安装 XR 插件管理按钮。Unity 导入和编译包脚本可能需要一点时间。
-
如果需要,再次单击XR 插件管理项以显示插件提供者和其他选项。请注意,每个目标平台都有一个标签页。选择你首先将针对的标签页。
例如,在XR 插件管理窗口中,只有当你通过 Unity Hub 安装 Unity 时安装了Android 构建支持模块,Android标签页才会可用。
-
勾选你想要使用的 AR 插件的复选框。例如,对于 Android,选择ARCore,而对于 iOS,选择ARKit。
小贴士 – 不要在同一个项目中混合 VR 和 AR 插件
你会看到XR 插件管理窗口允许你选择任何组合的 AR 和 VR 插件。在我们的项目中,我们只对 AR 插件感兴趣。通常,不要在同一个项目中包含 AR 和 VR 插件,因为构建设置、玩家设置、相机装置以及许多其他事物在 AR 和 VR 项目之间可能存在显著差异。(也许当你阅读这篇文章时,会有支持两种模式的单一应用程序的设备,但到目前为止,我并不了解任何这样的设备。)
在以下截图的项目设置窗口中,我已选择了XR 插件管理设置菜单。在我的窗口中,为这个项目可能的目标平台(我已安装的)每个都设置了三个标签页:桌面、iOS 和 Android(你的可能不同)。当选择Android标签页时,你可以看到我已经勾选了ARCore插件。你也会注意到,在左侧,还有一个额外的ARCore菜单项,你可以点击它来查看该插件特有的选项:
![图 1.9 – 已选择 ARCore 插件的 XR 插件管理窗口
![图 1.9 – 已选择 ARCore 插件的 XR 插件管理窗口
图 1.9 – 已选择 ARCore 插件的 XR 插件管理窗口
有趣的是,XR 插件管理器 是在 包管理器 中安装相应包的快捷方式。你可以通过以下步骤打开 包管理器 并检查已安装的包来验证这一点:
-
从主菜单打开包管理器,并选择 窗口 | 包管理器。
-
确保包管理器窗口左上角的过滤器选择为 项目中的包。
-
你应该在列表中看到你的插件;例如,ARCore XR 插件。
例如,在以下 包管理器 的屏幕截图(显示 项目中的包,位于窗口的左上角)中,ARCore XR 插件 已安装并选中。你可以看到,此特定版本的插件已针对此项目使用的 Unity 版本进行了 验证。它还显示了插件的功能描述、文档链接和其他详细信息。此外,我已经展开插件的 其他版本 列表,以向您展示如何查看插件的每个版本;这是您可能升级(或降级)插件到不同版本的地方:
![图 1.10 – 包管理器,此项目中已安装 ARCore XR 插件]
![img/Figure_1.10-package-manager-arcore.jpg]
图 1.10 – 包管理器,此项目中已安装 ARCore XR 插件
到目前为止,如果你想直接使用 XR 子系统的开发者界面 C# 编写代码,你可以开始开发一个增强现实项目。然而,更有可能的是你会安装一个更高级的、对 Unity 开发者更友好的工具包。尽管如此,你可能需要直接进入插件框架来访问 XR 子系统。例如,你可能希望扫描并启动特定的子系统,如示例所示 docs.unity3d.com/Manual/xrsdk-runtime-discovery.html。在本书的后续部分,我们可能需要访问插件框架的 SDK。大部分情况下,我们将使用更高级的 AR 基础 工具包。
安装 AR 基础包
AR 基础是一个包,它为你的应用程序和底层设备功能和插件之间提供了一个开发层。AR 基础提供了组件和其他资产,帮助你一次性构建 AR 项目,然后部署到多个移动和可穿戴 AR 设备。正如 Unity 所说,使用“统一的工作流程”,你的应用程序可以支持当前和未来的功能,这些功能可能在运行时在最终用户的特定设备上可用或不可用。这有助于“为 AR 应用程序的未来做好准备”。在本节中,我们将安装并探索 AR 基础。
AR 基础支持的功能将取决于目标设备的当前功能,并且在不同版本的 AR 基础之间有所不同。以下图表显示了 AR 基础提供的每个平台的功能支持:
![图 1.11 – AR 基础 4.1.5 每个平台的功能]

图 1.11 – AR Foundation 4.1.5 各平台功能
请参阅 AR Foundation 文档页面的平台支持部分(docs.unity3d.com/Packages/com.unity.xr.arfoundation@latest/index.html),以获取你使用的版本的最新详细信息。
Unity 提供了一个包管理器,它允许你通过在你的项目中安装额外的包来扩展 Unity 的核心功能。这样,你可以为特定项目选择所需的功能。我们在上一节中安装的 XR 插件是包。现在,我们将使用包管理器安装AR Foundation包。
信息 - Unity 包的优势
Unity 包带来了许多优势。Unity 可以独立于其他功能更新核心编辑器。同样,包可以在 Unity 核心发布周期之外更新。解耦它们的依赖性可以降低进度延误和技术问题的风险,从而实现更敏捷的开发周期,并支持 Unity 办公室内外技术进步。例如,如果苹果发布了 ARKit 的更新,那么 Unity 可以发布其 ARKit 插件的更新,而无需等待 Unity 编辑器的下一个版本发布,也不依赖于 Unity 核心开发团队。如果你曾经与多个团队合作完成过大型项目,你将能够欣赏这种架构的好处。团队可以组织起来,专注于他们包提供的细节,然后测试与 Unity 核心产品的成功集成。
你可以通过以下步骤使用包管理器安装 AR Foundation:
-
通过主菜单转到Window | Package Manager打开包管理器。
-
将左上角的包过滤器设置为Unity Registry,以查看所有官方包的列表。
-
在搜索框中输入
ar。你现在应该能看到列表中所有与 AR 相关的包。 -
注意包的版本号很重要,以及该特定版本是否已经与你在项目中使用的 Unity 版本进行了验证。
-
选择AR Foundation,然后按安装。安装可能需要一段时间。
安装完成后,你可能会发现主菜单栏中添加了新的项目,包括GameObject | XR下的选项。现在请不要选择任何选项——我们将在下一章,第二章**,您的第一个 AR 场景中详细介绍,我们将使用工具包创建第一个 AR 场景。
你还需要为你的项目选择一个输入处理器。我们将在下一节中探讨这个问题。
选择输入处理器
Unity 产品持续改进。一项相对较新的进步是引入了新的输入系统,它正在取代经典的输入管理器。在撰写本文时,Unity 项目可以配置为使用其中任何一个,或者在同一项目中同时使用两者。您选择的输入处理器可以对您的开发产生重大影响,因为它们的用法相当不同。经典的输入管理器主要使用轮询,而新的输入系统使用事件(见blog.unity.com/technology/introducing-the-new-input-system)。这是一个概括,因为两种软件模式都可以使用任何处理器实现,但新的输入系统设计得更好,更灵活。为了推进技术前沿,本书中的项目将使用新的输入系统。
然而,您将导入到项目中的某些示例场景,包括第二章**,您的第一个 AR 场景中的AR 基础样本,将使用经典的输入管理器,因此允许您的项目同时支持两者是明智的。
要配置项目以使用新的输入系统,请执行以下步骤:
-
要导入输入系统包,请通过访问窗口 | 包管理器打开包管理器。
-
从窗口左上角的筛选器选择中,选择Unity 注册表。
-
找到
input),然后点击安装。 -
您可能会被提示让 Unity 自动更改您的玩家设置以使用新的输入系统。您可以回答“不”。我们将手动完成此操作。
-
通过访问编辑 | 项目设置 | 玩家打开玩家设置窗口。
-
定位到配置 | 活动输入处理并选择两者(或者如果您愿意,可以选择输入系统包(新))。
我们将从第二章**,您的第一个 AR 场景开始处理输入,以及随后的章节。
您还需要设置项目的渲染管道以支持 AR。让我们学习如何做到这一点。
添加对通用渲染管道的支持
由于我们使用通用渲染管道(URP)创建了此项目,因此您还需要做一件事——向图形前向渲染器添加 AR 视频背景支持(见docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.1/manual/ar-camera-background-with-scriptable-render-pipeline.html)。此功能在虚拟图形渲染到这些像素之上之前立即在屏幕上渲染设备的视频流。请执行以下步骤:
-
在
Assets/Settings/文件夹中。 -
选择名为ForwardRenderer的资产。
-
在 检查器 窗口中,点击 添加渲染器功能 按钮,并选择 AR 背景渲染器功能。以下截图显示了生成的 Forward Renderer 设置:
![图 1.12 – 添加了 AR 背景渲染器功能的 ForwardRenderer 数据资产]
图 1.12 – 添加了 AR 背景渲染器功能的 ForwardRenderer 数据资产
此外,作为提醒,如果你将任何资产导入到你的项目中,你可能需要将它们的材质转换为渲染管线。我们将在本章末尾对示例资产进行此操作。
你现在已使用 Unity Hub 安装了 Unity,创建并打开了一个新的 Unity 项目,为你的 AR 设备安装了 XR 插件,安装了 AR Foundation 包,为你的项目选择了输入处理器,并配置了 AR 的渲染管线。下一步是继续为你的目标平台设置项目。
设置移动开发环境
根据你的项目针对的设备平台,你可能需要安装额外的软件和开发工具,以及配置你的 Unity 项目以使用特定平台的设置。
如果你正在为 Android ARCore 开发,请转到 设置 Android/ARCore 开发 部分。如果你正在为 Apple ARKit 开发,请转到 设置 iOS/ARKit 开发 部分。最后,如果你正在为可穿戴 AR 设备开发,请转到 为可穿戴 AR 眼镜开发 部分。
设置 Android/ARCore 开发环境
如果你想在 Android 设备上构建和运行你的项目,设置 Android 开发和 ARCore 的项目设置会有一些额外的步骤。我将在下面总结这个过程,但自然地,事情可能会变化,我建议你查看最新的说明文档,包括 Google 的 ARCore 文档,以及 Unity 手册。以下是一些相关的链接:
-
ARCore 支持的设备:
developers.google.com/ar/devices -
Unity 手册 – 安卓环境设置:
docs.unity3d.com/Manual/android-sdksetup.html -
ARCore Unity – 功能概述:
developers.google.com/ar/develop/unity -
Unity ARCore 扩展安装:
developers.google.com/ar/develop/unity-arf/enable-arcore -
Unity ARCore 插件:
docs.unity3d.com/Packages/com.unity.xr.arcore@4.1/manual/index.html(查找你使用的版本的相关文档页面)
你可能已经完成了设置 Android 和 ARCore 开发的第一步,但我会在这里简要重复:
-
构建支持模块:在 Unity Hub 中,请确保您已安装了与您项目使用的 Unity 特定版本对应的Android平台构建支持模块。
在 Unity Hub 的添加模块窗口中,有一个用于展开Android 构建支持的>图标。请确保您已勾选了Android SDK & NDK 工具和OpenJDK的复选框。
注意,如果您需要自定义 Android SDK、NDK 或 JDK 库的位置,请使用 Unity 编辑器中的Unity 首选项窗口,通过导航到编辑 | 首选项 | 外部工具,并指定 Unity 查找这些库分别安装的路径。
-
目标平台:在 Unity 编辑器中,通过选择文件 | 构建设置打开构建设置窗口。在平台面板中,从列表中选择Android平台。如果尚未选择,请点击更改平台按钮。如果它被禁用,请返回到步骤 1。
-
XR 插件:请确保ARCore插件已安装并选中。选择编辑 | 项目设置,然后从侧菜单中选择XR 插件管理(如果需要,初始化它)。点击Android图标以查看 Android 插件列表,如果未勾选,请勾选ARCore复选框。
-
USB 调试:下一步是在您的 Android 设备(手机或平板)上启用 USB 调试。打开设备的设置 > 关于窗口,找到构建号项。(根据品牌,您可能需要再深入一级或在不同位置找到构建号项。)接下来,我认为您必须做的下一件事非常有趣——通过点击构建号项七次来执行一个神奇的咒语!然后,神奇地,一个开发者选项菜单选项会出现。选择它并启用USB 调试。
现在,您可以将您的设备连接到您的开发机器,并且它应该被识别为附加的外围设备。
下一步要考虑的是您项目中的Android 玩家设置。这些选项的审查可以在以下位置找到:docs.unity3d.com/Manual/class-PlayerSettingsAndroid.html。针对 ARCore 的 AR 项目需要特定的设置。请仔细检查当前的要求,这些要求可以在Quickstart ARCore页面上的配置项目设置主题中找到(developers.google.com/ar/develop/unity-arf/quickstart-android)。从之前的步骤继续,我建议执行以下操作:
-
玩家设置:在 Unity 中,导航到 编辑 | 项目设置 | 玩家 以打开 玩家设置 窗口。它包含许多选项,包括顶部的选项卡,用于在平台特定设置之间切换。通常,除非另有建议或您正在优化项目构建,否则您可以保留默认设置。初始化以下设置:
-
其他设置 | 渲染:取消选中 自动图形 API。如果图形 API 下列出了 Vulkan,请将其删除,因为 Vulkan 目前不支持 ARCore。为此,请选择 Vulkan 并按右下角的 -(减号)图标。此外,取消选中 多线程渲染,因为它(目前)与 ARCore 不兼容。
-
com.DefaultCompany.MyARProject. -
其他设置 | 最小 API 级别:如果您正在构建 AR 需要 应用,请指定 Android 7.0 'Nougat' (API 级别 24) 或更高版本。如果您正在构建 AR 可选 应用,请指定 Android API 级别 14 或更高版本。
信息 – Unity 中的“player”一词
在 Unity 中,“player”一词具有多重含义。您的应用程序或游戏用户可能被称为 player。在游戏中,第一个由用户控制的 GameObject(包含一个摄像头)也可能被称为 player。在非 AR 视频游戏中,游戏控制器可能被称为 player controller。然而,在 项目设置 中,player 指的是构建过程的结果;它是一个安装在您的目标设备上的可执行程序(以及其他资产文件和数据),用于“播放”您的应用程序。在这种情况下,这个词类似于 媒体播放器,例如播放音乐或视频文件。Unity 中的 玩家设置 配置了 Unity 如何构建和部署到您的目标设备。
同时,您还有安装由 Unity 的 ARCore 扩展 包提供的额外功能的选项。此包扩展了 AR Foundation 以支持 ARCore 的更多高级功能,这些功能目前在 AR Foundation 中尚不支持。要安装 ARCore 扩展,请执行以下步骤:
-
从 GitHub 发布页面下载最新的
arcore-unity-extensions-*.tgztarball,网址为github.com/google-ar/arcore-unity-extensions/releases/。 -
使用 窗口 | 包管理器 打开包管理器。
-
在窗口的左上角,点击 + 图标并选择 从 tarball 添加包,如图所示:![Figure 1.13 – Adding a tarball package
![img/Figure_1.13-add-package-from-tarball.jpg]
图 1.13 – 添加 tarball 包
-
定位下载的
arcore-unity-extensions-*.tgztarball。 -
然后,点击 打开。安装包及其依赖项可能需要几分钟。
您的项目现在已设置好,以针对 Android ARCore 和 AR Foundation。我们将在下一章,第二章**,您的第一个 AR 场景中验证您的设置,当我们创建 AR 场景、构建并在您的设备上运行它时。
为 iOS/ARKit 开发设置
如果您想在苹果 iOS 设备上构建和运行您的项目,设置 iOS 开发和 ARKit 的项目时需要额外几个步骤。我将在下面总结这个过程,但请注意,事情可能会发生变化,我建议您查看最新的必要文档。
为 iOS 开发需要一个运行 OSX 的 Mac 计算机。然后,您需要安装 Xcode 开发环境。强烈建议您加入苹果开发者计划,目前个人费用为每年 99 美元(USD)。您可以在不成为苹果开发者的情况下进行一些有限的 Unity iOS 开发,但这并不实用,尤其是在 AR 方面,您需要在一个物理设备上测试您的应用程序。
这里有一些相关的链接列表:
-
苹果开发者计划:
developer.apple.com/programs/ -
Unity 手册 – iOS 开发入门:
docs.unity3d.com/Manual/iphone-GettingStarted.html -
Unity 手册 – 为 iOS 构建:
docs.unity3d.com/Manual/UnityCloudBuildiOS.html -
Unity ARKit 插件:
docs.unity3d.com/Packages/com.unity.xr.arkit@4.1/manual/index.html(查找您所使用版本的文档页面)信息 – 如何在没有 Mac 的情况下为 iOS 开发
虽然 iOS 开发需要一个运行 OSX 的 Mac 计算机,但您可以使用
.ipa文件在您的 iOS 设备上绕过这一点。这并不适合快速的开发周期!如果您处于这种情况,我的建议是购买一部支持 ARCore 的二手安卓手机。然后,在您的 Windows PC 上使用 AR Foundation 开发您的应用程序,首先针对安卓进行开发,然后定期运行 iOS/ARKit 构建,以测试和验证它在该设备上运行。Unity Cloud Builds 需要 Unity Plus 或 Pro 许可证或 Unity Teams Advanced 订阅。
为 iOS 和 ARKit 开发需要执行以下步骤。您可能已经完成了一些这些步骤:
-
苹果开发者计划: 这是您为 iOS 开发的入场券。前往
developer.apple.com/programs/了解更多信息并注册。 -
Xcode: 下载并安装当前版本的 Xcode,这是开发任何苹果产品所需的开发环境。您可以在 Mac App Store 中找到它:
apps.apple.com/us/app/xcode/id497799835。 -
构建支持模块:在Unity Hub中,确保您已为与项目一起使用的 Unity 特定版本安装了iOS平台构建支持模块。
-
目标平台:在 Unity 编辑器中,通过选择文件 | 构建设置打开构建设置窗口。在平台面板中,从列表中选择iOS平台。如果尚未选择,请点击更改平台按钮。如果按钮不可用,请返回到步骤 1。
-
XR 插件:确保已安装并选中了ARKit插件。选择编辑 | 项目设置,然后从侧菜单中选择XR 插件管理(如有必要,请初始化它)。点击iOS选项卡以查看 iOS 插件列表,如果未选中,请勾选ARKit复选框。
-
玩家设置:在
Required for augmented reality support中设置11,以及架构 | ARM64。
当 Unity 构建 iOS 项目时,它实际上并没有构建应用程序。相反,它构建一个 XCode 项目文件夹,然后在该文件夹中打开 XCode,XCode 随后用于构建应用程序。XCode 提供的一项关键服务是确保您通过配置您的应用程序进行开发授权,包括以下内容:
-
为您计划测试应用程序的每个设备安装开发配置文件。按照
docs.unity3d.com/Manual/UnityCloudBuildiOS.html中创建证书主题下的说明进行操作。 -
通过前往首选项 | 账户将您的Apple ID账户添加到 Xcode 中。
有关使用 Xcode 和 Unity 的更多信息,请参阅Unity 手册:Unity Xcode 项目的结构(docs.unity3d.com/Manual/StructureOfXcodeProject.html)和其他相关页面。
这个过程可能会让人困惑。每个为 iOS 开发的人都会经历一个类似的过程,所以您绝对不是唯一一个,互联网上有很多答案可以找到。记住:“DuckDuckGo 是你的朋友。”幸运的是,您通常只需要做一次。
注意,您也可以通过导航到编辑 | 项目设置 | 玩家 | 识别来在您的 Unity 玩家设置中设置您的签名团队 ID。
信息 - 苹果自己的 AR 开发工具
在查看苹果网页后,您会发现它们除了 Unity 之外还提供了自己的 AR 开发工具(developer.apple.com/augmented-reality/tools/)。当然,我是一位 Unity 和 AR Foundation 的大粉丝,它们为您提供了设备独立性和 Unity 的所有其他强大功能,但了解替代方案也是好的。
您的项目现在已设置为目标为 Apple ARKit 的 AR Foundation。我们将在下一章,第二章**,您的第一个 AR 场景中验证您的设置,当我们创建一个 AR 场景,构建它并在您的设备上运行时。
开发可穿戴 AR 眼镜
AR Foundation 不仅支持使用 ARCore 和 ARKit 的手持移动 AR 设备,还支持可穿戴 AR 眼镜,包括微软 HoloLens 和 Magic Leap。同样,针对可穿戴 AR 设备可能需要配置 Unity 以针对除 Android 或 iOS 以外的平台。由于它们针对的是企业或工业应用,可穿戴 AR 眼镜相对较贵,并且超出了典型消费者的购买能力。虽然这本书可以作为开发这些设备的美好起点,并且项目可以根据需要进行调整,但本书的范围不包括在后续章节中支持可穿戴 AR 设备。
对于微软 HoloLens,您必须设置 Unity 以针对通用 Windows 平台(UWP),从通过 Unity Hub 安装所需的模块开始,如下面的截图所示:

图 1.14 – 为 HoloLens 添加 UWP 构建支持
要为 HoloLens 开发设置,您需要使用Visual Studio IDE和兼容的Windows 10 SDK版本。有关更多信息,以下是一些有用的链接:
-
Unity for Windows Mixed Reality:
unity3d.com/partners/microsoft/mixed-reality。 -
微软混合现实 – 安装工具:
docs.microsoft.com/en-us/windows/mixed-reality/develop/install-the-tools?tabs=unity(这还包括一个安装清单)。 -
Unity Windows XR 插件:
docs.unity3d.com/Packages/com.unity.xr.windowsmr@5.2/manual/index.html。您只需找到您所使用版本的文档页面。此页面还包括推荐的构建设置和玩家设置。信息 – 微软混合现实工具包(MRTK)
注意,微软还为其 Unity 平台提供自己的开源跨平台开发工具包,称为混合现实工具包(MRTK),作为 AR Foundation 的替代品。我认为这个框架有一个非常有趣的实现,具有灵活的架构,支持从 AR 到 VR 的一系列设备。了解更多信息请点击此处:
docs.microsoft.com/en-us/windows/mixed-reality/develop/unity/mrtk-getting-started。
对于 Magic Leap 可穿戴 AR 产品,您必须设置 Unity 以针对 Lumen OS,从通过 Unity Hub 安装所需的模块开始,如下截图所示:

图 1.15 – 为 Magic Leap 添加 Lumen OS 构建支持
对于更多信息,以下是一些有用的链接:
-
Unity for Magic Leap:
unity3d.com/partners/magicleap -
Magic Leap 开发者门户:
developer.magicleap.com/en-us/home -
Magic Leap Unity 开发:
developer.magicleap.com/en-us/learn/guides/unity-overview -
使用 AR Foundation 与 Magic Leap:
resources.unity.com/unitenow/onlinesessions/using-magic-leap-with-ar-foundation-in-unity-2020-1(Unite Now 演讲)
有趣的是,Magic Leap 提供了一个 Unity 模板,您可以将它添加到 Unity Hub 中,作为新项目的起点(github.com/magicleap/UnityTemplate)。
现在您已经在您的目标平台和设备上设置了 AR 开发项目,让我们构建一个测试来确保一切按计划进行。
构建 和 运行 测试 场景
在继续构建 AR 项目之前,明智的做法是验证您的项目是否已经正确设置,通过尝试在您的目标设备上构建和运行它。为此,我们将创建一个最小的 AR 场景,并验证它是否满足以下清单:
-
您可以为您的目标平台构建项目。
-
应用程序在您的目标设备上启动。
-
当应用程序启动时,您会在屏幕上看到来自其摄像头的视频流。
-
应用程序扫描房间并在您的屏幕上渲染深度点。
我将一步步地引导您。如果您不理解所有内容,请不要担心;我们将在 第二章**,您的第一个 AR 场景 中更详细地一起探讨。请在您的当前项目中执行以下操作,该项目应在 Unity 中打开:
-
通过选择
Scenes文件夹,命名为BasicTest并点击 Save,创建一个名为 BasicTest 的新场景。 -
在 Hierarchy 窗口中,删除默认的 Main Camera(右键单击并选择 Delete,或使用 Del 键盘键)。
-
通过选择 GameObject | XR | AR Session 添加一个 AR 会话对象。
-
通过选择 GameObject | XR | AR Session Origin 添加一个 AR 会话原点对象。
-
通过在搜索字段中点击
ar point并选择 AR Point Cloud Manager,将点云管理器添加到会话原点对象。
你会注意到点云管理器有一个用于可视化检测到的深度点的空槽位,即点云预制件。预制件是一个保存为项目资产的游戏对象,可以在运行时添加到场景(实例化)。我们将使用一个非常简单的粒子系统创建一个预制件。再次提醒,如果你对此不熟悉,不要担心,只需跟着做即可:
-
通过选择GameObject | Effects | Particle System创建一个粒子系统。
-
在
点粒子中。 -
在粒子系统组件上,取消勾选循环复选框。
-
设置其
0.1。 -
取消勾选唤醒时播放复选框。
-
在搜索字段中点击
ar point,然后选择AR 点云。 -
同样,点击添加组件并选择AR 点云可视化器。
-
将PointParticle对象从层次结构窗口拖到项目窗口中的预制件文件夹(如果需要,先创建文件夹)。这使得 GameObject 成为一个预制件。
-
使用右键单击 | 删除或按Del键从层次结构窗口中删除PointParticle对象。
PointParticle对象的检查器窗口现在应该如下所示:

图 1.16 – 使用我们使用的设置突出显示的 PointParticle 预制件的检查器视图
我们现在可以将 PointParticle 预制件应用到 AR 点云管理器中,如下所示:
-
在层次结构窗口中,选择AR 会话起源对象。
-
从项目窗口,将PointParticle预制件拖到AR 点云管理器 | 点云预制件槽中。(或者,点击槽右侧的“甜甜圈”图标以打开选择 GameObject窗口,选择资产选项卡,并选择PointParticle)。
-
使用文件 | 保存保存场景。
结果的 AR 会话起源应该如下所示:

图 1.17 – 填充了 PointParticle 预制件的点云管理器组件的会话起源
现在,我们已经准备好构建和运行场景。执行以下步骤:
-
使用文件 | 构建设置打开构建设置窗口。
-
点击添加打开场景按钮将此场景添加到构建列表中。
-
在构建中的场景列表中,取消勾选除BasicTest以外的所有场景。
-
确保您的设备通过 USB 线连接到您的计算机。
-
按
Builds/。如果需要,给它一个文件名,然后按保存。完成此任务可能需要一些时间。
如果一切顺利,项目将构建,安装到你的设备上,并启动。你应该能在你的设备屏幕上看到相机视频流。慢慢地将手机向不同方向移动。当它扫描环境时,特征点将被检测并在屏幕上渲染。下面的屏幕截图显示了我办公室的门,我的手机上渲染了点云。当你扫描时,离相机更近的环境中的粒子看起来比远离相机的大,这有助于用户感知场景中的深度。

图 1.18 – 使用 BasicTest 场景在我的手机上渲染的点云
如果你在构建项目时遇到错误,请查看 Unity 编辑器中的控制台窗口中的消息(在默认布局中,它位于项目窗口后面的一个标签页)。仔细阅读这些消息,通常从顶部开始。如果这没有帮助,那么请回顾本章中详细说明的每个步骤。如果修复方法仍然不明显,请在互联网上搜索消息的文本,因为你可以确信你很可能不是第一个有类似问题的人!
小贴士 – 提前构建,经常构建
在项目中尽快使构建工作正常进行是很重要的。如果不是现在,那么至少在下一章结束之前要做到这一点,因为没有在物理设备上构建、运行和测试 AR 应用程序的信心去开发 AR 应用程序是没有太多意义的。
在成功构建之后,你现在可以开始构建自己的 AR 项目了。恭喜你!
摘要
在本章中,在对增强现实进行简要介绍后,你立即开始了自己 AR 项目的开发之路。你通过 Unity Hub 安装了 Unity,并学习了跟踪不同版本的 Unity、其项目和包的重要性。你对使用 Unity 编辑器进行了简要的浏览,包括一些对 3D 和 AR 基本概念至关重要的关键概念。
然后,你为 AR 开发设置了你的项目和系统软件,包括安装 XR 插件、AR Foundation 包、Android 或 Xcode 的工具,以及其他设置所需的项目。最后,我们创建了一个最小的 AR 场景(包括使用粒子系统组件的快速点云预制体)并构建了场景,以验证它是否可以在你的目标设备上构建和运行。
设置你的机器可能既复杂又痛苦,但这是你进入 Unity 开发的入场券,每个人都需要这样做。如果你在这一章中一切顺利,那么你就是英雄!
在下一章中,我们将开始通过创建一个新的 AR 场景,一步一步地深入了解使用 Unity 和 AR Foundation 进行 AR 开发,同时解释每个组件。
第二章:第二章:您的第一个 AR 场景
使用 Unity AR 基础创建一个简单的增强现实(AR)场景相当简单。涉及的步骤可能只需要一页或两页。然而,在本章中,我们将一起创建一个场景,每个步骤都将结合上下文进行解释,以便您能够全面了解使用 AR 基础构建的 AR 场景。
但在我们这样做之前,我们将查看 Unity 提供的某些 AR 示例,包括 AR 基础示例项目,并为您的设备构建它们的示例场景。由于该项目包含一些有用的资产,我们将将其导出为资产包,以便在您自己的项目中重复使用。
在本章中,我们将涵盖以下主题:
-
构建和运行 AR 基础示例项目
-
导出和导入示例资产
-
构建新的 Unity AR 场景
-
C#编程和 MonoBehaviour 类的介绍
-
使用 AR 射线投射将对象放置在平面上
-
实例化 GameObject
-
创建和编辑预制件
技术要求
要实现本章提供的项目,您需要在您的开发计算机上安装 Unity,并将其连接到支持增强现实应用程序的移动设备(有关说明,请参阅第一章**,为 AR 开发设置)。完成的项目可在此书的 GitHub 存储库中找到:github.com/PacktPublishing/Augmented-Reality-with-Unity-AR-Foundation。
探索 Unity 的 AR 基础示例项目
了解如何使用 Unity AR 基础创建 AR 项目的一个好方法是探索 Unity 的各种示例项目。这些项目包括示例场景、脚本、预制件和其他资产。通过克隆一个项目并打开一个示例场景,您可以学习如何使用 AR 基础,尝试功能,并了解一些最佳实践。特别是,请考虑以下项目:
-
XR 交互工具包示例:
github.com/Unity-Technologies/XR-Interaction-Toolkit-Examples/tree/master/AR。 -
对于更高级的工作,我也喜欢几位个人贡献者,包括 Unity 的高级 XR 开发者丹·米勒。更多信息请见
github.com/DanMillerDev。
请查阅每个项目的README文件(可在 GitHub 项目主页找到),以了解项目功能、依赖项以及其他有关项目的有用信息。
这些存储库中的每个都包含一个完整的 Unity 项目。也就是说,它们不仅仅是您可以导入到现有项目中的 Unity 资产包。相反,您将克隆整个存储库,并将其作为自己的项目打开。这对于可能具有其他包依赖项并需要预设设置才能正确构建和运行的项目来说是典型的。
AR 基础示例 项目是我学习各种 AR 基础功能的首选项目。它包含许多示例场景,演示了单个功能,通常替代其他地方的详细文档(见github.com/Unity-Technologies/arfoundation-samples/tree/main/Assets/Scenes)。
每个场景都非常简单(几乎到了错误的地步),因为它的原子目的是说明单个功能。例如,有单独的场景用于平面检测、平面遮挡和羽毛状平面。值得注意的是,该项目还包含一个主菜单场景(Assets/Scenes/ARFoundationMenu/Menu),当您将所有场景构建成一个单独的可执行文件时,它会启动其他场景。我建议从名为SimpleAR的场景开始,我们稍后会对其进行回顾。
另一个是 AR 基础演示 项目,它包含一些更复杂的用户场景和未在示例项目中涵盖的功能。例如,它演示了 Unity 的Onboarding UX资产,我们将在第四章**,创建 AR 用户框架中向您介绍。它还涵盖了图像跟踪、网格放置、语言本地化和一些有用的着色器(例如,线框、阴影和雾气)。
XR 交互工具包示例 仓库包含两个独立的 Unity 项目:一个用于 VR,另一个用于 AR。在我看来,它主要是为了将来要添加的内容而保留的占位符。
信息 – XR 交互工具包
Unity 的 XR 交互工具包在本书中没有涉及。它提供组件和其他资产,用于使用手控制器和设备支持的手势开发交互式场景。在撰写本文时,XR 交互工具包专注于虚拟现实(VR)应用(其示例项目包含七个 VR 场景和仅支持移动 AR 的一个 AR 场景),但我相信它是 Unity 未来 XR 策略和架构的关键部分。如果您对 VR 的 XR 交互工具包感兴趣,请查看我另一本书,Packt 出版社出版的Unity 2020 虚拟现实项目 – 第三版。
让我们获取 AR 基础示例项目的副本,并查看SimpleAR场景。
构建 和 运行 示例项目
在本节中,您将构建 AR 基础示例 项目并在您的设备上运行它。首先,请从其 GitHub 仓库克隆项目,并在 Unity 中打开它,如下所示:
-
从 GitHub 克隆项目的副本到您的本地机器。项目可以在
github.com/Unity-Technologies/arfoundation-samples找到。请使用您喜欢的任何克隆方法;例如,GitHub Desktop (desktop.github.com/)或命令行(git-scm.com/download/)。 -
将项目添加到Unity Hub中,通过选择项目 | 添加,导航到克隆项目的根文件夹,然后点击选择文件夹。
-
在 Unity 中打开项目。在Unity Hub的项目列表中,如果您看到一个黄色的警告图标,那么克隆项目的 Unity 版本目前尚未安装到您的系统上。使用Unity 版本选择来选择您拥有的较新版本的编辑器,最好是同一主要版本的(例如,20XX)。
-
从 Unity Hub 的项目列表中选择项目来打开它。
-
如果您的 Unity 版本比项目最后保存时的版本新,您将看到一个提示,询问“您想要将项目升级到 Unity 的新版本吗?”。点击确认。
其中一个场景,SimpleAR,是一个基本的 AR 示例场景。当运行时,用户将使用设备的摄像头扫描他们的房间,并且应用将检测屏幕上渲染的任何水平平面。当您的用户点击这些平面中的一个时,一个小的红色立方体将被放置在环境中。您可以在房间里走动,立方体将保持在放置的位置。如果您再次点击另一个位置,立方体将被移动到那里。让我们简要回顾一下这个SimpleAR场景的 GameObject:
-
打开
Scenes/SimpleAR/文件夹,双击SimpleAR场景文件。 -
在层次结构窗口中,您将找到两个特别感兴趣的 GameObject:AR 会话和AR 会话原点。
-
选择AR 会话原点对象,并在检查器窗口中检查其组件。这些包括AR 平面管理器、AR 点云管理器、AR 射线投射管理器和一个放置在平面上脚本。我们将在本章后面解释所有这些内容。
现在,让我们尝试构建和运行项目:
-
如果需要,切换到您的目标平台。为此,转到文件 | 构建设置,从平台列表中选择您的设备平台(例如,Android 或 iOS),然后点击切换平台。
-
很可能,克隆项目的设置已经配置好了,但让我们确保一下。从构建设置窗口,点击玩家设置按钮打开该窗口,并确认在第一章**,设置 AR 开发中提到的必要设置。例如,Android ARCore 不支持 Vulkan 图形,需要牛轧糖(API 级别 24)作为最低要求。
-
再次在构建设置窗口中注意,构建中的场景列表以菜单场景开头,并包含此项目中的所有演示场景(列表中的第一个将是应用加载时首先加载的场景)。您可以保留这些设置,或者只需选择构建中想要的一个即可。
-
确保您的移动设备已连接到计算机上的 USB 端口。
-
按
Builds/。如果需要,输入一个文件名,然后按保存。完成此任务可能需要一段时间。
如果一切顺利,项目将构建,安装到您的设备上,并启动。
如果在构建项目时遇到错误,请查看 Unity 编辑器中的控制台窗口中的消息(在默认布局中,它位于项目窗口后面的一个标签页)。仔细阅读这些消息,通常从顶部开始。如果修复方法不明显,请在网络上搜索消息文本,因为您可以确信您可能不是第一个遇到类似问题的人!
小贴士 – “无法生成 ARCore 参考图像库”错误
如果在尝试构建项目时收到类似“无法生成 ARCore 参考图像库”的错误,请确保项目文件夹的路径名中没有空格!有关更多信息,请参阅github.com/Unity-Technologies/arfoundation-samples/issues/119。
主菜单将显示,如下面的屏幕截图所示(左侧面板):

图 2.1 – 我手机运行 arfoundation-samples 应用和 SimpleAR 场景的截图
AR Foundation(以及这个项目)的一个酷特点是它可以在运行时检测运行其上的设备的性能。这意味着当 AR Foundation 检测到该场景中演示的功能不支持该设备时,主菜单中的按钮将被禁用。(我在前面的屏幕截图中使用的设备是安卓手机,因此一些仅限 iOS 的功能场景被禁用)。
点击简单 AR按钮打开该场景。您应该在设备屏幕上看到相机视频流。缓慢地将手机向不同方向移动,靠近或远离。当它扫描环境时,特征点和平面将被检测并在屏幕上渲染。轻触其中一个平面,在场景中放置一个立方体,如前一个屏幕截图的右侧面板所示。
样本项目中的某些资源和脚本对于构建我们自己的项目可能很有用。我现在将向您展示如何导出它们。
导出样本资源以供重用
Unity 提供了使用.unitypackage文件在项目之间共享资产的能力。让我们从 AR Foundation Samples 项目中导出资产以供重用。我喜欢的一个技巧是首先将所有示例文件夹移动到一个根文件夹中。在 Unity 中打开arfoundation-samples项目,请执行以下步骤:
-
通过点击窗口左上角的+图标并选择文件夹,在
Assets中命名ARF-samples。 -
将以下文件夹拖入
ARF-samples文件夹中:Materials、Meshes、Prefabs、Scenes、Scripts、Shaders和Textures。也就是说,将它们全部移动,但保留根目录下的XR文件夹。 -
右键单击
ARF-samples文件夹并选择导出包。 -
导出包窗口将打开。点击导出。
-
选择项目根目录外的目录,命名文件(例如,
arf-samples),然后点击保存。
项目窗口中的Assets/ARF-samples/文件夹在以下屏幕截图中显示:


图 2.2 – 正在导出到.unitypackage 文件的示例资产文件夹
如果你愿意,现在可以关闭arfoundation-samples项目了。你现在有一个资产包可以在其他项目中使用。
小贴士 – 通过复制示例项目开始新项目
从头开始创建新的 Unity AR 项目的一个替代方法是复制arfoundation-samples项目作为新 AR 项目的起点。为此,从你的 Windows 资源管理器(或 macOS Finder),复制整个项目文件夹,然后将其添加到 Unity Hub 中。这样,你就可以在一个地方获得所有示例资产和演示场景,并且它已经设置了合理的默认项目设置。我经常这样做,特别是对于快速演示和小型项目。
接下来,我们将导入示例资产到你的 Unity 项目中,并构建给定的 SimpleAR 场景。
在你的项目中构建 SimpleAR 场景
正如你将在本章后面看到的那样,示例项目包括一些我们可以用于你自己的项目的资产,节省了你的时间和精力,尤其是在开始时。我们将导入我们刚刚导出的unitypackage,然后构建给定的 SimpleAR 场景,作为另一个测试来验证你是否已经设置好构建和运行 AR 应用程序。
创建一个新项目
如果你已经设置了一个用于 AR 开发的 Unity 项目,如第一章**,为 AR 开发设置中详细说明,你可以在 Unity 中打开它并跳过本节。如果没有,请执行以下步骤,这些步骤已经简化以方便你。如果你需要更多详细信息或解释,请重新查阅第一章**,为 AR 开发设置。
要创建和设置一个新的 Unity 项目,其中包含 AR Foundation、Universal Render Pipeline 和新的输入系统,以下是简化的步骤:
-
通过打开
MyARProject并点击创建来创建一个新的项目。 -
通过在 Unity 编辑器中选择 Unity Hub 的项目列表中的项目来打开你的项目。
-
通过转到文件 | 构建设置,从平台列表中选择Android或iOS,并点击切换平台来设置你的目标平台。
-
根据*第一章,通过转到编辑 | 项目设置 | 玩家窗口来设置玩家设置,设置 AR 开发或你的设备的文档。例如,Android ARCore 不支持 Vulkan 图形,需要牛轧糖(API 级别 24)**作为最低要求。
-
通过转到编辑 | 项目设置 | XR 插件管理器 | 安装 XR 插件管理来安装 XR 插件。然后,勾选你设备的插件提供商复选框。
-
通过在搜索输入字段中转到
ar,选择AR Foundation包,并点击安装来安装 AR Foundation。 -
通过在搜索输入字段中转到
input,选择输入系统包,并点击安装来安装输入系统包。当提示启用输入后端时,你可以回答是,但我们将实际上在将样本资产导入项目时将此设置更改为两者。
-
通过定位到
Assets/Settings/文件夹,将AR 背景渲染器添加到 URP 前向渲染器中。在其检查器窗口中,点击添加渲染器功能并选择AR 背景渲染器功能。
你可能想要将这些步骤添加到书签中以便将来参考。接下来,我们将导入从 AR Foundation 示例项目中导出的样本资产。
将样本资产导入到你的项目中
现在你已经设置了一个用于 AR 开发的 Unity 项目,你可以将样本资产导入到你的项目中。在你的 Unity 项目中打开项目,执行以下步骤:
-
通过在主菜单中选择资产 | 导入包 | 自定义包来导入包。
-
在你的系统上定位
arf-samples.unitypackage文件并点击打开。 -
将会打开导入 Unity 包窗口。点击导入。
-
如果你使用的是通用渲染管线(或 HDRP),而不是像我们这样使用内置渲染管线,你需要转换导入的材料。选择编辑 | 渲染管线 | URP | 将项目材料升级到 URP 材料。然后,当提示时,点击继续。
-
然后,使用编辑 | 项目设置 | 玩家转到玩家设置,选择配置 | 活动输入处理,并选择两者。然后,当提示时,点击应用。
-
在本书的项目中,我们将使用新的输入系统。然而,一些示例项目中的演示场景仍在使用旧的输入管理器。如果你选择输入系统包(新)作为活动输入处理,那么这些演示场景可能无法运行。
希望所有资产都能无问题导入。然而,在编译示例脚本时可能会出现一些错误。这可能发生如果示例项目使用比您的项目更新的 AR Foundation 版本,并且它引用了您的项目未安装的功能的 API 函数。最简单的解决方案是将 AR Foundation 的版本升级到与示例项目相同或更新的版本。为此,请执行以下步骤:
-
要查看错误消息,请使用其标签或选择窗口 | 通用 | 控制台来打开控制台窗口。
-
假设在我的项目中,我安装了AR Foundation 4.0.12,但示例项目使用的是版本 4.1.3的功能,这些功能在我的版本中不可用。在这里,我将前往窗口 | 软件包管理器,选择AR Foundation软件包,点击查看其他版本,选择 4.1.3 版本,然后点击更新到 4.1.3按钮。
-
项目也可能正在使用软件包的预览版本。通过选择编辑 | 项目设置 | 软件包管理器 | 启用预览软件包来启用预览软件包。
-
确保 ARCore XR 插件和/或 AR Kit XR 插件版本与项目使用的 AR Foundation 软件包版本相匹配。
-
您可能还会看到一条消息,说明一些示例脚本需要您在项目中启用“不安全”的代码。前往项目设置 | 玩家 | 脚本编译 | 允许'不安全'代码并勾选复选框。
这并不像听起来那么可怕。“不安全”的代码通常意味着您安装的某个组件正在从项目调用 C++代码,从编译器的角度来看,该项目可能存在潜在的不安全性。在 Unity 中启用不安全代码通常不会成问题,除非,例如,您正在将 WebGL 发布到 WebPlayer,而我们不是这样做的。
最后,您可以通过构建和运行 SimpleAR 场景来验证您的设置,这次是从您自己的项目中。请执行以下步骤:
-
打开
ARF-samples/Scenes/SimpleAR/文件夹,双击SimpleAR场景文件。 -
通过前往文件 | 构建设置来打开构建设置窗口。
-
对于构建中的场景列表,点击添加打开场景按钮,并取消勾选列表中除 SimpleAR 之外的所有场景。
-
确保您的设备通过 USB 连接。
-
按
Builds/。如果需要,给出一个文件名,然后按保存。完成此任务可能需要一些时间。
应用程序应在您的设备上成功构建和运行。如果您遇到任何错误,请查阅本章以及第一章**,设置 AR 开发环境中详细说明的每个步骤。
当应用程序启动时,如前所述,您应该在屏幕上看到一个相机视频流。慢慢将您的手机向不同方向移动,靠近/远离。当它扫描环境时,特征点和平面将被检测并在屏幕上渲染。轻触这些平面之一,在场景中放置一个立方体。
你的项目现在已准备好进行 AR 开发了!
开始一个新的基本 AR 场景
在本节中,我们将创建一个与 SimpleAR(实际上,更像是名为 InputSystem_PlaceOnPlane 的样本场景)非常相似的场景,但我们将从一个新的空场景开始。我们将向场景层次结构中添加 AR 会话和 AR 会话原点对象,由 AR Foundation 提供,然后添加平面和点云的可追踪功能管理器。在本章的后续部分,我们将设置输入系统动作控制器,编写一个 C# 脚本来处理任何用户交互,并创建一个 3D 预制件图形以放置在场景中。
因此,按照以下步骤开始新场景:
-
通过访问 文件 | 新场景 创建一个新的场景。
-
如果提示,请选择 基本(内置) 模板。然后,点击 创建。
Unity 允许你在创建新场景时使用场景模板。名为 基本(内置) 的模板与 Unity 早期版本中的默认新场景相当。
-
通过使用 右键单击 | 删除(或键盘上的 Del 键)从 层次结构 窗口中删除 主摄像头。
-
通过从主菜单中选择 GameObject,然后选择 XR | AR Session 来添加 AR 会话。
-
通过从主菜单中选择 GameObject,然后选择 XR | AR Session Origin 来添加 AR 会话原点。
-
展开AR 会话原点并选择其子项;即 AR 摄像头。在 检查器 窗口中,使用左上角的 标签 选择器将其设置为我们的 主摄像头。(这不是必需的,但将场景中有一个标记为 主摄像头 的摄像头作为良好实践是不错的。)
-
使用
Assets/Scenes/文件夹保存场景,命名为BasicARScene,然后点击 保存。
你的场景层次结构现在应该如下所示:

图 2.3 – 开始场景层次结构
现在,我们可以更仔细地查看我们刚刚添加的对象,从 AR 会话对象开始。
使用 AR 会话
AR 会话 对象负责在目标平台上启用和禁用增强现实功能。当你在场景 层次结构 中选择 AR 会话 对象时,你可以在 检查器 窗口中看到其组件,如下面的截图所示:

图 2.4 – AR 会话对象的检查器窗口
每个 AR 场景必须包含一个(且仅有一个)AR 会话。它提供了几个选项。通常,你可以将这些选项保留为它们的默认值。
尝试更新选项指示 AR 会话尝试在设备上安装底层 AR 支持软件(如果尚未安装)。并非所有设备都需要这样做。例如,iOS 设备如果支持 AR,则不需要任何额外的更新。另一方面,要在 Android 上运行 AR 应用程序,设备必须安装 ARCore 服务。大多数 AR 应用程序会在缺少时为你这样做,这就是AR 会话的尝试更新功能所做的事情。如果需要,当你的应用程序启动且支持软件缺失或需要更新时,AR 会话将尝试安装Google Play Services for AR(见play.google.com/store/apps/details?id=com.google.ar.core)。如果所需的软件未安装,则设备上将无法使用 AR。你可以选择禁用自动更新并自行实现它们,以自定义用户入门体验。
注意
检查器窗口中的匹配帧率选项已过时。通常,你希望你的应用程序的帧更新与物理设备的帧率相匹配,通常不需要调整。如果你需要调整它,你应该通过脚本控制(见docs.unity3d.com/ScriptReference/Application-targetFrameRate.html)。
关于跟踪模式,你通常会将其设置为位置和旋转,因为这指定了你的 VR 设备正在使用其 XYZ 位置和围绕每个轴的旋转在物理世界的 3D 空间中进行跟踪。这被称为6 自由度跟踪,这可能是你期望的行为。但对于面部跟踪,例如,我们应该将其设置为仅旋转,正如你在第九章**,自拍:制作有趣的面孔中看到的。
AR 会话 GameObject 还有一个AR 输入管理器组件,用于管理我们的XR 输入子系统以跟踪设备在物理 3D 空间中的姿态。它从 AR 相机的AR 姿态驱动器(稍后讨论)读取输入。该组件没有选项,但这是设备跟踪所必需的。
我们还在层次结构中添加了一个 AR 会话原点 GameObject。让我们接下来看看它。
使用 AR 会话原点
AR 会话原点将是所有可跟踪对象的根对象。拥有根原点可以保持相机和任何可跟踪对象在同一空间中,并且它们相对于彼此的位置。这个会话(或设备空间)包括 AR 相机和任何在真实世界环境中由 AR 软件检测到的可跟踪特征。否则,检测到的特征,如平面,将不会相对于相机出现在正确的位置。
小贴士 - 在 AR 中缩放虚拟场景
如果你计划扩展你的 AR 场景,将你的游戏对象放置为 AR Session Origin 的子对象,然后缩放父 AR Session Origin 变换,而不是子对象本身。例如,考虑一个世界级城市地图或游戏法庭缩放到桌面大小。不要缩放场景中的单个对象;相反,通过调整根会话起源对象的大小来缩放一切。这将确保其他 Unity 系统,特别是物理和粒子系统,相对于摄像机空间保持其比例。否则,像重力这样的东西,以每秒米计算,以及粒子渲染可能会出错。
当你在场景 层次结构 中选择 AR Session Origin 对象时,你可以在 检查器 窗口中看到其组件,如下截图所示:

图 2.5 – AR Session 对象的检查器窗口
在撰写本文时,默认的 AR Session Origin 对象仅有一个 AR Session Origin 组件。我们希望通过添加更多组件来扩展其行为。
Session Origin 的 Camera 属性引用其自身的子 AR Camera GameObject,我们将在下一节中探讨。
使用 AR Camera
AR Camera 对象是 AR Session Origin 的子对象。其 检查器 窗口如下截图所示:

图 2.6 – AR Camera 对象的检查器窗口
在设置过程中,我们将 AR Camera 标记为 Camera.main,这是一个通过标签名称查找的快捷方式。
如其名称所示,AR Camera 对象包含一个 0.1, 20) 米的视野。在 AR 应用中,将设备放置在虚拟对象几英寸之内并不罕见,因此我们不希望它被裁剪。相反,在 AR 应用中,如果你离你放置在场景中的对象超过 20 米,你可能根本不需要渲染它。
重要的是,与在非 AR 场景中预期使用 Skybox 不同,摄像机的 背景 被设置为 纯黑 颜色。这意味着背景将通过摄像机的视频流进行渲染。这可以通过 AR Camera 的 AR Camera Background 组件进行控制。在高级应用中,你甚至可以自定义视频流的渲染方式,使用自定义的视频 材质(这个主题超出了本书的范围)。同样,在可穿戴 AR 设备上,需要一个黑色的摄像机背景,但没有视频流,以便在你的虚拟 3D 图形上方混合视觉透视视图。
视频流源由 AR Camera Manager 组件控制。例如,你可以看到 面向方向 可以从 世界 更改为 用户,用于自拍人脸追踪应用(见 第九章**,自拍:制作有趣的面孔)。
当您想在渲染虚拟对象时模拟真实世界光照时,会使用光照估计选项。我们将在本章后面使用此功能。
如果您发现相机功能不适合您的 AR 应用,您还可以选择禁用自动对焦。
小贴士 - 何时禁用 AR 的自动对焦
通常,我会禁用 AR 应用的自动对焦。当软件使用视频流来帮助检测环境中的平面和其他特征时,它需要一个清晰、一致且详细的视频流,而不是可能因自动对焦而不断变化的视频流。这会使准确处理与 AR 相关的算法以解码其跟踪变得困难。另一方面,自拍人脸跟踪应用可能允许启用自动对焦,并且当用户背后的区域因景深而失去焦点时,可能会改善用户体验。
AR 姿态驱动器组件负责在真实世界中跟踪设备时更新 AR 摄像机的变换。(例如,还有类似用于 VR 头戴设备和手柄的组件。)此组件依赖于 XR 插件和输入 XR 子系统来提供位置跟踪数据(见docs.unity3d.com/Manual/XRPluginArchitecture.html)。
我们的下一步是向场景中添加平面和点云可视化器。
添加平面和点云管理器
当您的应用运行时,您会要求用户扫描房间,以便 AR 软件检测环境中的特征,如深度点和平面。通常,您会在检测到这些特征时向用户展示。我们通过向 AR 会话原点游戏对象添加相应的特征管理器来实现这一点。例如,要可视化平面,您将向 AR 会话原点对象添加AR 平面管理器,而要可视化点云,您将添加AR 点云管理器。
AR Foundation 支持检测和跟踪以下功能:
-
锚点:物理环境中的固定姿态(由位置和旋转组成)(由 AR 锚点管理器组件控制)。这也被称为参考点。
-
反射探针:用于渲染具有光泽表面材料的场景反射探针(由 AR 环境探针管理器组件控制)。
-
人脸:由 AR 设备检测到的人脸(由 AR 人脸管理器组件控制)。
-
人体:可跟踪的人体及其骨骼(由 AR 人体管理器组件控制)。
-
图像:在环境中的 AR 跟踪图像管理器组件中检测和跟踪的二维图像。
-
参与者:协作会话中的另一个用户(设备)。
-
平面:通常从点云中推断出的水平或垂直的平面(由 AR 平面管理器组件控制)。
-
点云:由 AR 设备检测到的深度点集(由 AR 点云管理器组件控制)。
-
对象:在环境中检测和跟踪的 3D 对象(由 AR 跟踪对象管理器组件控制)。
并非所有这些功能在所有平台上都受支持。请参阅您当前版本的 AR Foundation 文档(例如,访问 docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.1/manual/index.html#platform-support 并在左上角选择您的版本)。我们将在本书的各个项目中使用其中许多功能。在这里,我们将使用平面和点云可追踪对象。请按照以下步骤添加它们:
-
从 层次结构 窗口中选择 AR 会话原点 对象。
-
通过在搜索输入字段中选择
ar并点击 AR 点云管理器 来添加点云管理器。 -
通过在搜索输入字段中选择
ar并点击 AR 平面管理器 来添加平面管理器。 -
在 AR 平面管理器上,将 检测模式 更改为仅水平平面,通过选择 无(以清除列表),然后选择 水平。
你会注意到点云管理器有一个空槽用于点云预制件可视化器,而平面管理器有一个空槽用于平面预制件可视化器。我们将使用来自 Samples 项目的预制件,如下所示:
-
在 检查器 窗口中,转到 AR 点云管理器 | 点云预制件 并在字段右侧按下 甜甜圈 图标以打开 选择游戏对象 对话框。
-
点击 资产 选项卡并双击 AR 点云可视化器 预制件。
您还可能想尝试其他一些点云可视化器预制件,例如 AR 点云调试可视化器 和 AllPointCloudPointsPrefab。
-
同样地,对于AR 平面管理器 | 平面预制件,在字段右侧按下 甜甜圈 图标以打开 选择游戏对象 对话框。
-
点击 资产 选项卡并双击 AR 羽毛平面。
还有一些其他的平面可视化预制件可以尝试,例如 AR 平面调试可视化器、AR 羽毛平面淡入 和 棋盘格平面。
-
通过转到 文件 | 保存 来保存场景。
我们正在使用从 Samples 项目中获得的可视化器预制件。在本章的后面部分,我们将更多地讨论预制件,更仔细地查看可视化器预制件,并学习如何编辑它们以创建我们自己的自定义可视化器。首先,我们将向场景中添加 AR Raycast 管理器。
添加 AR Raycast 管理器
我知道我们很快还需要另一个组件,称为 AR Raycast 管理器。这个组件将由我们的脚本用于确定用户的屏幕触摸是否对应于 AR 软件检测到的 3D 跟踪特征。我们将在脚本中使用它来在平面上放置对象。按照以下步骤将其添加到场景中:
-
从 层次结构 窗口中选择 AR 会话原点 对象。
-
在搜索输入字段中输入
ar,然后点击AR 射线投射管理器。
在检查器窗口中,我们现在可以看到我们添加的管理组件的AR 会话原点GameObject 看起来是这样的:

图 2.7 – 带有各种管理组件的 AR 会话原点
另一个方便包含的功能是光估计,它有助于更真实地渲染您的虚拟对象。
添加光估计
通过向您的方向光源添加光估计组件,AR 摄像头可以在渲染场景时使用这些信息,尝试使场景的照明更接近真实世界环境。
要添加光估计,执行以下步骤:
-
在层次窗口中,选择方向光对象。
-
在
光估计中,添加基本光估计组件。 -
在层次窗口中,找到AR 摄像头(AR 会话原点的子项),将其拖入检查器窗口,并将其放置在光估计 | 摄像头管理器槽中。
-
在层次窗口中,选择AR 摄像头,然后将AR 摄像头管理器 | 光估计设置为全部。请注意,并非所有平台都支持所有光估计功能,但使用全部标志将使它们在运行时使用所有可用的功能。
-
通过访问文件 | 保存来保存您的作品。
好的!我认为我们应该尝试构建并运行到目前为止所做的工作,并确保它正在运行。
构建和运行场景
目前,场景初始化 AR 会话,启用 AR 摄像头扫描环境,检测点和平面,并使用可视化工具在屏幕上渲染这些内容。让我们构建场景并确保它运行:
-
通过访问文件 | 构建设置来打开构建设置窗口。
-
对于构建中的场景列表,点击添加打开场景按钮,并取消选中列表中除当前场景之外的所有场景(我的场景名为BasicARScene)。
-
确保您的设备通过 USB 连接到您的电脑。
-
按下
Builds/。如果需要,输入一个文件名,然后按保存。完成这个任务可能需要一些时间。
应用应该能够在您的设备上成功构建和运行。如果您遇到任何错误,请仔细阅读控制台窗口中的错误消息。然后,回顾本章和第一章**,设置 AR 开发环境中详细说明的每个设置步骤。
当应用启动时,您应该在屏幕上看到一个视频流。慢慢将设备向不同方向移动,靠近或远离。当它扫描环境时,特征点和平面将被检测并在屏幕上使用您选择的可视化工具渲染。
接下来,让我们添加一个功能,可以点击其中一个平面以在那里实例化一个 3D 对象。
在平面上放置一个对象
我们现在将添加用户在平面上点击并放置 3D 虚拟对象在场景中的功能。实现这一功能有几个部分:
-
当用户点击屏幕时设置放置对象输入动作。
-
编写一个 PlaceObjectOnPlane 脚本,该脚本响应输入动作并在平面上放置一个对象。
-
使用 AR Raycast Manager 确定放置对象的位置和平面。
-
导入 3D 模型并将其制作成预制件以放置在此场景中。
让我们从创建一个屏幕点击的输入动作开始。
设置 PlaceObject 输入动作
我们将使用 Unity 输入系统包来处理用户输入。如果您对输入系统不熟悉,本节中的步骤可能看起来很复杂,但这只是因为它的多功能性。
输入系统允许您定义 动作,这些动作将输入的逻辑意义与物理输入方式分开。使用命名动作对应用程序和程序员来说更有意义。
注意 – 输入系统教程
要了解使用输入系统包的更完整教程,请参阅 learn.unity.com/project/using-the-input-system-in-unity。
在这里,我们将定义一个与屏幕点击输入数据绑定的 PlaceObject 动作。我们将现在设置它,然后在下一节中使用这个输入动作来找到被点击的 AR 平面并在那里放置一个虚拟对象。
在我们开始之前,我将假设您已经通过 包管理器 导入了 输入系统 包,并在 玩家设置 中将 活动输入处理 设置为 输入系统包(或 两者)。现在,按照以下步骤操作:
-
在
输入中使用 右键点击_App/文件夹。 -
通过在
输入文件夹内 右键点击,然后选择AR 输入动作来创建一个输入动作控制器资产。 -
点击 编辑资产 以打开其编辑器窗口。
-
在最左侧的
ARTouchActions。 -
在中间的
PlaceObject中使用 右键点击 | 重命名。 -
在右侧的 属性 面板中,将 动作类型 设置为 值。
-
将其 控制类型 设置为 Vector 2。
-
在中间的 动作 面板中,点击子项 <无绑定> 以添加绑定。
-
在右侧的 属性 面板下 绑定 中,使用 路径 选择列表,选择 TouchScreen | Primary Touch | Position。
-
在窗口顶部,点击 保存资产(除非已勾选 自动保存 复选框)。
通过这样,我们创建了一个名为 Vector2 的数据资产,其中包含像素坐标中的 X、Y 值。输入动作资产在以下屏幕截图中显示:

图 2.8 – 我们为屏幕点击设置的 AR 输入动作
现在,我们可以将输入动作添加到场景中。这可以通过玩家输入组件来完成。对于我们的 AR 场景,我们将在 AR Session Origin 上添加一个玩家输入组件,如下所示:
-
在 Hierarchy 窗口中,选择 AR Session Origin 对象。
-
在其 Inspector 窗口中,点击 Add Component | Input | Player Input。
-
从
Inputs/文件夹到 Inspector 窗口中的 Player Input | Actions 插槽。 -
留下
On前缀(例如,OnPlaceObject)并接收一个InputValue参数(docs.unity3d.com/Packages/com.unity.inputsystem@1.1/api/UnityEngine.InputSystem.InputValue.html)。广播消息: 与 Send Messages 类似,广播消息将向此 GameObject 的组件及其所有子组件发送消息(
docs.unity3d.com/ScriptReference/Component.BroadcastMessage.html)。调用 Unity 事件: 你可以使用 Inspector 或在脚本中设置事件回调函数(
docs.unity3d.com/Manual/UnityEvents.html)。回调函数接收一个InputAction.CallbackContext参数(docs.unity3d.com/Packages/com.unity.inputsystem@1.1/api/UnityEngine.InputSystem.InputAction.CallbackContext.html))。调用 C# 事件: 你可以在脚本中设置事件监听器(
docs.microsoft.com/en-us/dotnet/csharp/programming-guide/events/)。要了解更多关于 Player Input 组件的信息,请参阅
docs.unity3d.com/Packages/com.unity.inputsystem@1.0/api/UnityEngine.InputSystem.PlayerInput.html。
我已经决定使用 OnPlaceObject 函数,我们将在下一部分进行操作。但首先,我将提供一个关于 Unity C# 编程的快速介绍。
介绍 Unity C# 编程和 MonoBehaviour 类
编写 C# 脚本是每个 Unity 开发者必备的技能。你不需要成为编程专家,但无法避免编写一些代码来使你的项目工作。如果你是编程新手,可以简单地遵循这里提供的说明,随着时间的推移,你会变得更加熟练。我也鼓励你学习 Unity 提供的一些优秀的入门教程(learn.unity.com/)以及其他教程,包括以下内容:
-
Unity 中 C# 编程入门: https://unity3d.com/learning-c-sharp-in-unity-for-beginners
-
入门脚本编程: https://learn.unity.com/project/beginner-gameplay-scripting
既然如此,当我们通过这一节时,我会提供一些简要的解释。但我会假设您至少对 C#语言语法、常见的编程词汇(例如,类、变量和函数)、使用编辑器(例如 Visual Studio)以及如何阅读由于打字错误或其他常见的编码错误而在您的控制台窗口中出现的错误消息有基本的了解。
我们将创建一个名为PlaceObjectOnPlane的新脚本。然后,我们可以将此脚本作为组件附加到场景中的 GameObject 上。它将随后出现在对象的检查器窗口中。让我们首先执行以下步骤:
-
在
Scripts/文件夹中(我的文件夹是Assets/_App/Scripts/),右键单击它,然后选择创建 | C# 脚本。 -
将文件命名为
PlaceObjectOnPlane(文件名中不允许有空格或其他特殊字符,并且应以大写字母开头)。这创建了一个具有
.cs文件扩展名的新 C#脚本(尽管您在项目窗口中看不到扩展名)。 -
双击PlaceObjectOnPlane文件以在您的代码编辑器中打开它。默认情况下,我的系统使用 Microsoft Visual Studio。
如您在以下模板的初始脚本内容中看到的,PlaceObjectOnPlane.cs文件声明了一个名为PlaceObjectsOnPlane的 C#类,其名称与.cs文件相同(名称必须匹配;否则,将在 Unity 中引起编译错误):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlaceObjectOnPlane : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
此脚本中的前三行有一个using指令,它声明了一个将用于脚本中的 SDK 库或命名空间。当脚本引用外部符号时,编译器需要知道它们的位置。在这种情况下,我们说的是我们可能会使用标准.NET 系统库来管理对象集合(集合)。在这里,我们正在使用 Unity API。
UnityEngine定义的符号之一是PlaceObjectsOnPlane类被声明为MonoBehaviour的子类。(请注意其英式拼写,“iour”)。附加到场景中 GameObject 的脚本必须是MonoBehaviour的子类,它提供了一系列与附加的 GameObject 相关的功能和服务的功能。
首先,MonoBehaviour提供了对 GameObject 生命周期和 Unity Start()函数的钩子。这是一个添加一些初始化代码的好地方。
Unity 游戏引擎的主要目的是每帧渲染当前场景视图,可能是每秒 60 次或更多。每次帧更新时,您的Update()函数将自动被调用。这就是您放置需要每帧运行的任何运行时代码的地方。尽量保持Update()中完成的工作量最小;否则,您的应用程序可能会感觉缓慢和笨拙。
您可以在此处了解更多关于 MonoBehaviour 类的信息:MonoBehaviour。要全面了解 GameObject 和 MonoBehaviour 脚本的生命周期,请查看此流程图:执行顺序。
我们现在可以编写脚本。由于这是本书中的第一个脚本,我会慢慢展示。
编写 PlaceObjectOnPlane 脚本
PlaceObjectOnPlane 脚本的目的是在用户触摸时将虚拟对象放置在 AR 平面上。我们首先概述逻辑(在 C# 中,同一行后面的任何文本 // 都是注释):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlaceObjectOnPlane : MonoBehaviour
{
void OnPlaceObject(InputValue value)
{
// get the screen touch position
// raycast from the touch position into the 3D scene looking for a plane
// if the raycast hit a plane then
// get the hit point (pose) on the plane
// if this is the first time placing an object,
// instantiate the prefab at the hit position and rotation
// else
// change the position of the previously instantiated object
}
}
结果表明,在此脚本中不需要 Update 函数,因为它仅用于帧更新,而此脚本可以忽略这些更新。
此脚本实现了 OnPlaceObject 方法,该方法在用户触摸屏幕时被调用。正如我们之前提到的,我们添加到 AR Session Origin 的 Player Input 组件使用 OnPlacedObject 作为 InputValue。请注意,我还添加了一行使用 UnityEngine.InputSystem;,它定义了 InputValue 类。
首先,我们需要从传递的输入值中获取屏幕触摸位置。添加以下代码,它声明并分配给局部变量 touchPosition:
// get the screen touch position
Vector2 touchPosition = value.Get<Vector2>();
下一步是确定屏幕触摸是否对应于在 AR 场景中检测到的平面。AR Foundation 通过使用我们之前添加到 AR Session Origin GameObject 的 AR Raycast Manager 组件来提供解决方案。现在,我们将使用它来编写脚本。将以下行添加到脚本顶部:
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
然后,在 OnPlaceObject 函数内部,添加以下代码:
// raycast from the touch position into the 3D scene looking for a plane
// if the raycast hit a plane then
ARRaycastManager raycaster = GetComponent<ARRaycastManager>();
List<ARRaycastHit> hits = new List<ARRaycastHit>();
if (raycaster.Raycast(touchPosition, hits, TrackableType.PlaneWithinPolygon))
{
//
}
首先,我们获取 raycaster 的引用。我们声明并初始化一个 ARRaycastHit 列表,当射线检测到物体时,该列表将被填充。然后,我们调用 raycaster.Raycast(),传入屏幕的 touchPosition 和 hits 列表的引用。如果找到平面,它将返回 true 并将 hits 列表填充详细信息。第三个参数指示 raycaster.Raycast 可以击中的跟踪类型。在这种情况下,PlaneWithinPolygon 过滤器用于 2D 凸多边形平面。
信息 - 关于 AR 射线投射的更多信息
有关使用 ARRaycastManager 的更多信息,请参阅这里。
您可以在可传递的跟踪类型列表中查看。
if语句内的代码只有在raycaster.Raycast返回true时才会执行;也就是说,如果用户在屏幕上点击了一个可以投射到场景中可追踪平面的位置。在这种情况下,我们必须在那里创建一个 3D GameObject。在 Unity 中,创建一个新的 GameObject 被称为实例化对象。您可以在此处了解更多信息:docs.unity3d.com/Manual/InstantiatingPrefabs.html。
首先,让我们声明一个变量placedPrefab,用于保存我们想要在所选平面上实例化的预制体的引用。使用[SerializedField]指令允许属性在 Unity 检查器中可见并可设置。我们还将声明一个private变量spawnedObject,它保存已实例化对象的引用。将以下代码添加到类顶部:
public class PlaceObjectOnPlane : MonoBehaviour
{
[SerializeField] GameObject placedPrefab;
GameObject spawnedObject;
现在,在if语句内部,如果这是用户第一次点击屏幕,我们将实例化一个新的对象,并将其分配给spawnedObject。如果对象已经实例化并且用户再次点击屏幕,我们将把对象移动到新位置。添加以下突出显示的代码:
public void OnPlaceObject(InputValue value)
{
// get the screen touch position
Vector2 touchPosition = value.Get<Vector2>();
// raycast from the touch position into the 3D scene looking for a plane
// if the raycast hit a plane then
ARRaycastManager raycaster = GetComponent<ARRaycastManager>();
List<ARRaycastHit> hits = new List<ARRaycastHit>();
if (raycaster.Raycast(touchPosition, hits, TrackableType.PlaneWithinPolygon))
{
// get the hit point (pose) on the plane
Pose hitPose = hits[0].pose;
// if this is the first time placing an object,
if (spawnedObject == null)
{
// instantiate the prefab at the hit position and rotation
spawnedObject = Instantiate(placedPrefab, hitPose.position, hitPose.rotation);
}
else
{
// change the position of the previously instantiated object
spawnedObject.transform.SetPositionAndRotation( hitPose.position, hitPose.rotation);
}
}
}
Raycast填充了一个包含击中点的列表,因为用户点击屏幕的线上可能有多个可追踪的平面。它们按从近到远的顺序排序,所以在这种情况下,我们只对第一个感兴趣,即hits[0]。从那里,我们获取点的Pose,这是一个包含 3D 位置和旋转值的简单结构。这些值在放置对象时被使用。
然后,保存脚本文件。
现在,回到 Unity 中,我们将通过以下步骤将我们的脚本作为组件附加到AR Session Origin:
-
首先,检查控制台窗口(使用控制台选项卡或窗口 | 通用 | 控制台)并确保没有来自脚本的编译错误。如果有,请回到您的代码编辑器并修复它们。
-
在层次窗口中,选择AR Session Origin对象。
-
在脚本中声明的
placedPrefab变量中。让我们用 Samples 资源提供的红色立方体预制体填充它。 -
在
ARF-samples/Prefabs/文件夹中。 -
将AR Placed Cube预制体拖动到检查器窗口中的Place Object On Plane | Placed Prefab槽位。
-
通过访问文件 | 保存来保存场景。
我们作为组件在 AR Session Origin GameObject 上的脚本现在应该如下所示:
![图 2.9 – 将 PlaceObjectOnPlane 作为组件使用,其 Placed Prefab 槽位已填充]

图 2.9 – 将 PlaceObjectOnPlane 作为组件使用,其 Placed Prefab 槽位已填充
让我们试试!我们现在准备好构建和运行场景。
构建和运行场景
如果您之前已经构建了场景,在上一节中,您可以访问文件 | 构建并运行以启动此过程。否则,执行以下步骤来构建和运行场景:
-
通过访问文件 | 构建设置来打开构建设置窗口。
-
对于构建中的场景列表,点击添加打开场景按钮,取消勾选列表中除这个场景之外的所有场景(我的名为BasicARScene)。
-
确保您的设备通过 USB 连接。
-
按下
Builds/。如果需要,输入一个文件名,然后按Update()按钮将其更新为Start()(例如,初始化raycaster变量)。 -
避免在
Update()中分配新的内存,以避免内存碎片化和垃圾回收(例如,将hits列表初始化为类变量)。
修改后的脚本如下所示。更改的代码以顶部部分开始,其中包含新的类变量和Start()函数:
public class PlaceObjectOnPlane : MonoBehaviour
{
[SerializeField] GameObject placedPrefab;
GameObject spawnedObject;
ARRaycastManager raycaster;
List<ARRaycastHit> hits = new List<ARRaycastHit>();
void Start()
{
raycaster = GetComponent<ARRaycastManager>();
}
现在,添加OnPlacedObject函数,如下所示:
public void OnPlaceObject(InputValue value)
{
// get the screen touch position
Vector2 touchPosition = value.Get<Vector2>();
// raycast from the touch position into the 3D scene looking for a plane
// if the raycast hit a plane then
// REMOVE NEXT TWO LINES
// ARRaycastManager raycaster = GetComponent<ARRaycastManager>();
//List<ARRaycastHit> hits = new List<ARRaycastHit>();
- if (raycaster.Raycast(touchPosition, hits, TrackableType.PlaneWithinPolygon))
{
请保存脚本,然后再次构建并运行它以验证它仍然有效。
信息 - 公共与私有以及对象封装
面向对象编程的驱动原则之一是为此目的进行private和public声明。在 C#中,任何未声明为public的符号都假定是私有的。在 Unity 中,任何公共变量在脚本作为组件附加到游戏对象时,在检查器窗口中也是可见的(序列化的)。通常,私有变量是不可见的。使用[SerializeField]指令可以使私有变量在检查器窗口中也可见并可修改。
恭喜!这并不一定是一个出色的应用程序,它是根据 Samples 项目中的示例场景建模的,但你从文件 | 新场景开始,完全自己构建了它。现在,让我们玩玩它,找到一个比小红立方体更有趣的 3D 模型。
创建用于放置的预制件
我们在本章中放置在平面的预制件对象是名为AR Placed Cube的对象,它是从 AR Foundation Samples 项目中导入的。让我们找到一个不同、更有趣的模型来使用。在这个过程中,我们将了解更多关于游戏对象、变换和预制件的知识。
理解游戏对象和变换
我认为一个好的开始是仔细看看我们一直在使用的AR Placed Cube预制件。让我们通过以下步骤在编辑器中打开它:
-
在
ARF-samples/Prefabs/文件夹中。 -
双击AR Placed Cube预制件。
我们现在正在编辑预制件,如下面的截图所示(我已将我的窗口布局调整为与默认布局不同):

图 2.10 – 编辑 AR Placed Cube 预制件
(0, 0, 0)), (0, 0, 0)), 和 (1, 1, 1))。
在放置在 AR 立方体下面的子立方体是0.05, 0.05, 0.05(单位是米,0.05 米大约是每边 2 英寸)。这就是它在我们的应用物理环境中放置时的尺寸。
你还会注意到子立方体的 X-Y-Z 坐标0, 0.025, 0),在 Unity 中,Y 轴是向上轴。由于 0.025 是 0.05 的一半,我们将立方体提升到其高度的一半以上零 X-Z 平面。
立方体的原点是它的中心。因此,AR 放置立方体的原点是子立方体的底部。换句话说,当我们放置这个预制件在场景中时,立方体的底部侧面位于由射线投射确定的姿态位置。
使用空的 GameObject 来归一化模型的比例并调整其原点,在 Unity 开发中是一个常见的模式。
现在,让我们为我们的应用找到一个不同的模型,并在将其制作成预制件时对其进行归一化。
寻找 3D 模型
要找到一个 3D 模型,你可以自由地在互联网上搜索你喜欢的 3D 模型。如果你是 3D 艺术家,你可能已经有了自己的模型。你将需要一个相对简单、低多边形的模型(即多边形数量不多)。寻找.FBX或.OBJ格式的文件,因为它们将无需转换即可导入 Unity。
我在cgtrader.com这里找到了一个病毒微生物的模型:www.cgtrader.com/free-3d-models/science/medical/microbe。这是一个免费下载且无版税的模型,有 960 个多边形,以 FBX 格式提供。我的文件名为uploads_files_745381_Microbe.fbx。
一旦你找到了文件并将其下载到你的电脑上,执行以下步骤将其导入 Unity 中:
-
在你的
_App文件夹下的Models中(这一步是可选的)。 -
将模型从你的 Windows 文件资源管理器或 macOS Finder 拖到
Models文件夹中,以将其导入到项目中。或者,你也可以通过主菜单点击Assets | Import New Asset。 -
当你在Project窗口中选择模型时,你可以在Inspector窗口中查看它。在那里,看看许多导入设置。通常,你可以保持它们的默认值。
现在,我们将制作模型的预制件,并确保它已缩放到可用的尺寸。我喜欢使用临时的立方体对象来测量它:
-
在你的
_App文件夹下的Prefabs中(这一步是可选的)。 -
在
Prefabs文件夹内右键点击,选择Virus)。 -
双击新的预制件,或者在Inspector窗口中点击其Open Prefab按钮。
-
为了测量目的,从主菜单中选择GameObject | 3D Object | Cube来添加一个临时立方体(或者使用左上角的+按钮,或者在Hierarchy窗口中直接右键点击)。
-
假设我想我的模型在场景中看起来与之前使用的红色立方体大小相同,设置这个测量立方体
0.05, 0.05, 0.05)和其0, 0.025, 0)。 -
将你从
Models文件夹中导入的 3D 模型拖到Hierarchy窗口中,作为根对象的子对象。 -
使用
0.5, 0.05, 0.05),0, 0.04, 0),0, 0, 0)。 -
删除或禁用立方体。在立方体被选中时,在其检查器窗口中,取消勾选左上角的启用复选框。
-
通过点击场景窗口顶部的保存按钮来保存预制件。
我找到的模型没有附带材质,所以现在让我们为它创建一个。在我们正在编辑的预制件仍然打开以供编辑时,执行以下额外步骤:
-
在你的
_App文件夹下的Materials中(此步骤是可选的)。 -
在
Materials文件夹内右键点击,选择Virus Material。 -
将病毒材料拖放到层次窗口中的模型对象(uploads_files_745381_Microbe)上。
-
在
0.5中选择微生物模型。 -
再次,保存你的预制件。
-
通过点击层次窗口左上角的<按钮返回场景编辑。
当打开进行编辑时,我的预制件现在看起来是这样的(我已经重新排列了我的窗口,使它们与默认布局不同):

图 2.11 – 编辑我的病毒预制件
我们现在准备好将这个预制件添加到场景中。之后,我们将构建并运行完成的项目。
完成场景
我们现在有了自己的预制件可以放置在 AR 场景中。让我们将其添加到放置对象在平面上组件中,如下所示:
-
确保你已经退出了预制件编辑模式,现在正在编辑 BasicARScene。
-
在层次窗口中选择AR 会话原点对象。
-
从
_App/Prefabs/Virus拖放到检查器窗口,到放置对象在平面上 | 放置预制件槽位。 -
使用文件 | 保存来保存场景。
-
通过转到文件 | 构建并运行来构建并运行场景。
如以下截图所示,我已经在我的桌子上感染了病毒!

图 2.12 – 运行项目显示键盘上的病毒
就这样。你已经成功创建了一个增强现实场景,将虚拟 3D 模型放置在现实世界中。也许你不会选择病毒,但这却是时代的标志!
你现在可以开始创建自己的 Unity AR 项目了。
摘要
在本章中,我们使用 AR Foundation 检查了增强现实场景的核心结构。我们从 Unity 的 AR Foundation Samples 项目开始,构建它以在您的设备上运行,然后将其资产导出为资产包以供重用。然后,我们将这些示例资产导入到我们的项目中,更详细地查看SimpleAR场景,并在您的设备上构建它。
然后,从一个全新的空白场景开始,我们从头开始构建了自己的基本 AR 演示,允许用户在物理世界环境中放置一个虚拟 3D 对象。为此,我们添加了OnPlaceObject动作消息。这个函数从屏幕触摸位置进行射线投射,以在可追踪的水平平面上找到一个姿态点。然后,它在该位置平面上实例化一个对象。我们通过在互联网上找到一个 3D 模型,将其导入到项目中,从模型创建一个缩放预制件,并将其用作场景中放置的虚拟对象来结束这一章节。在过程中,我们多次进行了构建和运行项目,以验证我们在此阶段的工作在目标设备上是否按预期运行。
在下一章中,我们将探讨工具和实践,以促进 AR 项目的开发和故障排除,这将有助于提高开发者的工作流程,然后再继续在后续章节中创建更完整的项目。
第三章:第三章:改进开发者工作流程
在开发增强现实(AR)时,就像任何软件开发一样,了解你的工具、学习如何在遇到“卡住”时进行故障排除,并努力使你的整体开发者工作流程更加高效是非常重要的。在本章中,我们将探讨一些故障排除和测试开发中 AR 应用程序的最佳实践、技术和高级工具。
Unity 通常对移动设备开发非常友好。例如,你通常使用编辑器播放模式在编辑器中预览你的场景,允许快速开发-测试-更新-重复循环。并且,使用编辑器远程工具,你可以在目标移动设备上运行和测试,而无需每次都进行构建。
但是,增强现实提出了独特的挑战,因为它需要在远程设备上获取传感器输入,包括实时摄像头流和运动传感器。它还需要在移动软件(Android、iOS)中集成的 AR 处理,以检测环境中的特征(如平面或人脸)并跟踪你的物理设备在现实世界中的位置。你的应用程序需要这些数据,但它们是远程的,并且在编辑器播放模式下通常不可用。在本章中,我们将探讨各种技术和工具来处理这些问题,并改善 AR 开发工作流程。
在本章中,你将了解以下内容:
-
使用日志消息进行故障排除
-
使用调试器进行调试
-
使用编辑器远程工具进行测试
-
使用 Unity 项目 MARS 模拟环境
如果你急于开始开发一个 AR 项目,可以直接跳过本章,进入第四章,创建 AR 用户框架,在那里我们将开始我们的第一个实际项目。如果这样做,请继续,但请计划一旦你意识到本章能帮助你,就尽快回来。
技术要求
本章除了需要一个安装了 Unity 的工作开发系统、设置了 XR 插件和 AR Foundation 包的项目,以及能够在目标设备上成功构建和运行的能力之外,没有特殊的技术要求,这些要求在第一章,为 AR 开发做准备中已有说明。本章中创建的脚本和资源可以在本书的 GitHub 仓库中找到:github.com/PacktPublishing/Augmented-Reality-with-Unity-AR-Foundation。
使用日志消息进行故障排除
如果在开发或运行你的 Unity 项目时发生错误(并且当它发生时),你必须做的第一件事就是查看控制台窗口中的信息。控制台窗口是你会找到各种信息的地方,包括资产导入警告、编译器错误、播放模式中的运行时错误、当你构建并运行时的构建问题以及其他问题。编译器错误(如编码语法错误)可能会阻止场景运行(并且播放按钮将变为禁用状态)。
控制台信息有三个级别:信息、警告(以橙色显示)和错误(以红色显示)。你可以使用控制台窗口工具栏中的切换按钮来过滤信息,如下面的屏幕截图所示:

图 3.1 – 显示 null 异常错误的控制台窗口
运行时错误,例如null。
小贴士:“警告”信息可能是多余的
我通常忽略 Unity 编辑器控制台窗口中的警告信息,除非我故意在寻找某些东西,因为它们通常很冗长,与我自己的问题解决不相关,因此变成了噪音而不是信息。你可以通过在控制台工具栏中取消点击警告按钮来隐藏警告信息。
在Assets/文件夹中。在前面的屏幕截图图 3.1中,当调用 C#的System.Int32.Parse函数时,MyScript.cs文件的第 27 行发生了 null 异常错误。
小贴士:仔细阅读你的控制台信息
我经常看到新手和经验丰富的开发者都犯的一个常见错误就是没有仔细阅读错误信息。当你处于事情的处理流程中时,很容易假设你知道信息在说什么,而没有真正阅读它,从而错过了调试所需的关键线索。
你也可以使用Debug.Log调用从你的脚本向控制台写入信息。
使用 Debug.Log
当编写 C#脚本时,你可以使用Debug.Log()函数调用将你的信息记录到控制台。这是检查和了解代码在运行时内部发生什么的最常见方法。Debug.Log信息在控制台中显示为信息消息(你也可以调用Debug.LogError()使它们显示为错误消息)。
例如,假设我正在尝试定位我项目中一个错误的根本原因。假设有几个与这个问题相关的MonoBehaviour脚本是我正在开发的。我可能会在特定函数的入口处放置日志语句,以及其他日志语句来打印出我怀疑的特定变量。以下是一个名为MyScript.cs的脚本的示例代码:
// MyScript.cs
using UnityEngine;
class MyScript : MonoBehaviour
{
public int number;
void Start()
{
number = 10;
}
void Update()
{
if (number >= 0)
{
Debug.Log("in MyScript Update, count = " + number);
DoSomething();
number -= 1; // reduce number by one
}
}
private void DoSomething()
{
Debug.Log("inside DoSomething");
number = -1; // accidently set number to minus-1
// other code…
}
}
在 C#中,你可以使用加号(+)运算符组合(连接)文本字符串。在我们的例子中,整数number被连接到消息字符串(在Update中),C#自动将数字转换为字符串值。
通过创建一个空 GameObject(GameObject | 创建空)并将脚本文件从项目窗口拖放到 GameObject 上,将此脚本添加到场景中。然后点击播放。
当这段代码运行时,我在控制台窗口中看到的内容如下所示:

图 3.2 – 关于我的 Debug.Log 语句的控制台消息
这将揭示DoSomething只被调用了一次,而不是预期的 10 次。你能找出原因吗?
研究代码中的Update并不能解释为什么DoSomething只被调用了一次。从那里我可以重新检查逻辑,以确定为什么和何时number提前变成小于零。你可以看到错误实际上在DoSomething函数本身,它“意外地”将number = -1,导致Update中的条件在第一次之后永远不会再次调用DoSomething。你可能一直专注于Update代码,但后来发现错误实际上发生在程序更深的地方。
小贴士:在寻找错误?可能不是你正在寻找的地方
这里有一个有趣的故事。一个男人离开酒吧,看到一个男人在路灯附近绕着圈子,在人行道上寻找。 “嘿,伙计,怎么了?” 醉汉回答,“我丢了钥匙。” 于是他们一起继续寻找。最后,那个男人问,“你确定你在这里丢了吗?” 另一个回答,“嗯,我是在那里丢的。但这里的光线更好。” 当你试图寻找错误时,请记住这一点——它通常正好不是你正在寻找的地方,否则你可能已经找到了它!
到目前为止,我们一直在使用控制台窗口,通过 Unity 编辑器的播放模式来记录消息。实际上,控制台在故障排除方面非常有用,你可能还希望在运行远程设备上的项目时查看你的调试消息。接下来,让我们考虑一下如何在通过 USB 连接的移动设备上运行时使用控制台。
使用控制台与移动设备
你可以在移动设备上运行你的应用时使用控制台日志,前提是应用已启用开发模式,并且设备通过 USB 线(或等效设备)连接到 Unity 编辑器。要设置此环境,请按照以下步骤操作:
-
使用文件 | 构建设置打开构建设置窗口。
-
选中开发模式复选框。
-
点击构建并运行按钮。
-
在应用成功构建、安装到设备、启动并开始运行后,如果你将控制台附加到应用上,任何
Debug.Log调用都会出现在控制台。在 Unity 控制台窗口工具栏中,选择编辑器按钮并选择在移动设备上运行的进程。例如,以下截图显示我将编辑器控制台附加到我的 Android 设备上:

图 3.3 – 连接到 Android 设备的控制台窗口
这就这么简单。
Unity 和您的设备操作系统还提供了其他类型的日志。在 控制台 窗口中,使用右上角的三点上下文菜单来访问完整的玩家日志和编辑器日志文件。在 Android 上,您还可以使用 logcat 从您的 Android 设备获取更详细的消息。
使用 Android 设备的 logcat
在 Android 移动设备上,您可以使用名为 logcat 的工具监控 Android 本身以及设备上运行的任何应用程序的任何日志消息(包括您自己的 Unity 应用程序)。您可以通过以下步骤直接在 Unity 编辑器中通过包管理器安装和使用 logcat:
-
使用 窗口 | 包管理器 打开 包管理器 窗口。
-
在左上角的过滤器选择列表中,选择 Unity 注册表。
-
使用右上角的搜索输入字段查找
logcat。 -
选择包并点击 安装 按钮。
-
安装包之后,使用 窗口 | 分析 | Android Logcat 打开 Logcat 窗口。
在安装了 logcat 并打开其窗口后,您可以在连接的移动设备上运行您的应用程序。现在它不需要在 开发模式 下构建,也不需要连接到编辑器控制台。您会发现设备内部有很多活动;消息可能来自所有正在运行的任务,而不仅仅是您自己的应用程序!Android Logcat 窗口提供了过滤消息的方法,以仅显示在设备上运行的应用程序发出的消息:
-
使用过滤器下拉列表并选择您的应用程序。
-
输入搜索表达式以过滤消息流。
下图显示了 Android Logcat 窗口的屏幕截图:
![Figure 3.4 – Android Logcat window
![img/Figure_3.04-LogcatWindow.jpg]
图 3.4 – Android Logcat 窗口
我意识到这个屏幕截图中的文本可能太小,在这里看不清楚!这个截图的目的是让您了解窗口提供的内容。
信息:使用 Android adb 命令行工具
如果您正在为 Android 设备开发,我建议您还安装 Android adb(Android 调试桥)命令行工具。(这是 Unity 用于监视控制台日志和运行 Logcat 窗口的内部工具。)如果您已安装完整的 Android Studio (developer.android.com/studio),它可能已经存在于您的系统上。否则,您可以通过导航到 developer.android.com/studio#downloads 并滚动到页面底部的 仅命令行工具 部分来找到您平台的下载链接。
在安装了 adb(并在您的命令路径中)的情况下,您可以运行各种设备操作。有关更多详细信息,请参阅developer.android.com/studio/command-line/adb。例如,adb devices命令将列出当前连接到您的计算机的 Android 设备。adb logcat将显示内部设备日志。要仅过滤 Unity 相关的消息,请使用adb logcat -v time -s Unity command。
使用 Unity 编辑器的logcat很棒,但如果您想在未连接到计算机的移动设备上调试应用程序,您能做什么呢?这对于需要在你环境中移动的增强现实应用程序来说肯定是这样。一个解决方案是您可以创建一个虚拟控制台窗口,下面将进行解释。
信息:使用 iOS 设备的 Xcode 控制台
如果您正在为 iOS 开发,Unity 中没有logcat的等效功能。但是,您可以使用 Xcode 日志控制台查看设备日志。使用视图 | 调试区域 | 激活控制台打开控制台。
在您的应用程序中模拟控制台窗口
捕获控制台日志的策略之一是在您的应用程序中提供一个“虚拟控制台”窗口,即使您的设备已从开发计算机断开连接。这个窗口用于开发,而不是生产。其想法是将Debug.Log调用替换为一个包装函数,在运行开发版本时,该函数可以选择性地将输出到应用程序文本区域上的文本对象。
我们将在第四章中更详细地讨论 Unity Canvas对象和 UI 组件,创建 AR 用户框架,因此在这里仅提供步骤,而不进行过多解释。
要实现包装函数,请按照以下步骤操作:
-
在
scripts/文件夹中(如果您还没有,请先创建一个)。 -
在
scripts文件夹中右键单击,选择ScreenLog。 -
打开脚本进行编辑,并将默认代码替换为以下内容:
// ScreenLog.cs using UnityEngine; using UnityEngine.UI; public class ScreenLog : MonoBehaviour { public Text logText; public static ScreenLog Instance { get; private set; } void Awake() { if (!Instance) Instance = this; } private void Start() { logText.text = ""; } private void _log(string msg) { if (logText) logText.text += msg + "\n"; } public static void Log(string msg) { if (Instance) Instance._log(msg); Debug.Log(msg); } }
我们将ScreenLog实现为一个public static ScreenLog Instance变量,确保场景中只有一个ScreenLog实例,并提供将Log函数作为类方法调用的能力。(我们将在下一章中更详细地讨论类与实例方法以及单例模式。)这样,您就可以从自己的代码中的任何地方调用ScreenLog.Log()。
接下来,我们将在应用程序中添加一个文本窗口,并通过 UI 中的调试按钮切换其可见性。(如前所述,我们将在后面的章节中更详细地介绍 Unity UI。)首先,让我们假设您的 AR 应用程序将在纵向屏幕空间 Canvas 中用于移动设备,以包含文本区域:
-
从主菜单中,通过选择
Debug Canvas创建一个新的 Canvas。如果场景中尚未存在,这将也会添加一个事件系统游戏对象。 -
要编辑屏幕空间画布,让我们通过在场景窗口工具栏中单击2D按钮将场景窗口切换到 2D 视图。
-
将游戏窗口和场景窗口并排排列也很有用。因为我们正在为 AR 开发,所以将游戏窗口的显示设置为固定肖像宽高比,例如使用游戏窗口顶部工具栏中的尺寸选择列表的2160x1080 肖像。此布局可以在以下屏幕截图中看到:

图 3.5 – 场景和游戏窗口并排显示,并设置了肖像设备视图
(此屏幕截图是在完成所有这些步骤之后捕获的;你的画布还没有滚动文本区域。)
接下来,我们将添加一个滚动文本区域,我们将在此处写入日志消息:
-
在层次结构窗口中,选择调试画布。然后右键单击,并选择UI | 滚动视图。
-
调整大小并放置
400。结果为(0, 0),0,(0, 1),(1, 1),和(0.5, 1)。以下屏幕截图中显示了矩形变换组件和锚点预设菜单按钮的位置:![图 3.6 – 带有锚点预设按钮高亮的矩形变换组件]()
图 3.6 – 带有锚点预设按钮高亮的矩形变换组件
-
通过取消选中滚动矩形 | 水平复选框,仅允许垂直滚动。
-
然后在层次结构中双击调试画布以将其聚焦(你可能需要双击两次)。
-
在层次结构中展开滚动视图(三角形图标)及其视口。选择子内容对象,通过选择矩形变换 | 锚点预设 | 拉伸-拉伸来设置其锚点和大小,并使用Shift + Alt + 点击拉伸-拉伸以填充视口。
滚动视图的检查器窗口如下截图所示:

图 3.7 – 滚动视图属性设置
现在,我们可以开始处理文本元素本身:
-
在
Debug Text上。 (注意,你可能更喜欢使用TextMesh Pro文本元素,它提供了对字体和填充的更多控制,而不会产生任何性能成本——将在下一章中介绍。) -
选择调试文本游戏对象,使用锚点预设 | 拉伸-拉伸使其填充内容区域,并使用Shift + Alt + 点击拉伸-拉伸。
-
为了参考,在
[日志消息]中输入占位符字符串。 -
调整
36。 -
将对齐方式设置为底部。
-
将垂直溢出更改为溢出(而不是截断)。
-
我个人喜欢在控制台使用白色文本和黑色背景。如果你同意,在
0和200,以及255。
调试文本对象的最终层次结构和检查器设置如图所示:

图 3.8 – 调试画布层次结构和调试文本设置
接下来,我们将把我们的脚本添加到场景中:
-
在层次结构窗口中选择调试画布,在项目窗口中找到ScreenLog脚本,并将脚本文件拖放到调试画布游戏对象上。
-
仍然选择调试画布,在层次结构窗口中找到调试文本游戏对象,将其拖入检查器,并将其放到屏幕日志组件上的日志文本槽位,如图所示:

图 3.9 – 将日志文本引用设置为调试文本游戏对象
现在,我们可以在 UI 中添加一个调试按钮来切换文本面板,如下所示:
-
右键点击
调试按钮。 -
在屏幕上调整按钮的大小和位置。例如,设置其
175, 175),并使用30, 30将其锚定到屏幕的左下角。 -
在
调试。您也可以从这里调整其字体属性,例如设置36。 -
将调试按钮改为切换按钮,需要将按钮组件替换为切换组件。
在层次结构中,选择调试按钮对象。在检查器中,使用按钮组件上的三个点上下文菜单图标(或右键单击组件)并选择移除组件。
-
然后点击
切换,并添加一个切换组件。 -
现在,我们将配置切换以处理
On Value Changed事件。在切换 | On Value Changed属性中,点击右下角的小+图标。 -
将滚动视图对象从层次结构窗口拖放到点击事件的无(对象)槽位。然后在函数选择器中,选择游戏对象 | 动态布尔值 | SetActive。
切换组件现在有以下设置:
![图 3.10 – 配置了切换组件的调试按钮]()
图 3.10 – 配置了切换组件的调试按钮
-
最后,将这个设置保存为一个预制件,您可以在其他场景中重复使用。将
Prefabs/文件夹从Prefabs文件夹拖动出来,首先创建一个。文件夹名称不是必需的,这是惯例。)并且记住,如果你在层次结构中对这个画布(或其子项)进行了新的更改,请使用覆盖 | 应用全部将那些更改保存到预制资产中。
使用这个设置,我们现在可以在代码中任何需要添加信息消息的地方使用ScreenLog.Log()函数代替Debug.Log(),如图所示:

图 3.11 – 使用虚拟控制台的手机截图
这种方法的优点是您可以修改它,以便为最终用户选择性地提供状态消息日志,而不仅仅是自己的开发。
信息:第三方虚拟控制台
除了像本章中我们这样做之外,您还可以在 Asset Store 中找到第三方虚拟控制台包,它们具有各种功能和成本。例如,Lunar Mobile Console – Free 资产易于安装和使用 – 请参阅assetstore.unity.com/packages/tools/gui/lunar-mobile-console-free-82881。这些通常仅用于开发目的,不适合向最终用户公开日志消息。
为了更深入地了解他们的代码在做什么,许多程序员喜欢使用由集成开发环境(IDEs)如 Visual Studio 提供的调试器工具。
使用调试器进行调试
专业软件开发者熟悉代码调试器,用于通过在特定代码行停止执行来测试和调试程序,并检查内存和其他运行时状态。在本节中,我将向您介绍如何使用 Visual Studio 调试器与 Unity 项目。调试器可以在 Unity 编辑器的播放模式下使用,也可以在附加设备上运行的构建中使用。
使用调试器,您可以在特定代码行设置断点,执行将在该行停止,允许您查询变量的值,并等待您逐步执行或继续程序的执行。
要在编辑器播放模式下使用调试器,您不需要进行任何特殊更改,前提是您已经使用 Visual Studio 作为您的代码编辑器(或另一个受支持的交互式开发环境(IDE)如 VS Code 或 JetBrains Rider)。您可以使用编辑 | 首选项 | 外部工具来配置 Unity 以使用您首选的编辑器/调试器。例如,在以下屏幕截图中,您可以看到我的 Unity 安装已将外部脚本编辑器设置为Visual Studio Community版本:
![Figure 3.12 – 您可以在 Unity 首选项窗口中设置您的默认代码编辑器
![Figure 3.12-ExternalEditor.jpg]
图 3.12 – 您可以在 Unity 首选项窗口中设置您的默认代码编辑器
在为您的项目打开 Visual Studio(选择MyScript.cs脚本已打开,我在第 25 行创建了一个断点,在 VS Code 中屏幕上以红色点表示):
![Figure 3.13 – 在 Visual Studio 中设置断点
![Figure 3.13-VSBreakpoint.jpg]
图 3.13 – 在 Visual Studio 中设置断点
要将调试器附加到您的 Unity 编辑器会话,请使用顶部工具栏中的附加到 Unity按钮。回到 Unity,如果您尚未启用 C# 调试,您将收到如下提示:
![Figure 3.14 – Unity 提示启用调试
![Figure 3.14-EnableDebuggingDialog.jpg]
图 3.14 – Unity 提示启用调试
点击一个 启用 按钮。
注意,可以使用编辑器窗口右下角的相应图标切换调试模式。
一旦启用调试并您在编辑器中点击 播放,如果您的代码中达到断点行,执行将停止,并且 Visual Studio 将在您的桌面上获得焦点。当前代码行将在您的屏幕上以黄色突出显示:

图 3.15 – 在 Visual Studio 调试器中调试一行代码
此外,还有一些调试窗口,您可以在其中检查脚本中变量的当前值、当前的调用堆栈等。
在调试过程中,调试工具栏 也将在窗口顶部活动,如下面的截图所示:

图 3.16 – 调试工具栏
继续 按钮(1)将从此处继续运行,直到达到另一个断点。停止 按钮(2)将禁用调试模式,而步骤按钮(3)执行以下操作:进入 将代码跟随到函数调用的主体中,跳过 将运行到当前文件中的下一行代码,而向上 将您带到调用堆栈的一个级别。
您还可以在连接的移动设备上运行的代码上运行调试器。
在远程设备上进行调试
要在您的移动设备上运行的项目中运行调试器,您必须首先在项目的 构建设置 中启用 脚本调试 和 开发构建。使用以下步骤:
-
使用 文件 | 构建设置 打开 构建设置 窗口。
-
选中 开发构建 复选框。
-
选中 脚本调试 复选框。
-
可选地,选中 等待托管调试器 复选框。
-
当您准备好时,点击 构建并运行。
当应用程序在设备上运行时,按照以下方式将调试器附加到远程进程:
-
在 Visual Studio 中,从主菜单选择 调试 | 附加 Unity 调试器。
-
将出现一个对话框,其中包含潜在进程的列表,如下面的截图所示。选择您想要附加的进程并点击 确定:

图 3.17 – 将 Visual Studio 调试器附加到手机上的 Unity 进程
现在您可以在设备上运行的应用程序中设置和检查断点。注意,一旦您在手机上关闭应用程序,调试器也会在 Visual Studio 中停止并断开连接。
等待托管调试器 构建选项在您需要在 Unity 开始运行之前启动调试器时很有用。由于 Visual Studio 需要一个进程来附加,应用程序将启动,然后等待您附加调试器,如下面的截图所示:

图 3.18 – 手机上等待附加调试器的提示
事实上,在我们的这个小例子中,我们需要这个功能,因为MyScript中的Update函数可能会在我有机会附加调试器之前被调用。
信息:额外的 Unity 调试工具
Unity 提供了更多您可以使用来调试项目和深入了解底层发生情况的窗口和工具。要调试输入系统操作,请参阅窗口 | 分析 | 输入调试器。对于深入分析和性能分析,还有性能分析器、帧调试器和物理调试器,它们也位于窗口 | 分析菜单下。对于 UI,有即时模式 GUI(IMGUI)调试器,位于窗口 | 分析 | IMGUI 调试器,当自定义 Unity 编辑器用户界面时,请参阅窗口 | UI 工具包 | 调试器(UI 工具包预计将在未来扩展以用于您自己的应用程序)。甚至还有一个窗口 | 渲染管线 | 渲染管线调试窗口。
如果您能够点击播放在您的移动设备上运行项目而不必每次都进行构建和运行,那岂不是更好?让我们接下来看看编辑器远程工具。
使用编辑器远程工具进行测试
开发者已经使用 Unity 多年为 iOS 和 Android 设备开发游戏和应用程序。您希望在 Unity 编辑器中点击播放,然后在您连接的移动设备上远程运行当前场景。拥有迭代性的开发-测试-更新-重复循环对于更高效和有效的开发至关重要。
为了方便这种开发工作流程,Unity 提供了一个名为Unity Remote 5的应用程序,您可以在手机上安装它,然后将其连接到 Unity 编辑器。它适用于 Android (play.google.com/store/apps/details?id=com.unity3d.mobileremote) 和 iOS (apps.apple.com/us/app/unity-remote-4/id871767552)。它允许您使用移动设备在 Unity 编辑器中实时查看和测试您的项目,而不必每次都进行构建。该设备充当编辑器播放模式中运行的场景的“遥控器”,包括屏幕触摸、加速度计、陀螺仪和摄像头输入。
不幸的是,Remote 5 不适合 AR 开发。一个与 AR Foundation 兼容的远程工具已经被长期承诺并预期,但现在我写这篇文章时,它还不存在。也许当您阅读这篇文章时,它将作为一个免费的 Unity 核心功能可用,所以请尝试在 Unity 论坛上搜索 (forum.unity.com/?gq=AR%20Foundation%20Editor%20Play%20Mode))。
在大型开发者社区中,通常至少会有一个有才华的个人站出来,为 AR 基础库制作远程工具,并在 Unity 资产商店提供。Kyrylo Kuzyk 的 AR Foundation Editor Remote 工具可以在 assetstore.unity.com/packages/tools/utilities/ar-foundation-editor-remote-168773 找到。该工具不是免费的,并且目前不支持新的输入系统,只支持传统的输入管理器。
如果您选择购买该套餐,可以使用包管理器按照以下步骤进行安装:
-
使用 窗口 | 包管理器 打开 包管理器。
-
使用窗口左上角的选项列表过滤 我的资产 列表。
-
查找 AR Foundation Editor Remote 套件,如果需要,点击 下载,然后点击 导入。然后在 导入 对话框中,点击 导入 按钮。
-
套件安装在
Plugins/ARFoundationRemoteInstaller/文件夹中。安装程序应该会自动运行。注意,还有一个Documentation文件。
要使用 AR 基础库编辑器远程工具,请按照文档中概述的以下步骤操作:
-
前往 编辑 | 项目设置 | XR 插件管理 | 桌面 选项卡。
-
选择 AR Foundation Remote 复选框。
-
确保您的项目针对您的移动设备平台在 文件 | 构建设置 中进行设置。
-
在
Plugins/ARFoundationRemoteInstaller中,选择 Installer 资产,并查看如下截图所示的 Inspector 窗口:![图 3.19 – AR 基础库远程安装程序]()
图 3.19 – AR 基础库远程安装程序
-
点击 安装 AR 配套应用程序 按钮。
让它在您的设备上构建并安装配套应用程序。
-
在
Plugins/ARFoundationRemoteInstaller/Resources/文件夹中,选择 Settings 对象。 -
按照手机屏幕上的说明操作,并在 设置 | AR 配套应用程序 IP 字段中输入提供的 IP 地址。
您现在已设置好。当您想使用 AR Foundation Remote 时,确保 AR 配套应用程序在您的手机上正在运行。然后,点击 播放 以使用移动设备作为远程来运行您的场景。
使用编辑器远程工具可以让您使用移动设备上的 Unity 编辑器 Play 模式。摄像头和其他感应数据输入到您的 游戏 窗口中,这样您就可以在真实世界环境中进行测试,而无需使用 构建并运行。
如果我们不是在移动设备上运行您的应用程序,而是将您的真实世界环境引入 Unity 编辑器,情况会怎样呢?Unity 正在通过 Unity MARS 探索这种创新的方法。
使用 Unity MARS 进行模拟
Unity MARS(代表混合增强现实工作室)(unity.com/products/unity-mars)是 Unity Technologies 提供的解决方案,它解决了本章迄今为止讨论的许多开发增强现实应用程序的问题,以及更多。
什么是 MARS?使用 MARS,您可以在 Unity 编辑器内创建和测试复杂的 AR 应用程序,并为一系列目标物理世界环境提供运行时逻辑。
考虑以下一个场景:您正在为博物馆参观者开发一个 AR 应用程序,他们可以将他们的移动设备指向展品或艺术品,应用程序识别它并提供额外的信息和娱乐,从而提供更丰富的学习体验。但您在办公桌前,在办公室,在城镇的另一边,或在不同的城市。您如何开发和测试您的应用程序?与其制定旅行计划,您可以使用 MARS 将目标物理空间带入 Unity 编辑器,并在您自己的办公桌前开发-测试-更新-重复。
使用 MARS,您可以捕获和组装现实世界的资产,如位置、对象和道具,然后将它们拖放到 Unity 中进行测试。它支持跟踪平面、图像、面部以及许多其他具有语义意义的特征数据。MARS 文档可以在docs.unity3d.com/Packages/com.unity.mars@1.0/manual/index.html找到。
在这个博物馆场景中,第一步可能是捕获博物馆的物理环境传感器读数,以便在您的工作站上使用。本节中更详细描述的 MARS 伴侣应用程序可以用于此目的。同样,MARS 包含一系列您可以直接使用的环境模板。以下图像,例如,显示了一个厨房的模拟视图以及同一空间中的设备视图:

图 3.20 – MARS 模拟和设备视图
使用 MARS,您有可以在编辑模式或播放模式下运行的环境模拟。您可以在编辑模式下预览场景的执行,其中真实世界的代理对象被复制到模拟场景视图中,该视图与正常的项目场景视图分开。您还可以启动和停止一个连续的模拟视图,这更类似于 Unity 中的正常播放模式。输入模拟的数据可以是合成数据、记录数据或实时数据。然后您可以对各种室内和室外空间进行测试。关于 MARS 模拟如何工作的更完整解释,我推荐这篇文章:blogs.unity3d.com/2020/08/14/a-look-at-how-simulation-works-in-unity-mars。
MARS 提供了额外的工具和智能组件,以解决 AR 开发者面临的常见挑战。物理环境并不总是那么可预测。MARS 过程式创作框架模拟现实世界中的对象、条件和动作,包括“模糊创作”,在应用程序决定何时以及在哪里让用户交互时,您可以指定物理特征的最低和最高测量值。
MARS 建立在 AR Foundation 之上,因此与所有支持的 AR 设备和平台兼容。目前,在试用期间之后,使用 Unity MARS 需要支付单独的年度许可费用。
使用 MARS,您可能仍然面临如何捕获目标环境几何形状和表面特征以用于模拟的问题。这就是 MARS Companion 应用程序的用武之地。
使用 MARS Companion 应用程序进行捕获
MARS Companion 应用程序可用于捕获现实世界数据并将其带入 Unity 编辑器,以便与 Unity MARS 一起使用。
使用该应用程序,您可以扫描房间、拍照和录制视频,将此数据捕获并保存到云端。然后,可以使用 MARS 创作室将这些数据提供给 Unity 编辑器。
该应用程序还具有有限的创作功能,允许您在设备上创建内容和布局资产。例如,这可能有助于解决边缘情况,例如当照明或环境特征模糊或难以扫描时。
目前,MARS Companion 应用程序仍处于 Beta 测试阶段(forum.unity.com/threads/unity-mars-companion-app-open-beta-announcement.1037638/),并可能最终从 MARS 中分离出来,用作编辑器远程工具(参见本章前面的部分)。
Unity MARS 是用于增强现实开发的一个强大新框架。它代表了 Unity 对 AR 行业、用户、开发者和设备制造商的长期承诺。像大多数 Unity 包和模块一样,它也可以通过自定义行为、数据扩展、查询和其他附加模块进行扩展。
我在本章中并未尝试提供 MARS 的教程(它可能是一本完全独立的书籍),并且我们不会在本书的工程项目中使用 MARS。尽管如此,如果您愿意,完全可以使用 MARS 来改进本书中的项目工作流程。
摘要
增强现实本质上混合了物理世界和虚拟世界,这对 AR 开发者提出了独特的挑战。我们在桌面或笔记本电脑上开发,但应用程序的目标设备是未连接的移动设备。在 Unity Play 模式下运行应用程序时,AR 场景仍然需要从远程设备获取传感器输入。
在本章中,我们介绍了一系列工具和技术,这些工具和技术可以帮助您使用 Unity 开发和调试增强现实应用程序。我们从基本的经典“打印语句”开始,使用Debug.Log(),您可以将日志消息输出到ScreenLog.Log()包装函数,这样您可以选择在您的设备上查看日志消息,而无需完全连接到 Unity。
为了更深入地理解并调试您的应用程序,您可以使用 Visual Studio 提供的调试器。在调试过程中,您可以设置断点、检查变量值并逐行执行代码。您可以在 Unity 的 Play 模式下以及构建并运行在您的移动设备上的应用程序上运行调试器。
您还可以使用编辑器远程工具——一个在移动设备上运行并连接到 Unity 编辑器的应用程序,这样您就可以使用 Play 模式并从连接的设备接收输入数据。
然后我们对 Unity MARS 进行了简要的浏览。这个 AR 开发工作室框架颠覆了常规的远程开发模式。而不是在远程设备上运行您的应用程序以捕获环境传感器数据,MARS 允许您直接在 Unity 编辑器中使用环境传感器模拟。这为您提供了极大地改善您的开发工作流程和测试您的应用程序以适应广泛物理环境的机会,而无需离开您的办公桌。
您现在可以开始构建 AR 应用程序了。在下一章中,我们将开发一个用于控制 AR 项目中用户交互的框架。这个框架将被保存并用作构建和管理本书中每个项目中用户界面的模板。
第二部分 - 可重用 AR 用户框架
在本节中,我们将创建一个使用 Unity 和 AR Foundation 构建 AR 应用的框架。拥有这样一个框架可以概括一些我从项目到项目重复使用的场景结构。该框架管理用户交互模式、用户界面面板以及 AR 入门图形,我们将将其保存为模板,以便在本书的其他项目中重复使用。然后,我们将向您展示如何在一个简单的放置在平面上的项目中使用这个框架。
本节包括以下章节:
-
第四章**,创建 AR 用户框架
-
第五章**,使用 AR 用户框架
第四章:第四章:创建 AR 用户框架**
在本章中,我们将开发一个用于构建增强现实(AR)应用的框架,该框架管理用户交互模式和相应的用户界面(UI)。该框架包括在运行时启动 AR 会话和与 AR 功能交互时的重要用户体验(UX)步骤。这个框架将成为本书后面项目中新场景的基础。
这是一个用于构建基于模式的 Unity 框架。它概括了我发现自己从项目到项目重复的一些场景结构。例如,当 AR 应用首次启动时,它必须验证设备是否支持 AR。一旦 AR 会话初始化,应用可能会提示用户开始扫描环境以建立跟踪。在应用中的某个时候,用户可能会被提示触摸屏幕以放置虚拟对象,通常在添加对象模式中。这些步骤对于许多 AR 应用都是通用的,包括本书中的项目,因此我们将在一个可能用作模板的场景中事先设置一些基础设施。
本章涉及一些高级 C#编程。如果你已经是中级或高级程序员,你应该能够相当容易地跟随。如果你是新手,你可以直接复制/粘贴这里提供的代码并从中学习。或者,你也可以选择完全跳过本章,并使用本书 GitHub 仓库中找到的本章的场景模板。
在本章中,我们将涵盖以下主题:
-
安装我们框架的先决资产
-
从一个新的场景开始
-
创建 UI 画布和面板
-
创建 UI 控制器,使用 Singleton 类
-
创建交互模式控制器
-
创建交互模式,包括启动、扫描、主模式和 AR 模式
-
使用 Unity 入门 UX 资产
-
为新场景创建场景模板
到本章结束时,你将拥有一个名为ARTemplate的场景模板,其中包含 AR 入门功能,以及一个用户交互框架,可以作为其他 AR 项目的起点。
技术要求
要实现本章的项目,你需要在你的开发计算机上安装 Unity,并连接到一个支持 AR 应用的移动设备。我们将使用在第一章,为 AR 开发设置中设置的 Unity AR 开发项目。回顾一下,项目配置包括以下内容:
-
使用Unity Hub模板创建了一个新的项目(via Unity Hub)。
-
它在构建设置中为Android或iOS设置了目标平台,并配置了相应的玩家设置。
-
它安装了一个XR 插件,AR Foundation包,并为 AR 配置了URP 前向渲染器。
-
它安装了输入系统包,并设置活动输入处理(到输入系统包或两者)。
本章完成后的场景可以在本书的 GitHub 仓库中找到,网址为github.com/PacktPublishing/Augmented-Reality-with-Unity-AR-Foundation。
理解 AR 交互流程
在增强现实应用程序中,用户必须做的第一件事是用设备相机扫描环境,慢慢移动设备直到检测到用于跟踪的几何形状。这可能是水平平面(地板、桌面)、垂直平面(墙壁)、人脸或其他对象。以下图表显示了许多示例场景中给出的简单用户流程:
![图 4.1 – 简单的 AR 入门用户工作流程
![img/Figure_4.01-OnboardingFlow.jpg]
图 4.1 – 简单的 AR 入门用户工作流程
如前图所示,应用程序首先检查 AR 支持,请求用户允许访问设备相机和其他初始化。然后,应用程序会要求用户扫描环境以寻找可追踪的对象,并可能需要报告扫描问题,例如房间太暗或纹理不足无法检测特征。一旦实现跟踪,就会提示用户点击屏幕在场景中放置虚拟对象。
这对于演示场景来说很棒,但对于实际的 AR 应用程序来说可能过于简单。例如,在我们将在第六章“画廊:构建 AR 应用程序”中构建的 Art Gallery 应用程序中,应用程序启动后,会扫描垂直平面(墙壁)。
然后,应用程序进入主模式,用户必须点击添加按钮来添加一张新图片。这反过来会显示一个模态的选择图片菜单。当场景中添加了图片后,用户可以选择一张并进入编辑模式来移动、调整大小或以其他方式修改虚拟对象。以下图表显示了这部分一般的交互流程:
![图 4.2 – 用户交互流程,包括主、添加和编辑模式
![img/Figure_4.02-userflow-add-edit.jpg]
图 4.2 – 用户交互流程,包括主、添加和编辑模式
自然地,每个应用程序都有自己的交互流程。本章中我们构建的框架支持这种场景,并且可以适应其他需要管理当前模式状态和相应 UI 的项目。
此框架实现了一个状态机设计模式,其中场景有一个当前状态(交互模式和可见 UI)。必须满足特定条件才能从一种状态转换到另一种状态。
该框架有两个主要区域——UI 面板和交互模式。通常,模式和使用的 UI 之间将存在一对一的关联。例如,在主模式中,将会有主菜单 UI。在添加对象模式中,将会有一个 UI 提示用户在场景中放置对象。这实现了名为视图控制器的设计模式,具有 UI 视图和模式控制器。
现在我们开始在我们的场景中实现这个基本工作流程,通过向项目中添加一些额外的必备包。
安装必备资源
我们的用户交互框架使用了一些需要安装到您项目中的附加包,即 TextMeshPro、DOTween 和 Serialized Dictionary Lite。在本节中,我还会包括一些实用资产。现在让我们安装它们。
TextMeshPro
TextMeshPro 提供了高质量的文本资源,可以替换内置的文本元素。这不是强制性的,但我强烈推荐它。要导入TextMeshPro,如果您尚未在项目中安装,请执行以下步骤:
-
前往窗口 | TextMeshPro | 导入 TMP 必要资源。
-
在导入 Unity 包窗口中,点击导入。
文本 MeshPro 包现已安装。您还可以安装TMP 示例和额外资源包,其中包含额外的字体和其他可能对您的项目有用且有趣的资源。
DOTween
在我看来,DOTween 是一个不可或缺的免费包,用于在几乎任何MonoBehaviour属性上执行小型、轻量级的动画效果。没有它,您可能需要编写数十行代码才能完成 DOTween 在一行中完成的事情。DOTween 的文档可以在网上找到:dotween.demigiant.com/documentation.php。
要添加 DOTween,请执行以下步骤:
-
前往其 Unity Asset Store 页面:
assetstore.unity.com/packages/tools/animation/dotween-hotween-v2-27676。 -
点击添加到我的资产和/或在 Unity 中打开。
-
这将带您进入 Unity 项目中的包管理器窗口。
-
确保从包管理器窗口左上角的包过滤器下拉菜单中选择我的资产。
-
使用包管理器窗口右上角的搜索文本输入字段搜索
DOTween。 -
选择DOTween包,然后点击安装。
-
导入后,系统会提示您打开 DOTween 工具面板以设置包。
-
然后,点击设置 DOTween 按钮。
DOTween 现已安装并设置到您的项目中。
Serialized Dictionary Lite
C#的字典是一个键值列表结构,其中列表中的值可以通过键值引用。例如,我们将使用字典通过名称查找 UI 面板或交互模式对象。不幸的是,Unity 在编辑器的检查器窗口中不提供对字典的原生支持。Serialized Dictionary Lite是 Unity 编辑器的免费扩展,允许使用检查器编辑字典。要将 Serialized Dictionary Lite 添加到您的项目中,请执行以下步骤:
-
前往其 Unity Asset Store 页面,
assetstore.unity.com/packages/tools/utilities/serialized-dictionary-lite-110992 -
按添加到我的资产和/或在 Unity 中打开。
-
这将带您进入 Unity 项目中的包管理器窗口。
-
确保在包管理器窗口左上角的包过滤器下拉菜单中选择我的资产。
-
使用包管理器窗口右上角的搜索文本输入字段搜索
Serialized。 -
选择Serialized Dictionary Lite包,然后点击安装(或,如果提示,点击下载然后导入)。
Serialized Dictionary Lite 现在已安装到您的项目中。
其他先决条件资产
除了上述包之外,我们假设您已经将以下内容添加到您的 Unity 项目中:
-
来自第二章,您的第一个 AR 场景中创建的 Unity
ARF-samples.unity包的资产。 -
在第二章,您的第一个 AR 场景中,我们还创建了一个包含名为ARTouchActions的Action Map的AR 输入动作资产,包括(至少)一个放置对象动作。
在具备先决条件资产的情况下,我们可以开始构建场景。
从一个新的场景开始
我们以一个新的空场景开始这个项目,并按照以下步骤设置 AR Foundation 对象:使用以下步骤使用ARFramework:
-
使用文件 | 新场景创建一个新的场景。
-
选择基本(内置)模板。按创建。
-
使用
Assets/Scenes/文件夹保存场景,将其命名为ARFramework,然后点击保存。
接下来,我们将按照以下方式设置场景,使用基本的 AR Foundation 游戏对象:
-
通过右键单击并选择删除(或按键盘上的Del键)从层次结构窗口中删除主相机。
-
通过从主菜单中选择GameObject,然后选择XR | AR Session来添加一个 AR 会话。
-
通过从主菜单中选择GameObject,然后选择XR | AR Session Origin来添加一个AR 会话原点对象。
-
选择
raycast,然后添加一个AR Raycast Manager组件。 -
展开AR 会话原点并选择其子 AR 相机。在 检查器 窗口中,使用左上角的 标签 选择器将其标签设置为 MainCamera。(这不是必需的,但将场景中一个相机标记为 MainCamera 是一个好习惯)。
-
在
audio listener中,并为相机添加一个 音频监听器 组件。
为了演示目的,我们将添加一个 AR 平面管理器 组件来检测和跟踪水平平面。这可能会根据特定项目的需求而变化:
-
使用
ar plane manager,然后添加一个 AR 平面管理器 组件。 -
选择一个 AR 平面可视化预制件并将其添加到
ARF-samples/Prefabs文件夹中。
我们还可以设置一些基本的 AR 光照估计,如下所示:
-
在 层次结构 窗口中选择 主相机。在其 AR 相机管理器 组件中,将 光照估计 设置为 全部。
-
在
light estimation中,然后添加一个 基本光照估计 组件。 -
将 AR 相机 对象从 层次结构 窗口拖动到 基本光照估计 | 相机管理器 槽中。
-
使用 文件 | 保存 来保存你的工作。
现在,我们有一个名为 ARFramework 的场景,其中设置了一些内容,包括 AR 会话、AR 会话原点、AR 相机和基本光照估计。我们现在可以开始构建我们框架的 UI 面板。
创建 UI 画布和面板
主屏幕空间 UI 画布将包含各种用户界面面板,这些面板可能会在应用程序的各个时间点显示。目前,我们将包括以下 UI 面板。
-
带有任何初始化信息的启动 UI 面板
-
扫描 UI 面板,提示用户扫描可追踪的特征
-
主 UI 面板,用于显示主菜单按钮
-
非 AR UI 面板,当设备不支持增强现实时可能会显示
创建屏幕空间画布
首先,我们需要创建一个画布来包含这些面板。按照以下步骤操作:
-
从主菜单中选择
UI Canvas。我们可以将默认的 渲染模式 留为 屏幕空间 – 遮罩。如果场景中尚未存在,这也会向场景添加一个 事件系统 游戏对象。 -
默认情况下,新的画布位于屏幕空间中,这正是我们想要的。有些人更喜欢将 Canvas Scaler UI Scale Mode 从 固定像素大小 更改为 与屏幕大小缩放。
-
要编辑屏幕空间画布,让我们通过在 场景 窗口工具栏中单击 2D 按钮将 场景 窗口切换到 2D 视图。然后,在 层次结构 窗口中双击 UI Canvas 对象以将 场景 视图聚焦于此对象。
-
将 游戏 窗口和 场景 窗口并排排列也是有帮助的。因为我们正在为 AR 开发,所以将 游戏 窗口的显示设置为固定的纵向宽高比,例如使用 游戏 窗口顶部工具栏中的尺寸选择列表的 2160x1080 纵向。
在此画布上,我们将添加单独的面板。首先,让我们在屏幕顶部添加一个应用标题。
添加应用标题
让我们在屏幕顶部添加一个文本面板作为应用标题的占位符。使用以下步骤添加标题:
-
右键单击
App Title Panel。 -
选择App Title Panel对象后,在其检查器窗口中,打开锚点预设菜单(位于Rect Transform组件的右上角),并点击拉伸-顶部按钮。以下截图显示了打开的锚点预设菜单,位于Rect Transform组件的左侧:![图 4.3 – 设置为顶部拉伸的 App 标题面板的锚点预设菜单]
![img/Figure_4.03-apptitle-anchorpreset.jpg]
图 4.3 – 设置为顶部拉伸的 App 标题面板的锚点预设菜单
-
然后,按Shift + Alt + 拉伸-顶部以设置其枢轴和位置。
-
设置
100。 -
接下来,右键单击
Title Text。 -
在其
My AR Project中。 -
在Rect Transform组件的右上角使用锚点预设菜单选择拉伸-拉伸。然后,按Shift + Alt + 拉伸-拉伸。
-
将对齐设置为居中和中间。
-
您也可以根据需要调整字体大小和顶点颜色字段。
没什么可看的,但以下截图显示了游戏窗口以及应用标题:
![图 4.4 – 截图显示带有 App 标题面板锚点设置为顶部拉伸的游戏窗口]
![img/Figure_4.04-apptitlepanel.jpg]
图 4.4 – 截图显示带有 App 标题面板锚点设置为顶部拉伸的游戏窗口
现在您已经使用过锚点预设菜单,我将简化后续说明。接下来,我们将添加启动模式的面板。
创建 UI 面板
我们现在将为框架支持的每个初始交互模式创建 UI 面板。由于它们都非常相似,我们将创建第一个,然后复制并修改它以用于其他面板。
第一个 UI 面板,启动 UI,将在应用初始化时显示文本面板。使用以下步骤创建它:
-
在
Startup UI中。 -
我们不需要背景图像,因此,在检查器窗口中,使用3 点上下文菜单 | 移除组件移除Image组件。
-
点击
canvas group,并将一个Canvas Group组件添加到面板中。我们将使用此组件在本章的后面部分来淡入淡出面板。 -
右键单击启动 UI对象,并选择UI | 文本 – TextMeshPro。
-
设置
初始化中...。 -
使用其锚点预设菜单,选择拉伸-拉伸。然后,按Shift + Alt + 拉伸-拉伸。
-
将对齐设置为居中和中间。
接下来,我们可以添加一个面板,如果我们在其上运行的设备不支持 AR,则可以显示此面板。按照以下步骤创建此面板:
-
右键单击
NonAR UI。 -
展开对象并选择其子文本对象。将文本内容更改为
此设备不支持增强现实。
扫描 UI 面板将在应用程序尝试检测 AR 功能时提示用户扫描房间。按照以下步骤创建面板:
-
右键点击
Scan UI。 -
展开对象并选择其子文本对象。将文本内容更改为
Scanning… Please move device slowly。
最后,我们将为主模式 UI 添加一个占位符面板。这个面板可以后来包含,例如,应用程序的主菜单:
-
右键点击
Main UI。 -
展开对象并选择其子文本对象。出于开发目的,将文本内容更改为
Main Mode Running。
当前 UI Canvas 层级结构如图所示:

图 4.5 – UI Canvas 层级
到目前为止,我们已经在屏幕空间 UI Canvas 下创建了一个简单的 UI 面板层次结构。这些面板大部分作为占位符使用,包含一个文本元素,以便你在运行时可以看到哪个面板是活动的。随着你从这个场景构建自己的应用程序,你将在面板中填充应用程序特定的 UI 元素。
接下来,我们将创建 UI 控制器脚本。
创建 UI 控制器
拥有一个具有小型 API 的脚本,使其能够轻松地在 UI 面板之间切换,将会很方便。对于我们的框架中的控制器脚本,我决定将它们定义为单例。
类定义中的 Instance。了解更多信息请访问 wiki.unity3d.com/index.php/Singleton。
然后,我们将编写一个 UIController 脚本来控制 UI 面板的可见性。最后,我们将实现一些代码,以便在隐藏和显示面板时提供更愉悦的用户体验。
创建单例类脚本
我们将首先编写一个 Singleton 类来使用(或者,如果你已经有了喜欢的,你也可以自由地使用那个 Singleton 类定义)。你可以在 Unity Asset Store 中找到一些作为包提供的单例实现,但我们只需要一个简短的脚本,你现在可以创建如下:
-
在你的
Scripts/文件夹中,通过右键点击并选择Singleton。 -
按照以下方式编写脚本:
using UnityEngine; /// Singleton behaviour class, used for components that should only have one instance /// </summary> /// <typeparam name="T"></typeparam> public class Singleton<T> : MonoBehaviour where T : Singleton<T> { public static T Instance { get; private set; } /// <summary> /// Returns whether the instance has been initialized or not. /// </summary> public static bool IsInitialized { get { return Instance != null; } } /// <summary> /// Base awake method that sets the singleton's unique instance. /// </summary> protected virtual void Awake() { if (Instance != null) Debug.LogError($"Trying to instantiate a second instance of singleton class {GetType().Name}"); else Instance = (T)this; } protected virtual void OnDestroy() { if (Instance == this) Instance = null; } } -
保存文件。
信息:单例作为反模式
注意,单例模式可能会被滥用,一些程序员坚决反对使用它,因为如果应用程序增长并变得更加复杂,它可能会在未来造成问题。但当你确定应用程序将始终只需要一个类的实例时,它将是一个强大的工具,就像在这个交互框架中一样。单例的主要优点之一是,你可以将对象实例作为对象类本身的静态变量来引用。另一种技术是在运行时找到组件的实例,例如,通过从脚本的
Start()函数调用FindObjectOfType<T>()。
此脚本可用于声明单例的 MonoBehaviour 类,正如我们接下来在 UIController 和其他脚本中将要看到的。
编写 UIController 脚本
拥有我们的 Singleton 类后,我们现在可以编写 UI 控制器。这个组件提供了一个在用户可见的 UI 面板之间切换的方法。按照以下步骤编写 UIController 类:
-
在你的
Scripts/文件夹中创建一个新的脚本,通过 右键单击并选择UIController。 -
双击 文件以打开它进行编辑,并替换默认内容,从以下声明开始:
using UnityEngine; using RotaryHeart.Lib.SerializableDictionary; [System.Serializable] public class UIPanelDictionary : SerializableDictionaryBase<string, CanvasGroup> { } public class UIController : Singleton<UIController> { [SerializeField] UIPanelDictionary uiPanels; CanvasGroup currentPanel;在顶部,我们使用
CanvasGroup组件声明了一个可序列化的字典,UIPanelDictionary。我们不是将
UIController声明为MonoBehaviour类,而是将其声明为Singleton(它本身继承自MonoBehaviour)。不用担心声明的语法,public class UIController : Singleton<UIController>。这正是我们的Singleton类所期望的。脚本声明了一个
uiPanels变量,作为一个UIPanelDictionary。我们还声明了一个currentPanel变量来跟踪当前哪个面板是活跃的。 -
接下来,将以下函数添加到脚本中,通过遍历
uiPanels列表并调用SetActive(false)来确保在应用程序启动时所有 UI 面板都被禁用:void Awake() { base.Awake(); ResetAllUI(); } void ResetAllUI() { foreach (CanvasGroup panel in uiPanels.Values) { panel.gameObject.SetActive(false); } } }注意,
Awake调用base.Awake(),因为父Singleton类也有一个Awake方法,必须调用它才能使这个方法工作。然后它调用ResetAllUI。 -
然后,将以下函数添加到脚本中:
public static void ShowUI(string name) { Instance?._ShowUI(name); } void _ShowUI(string name) { CanvasGroup panel; if (uiPanels.TryGetValue(name, out panel)) { ChangeUI(uiPanels[name]); } else { Debug.LogError("Undefined ui panel " + name); } } void ChangeUI(CanvasGroup panel) { if (panel == currentPanel) return; if (currentPanel) currentPanel.gameObject.SetActive(false); currentPanel = panel; if (panel) panel.gameObject.SetActive(true); }_ShowUI是一个实例函数,给定一个面板名称,调用ChangeUI。ChangeUI隐藏当前面板然后激活所需的那个(注意,我使用下划线前缀来区分私有实例函数和公共函数)。C# 字典的TryGetValue方法查找给定键的值。
静态的 ShowUI 类函数只是调用实例的 _ShowUI 函数。这样,另一个脚本可以通过调用 UIController.ShowUI(panelname); 来显示一个面板,而不需要直接引用实例。它使用空条件运算符 (docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-) 作为快捷方式,确保在引用它之前实例已经定义。
现在,将脚本作为组件添加到 UI Canvas 上,并按照以下步骤设置其属性:
-
在 Hierarchy 窗口中,选择 UI Canvas。
-
将
UIController脚本拖放到 UI Canvas 上,将其作为组件添加。 -
在 Inspector 窗口的 UI Controller 组件上,展开 UI Panels 字典列表。
-
点击 UI 面板列表右下角的 + 按钮。
-
在元素
Startup中。 -
展开元素,然后从 Hierarchy 窗口,将 Startup UI 游戏对象拖放到 Value 槽中。
-
对于以下每个选项重复步骤 4 – 6 三次:
NonAR:Scan:Main: 主 UI。
UI 控制器组件现在应如下所示:
![图 4.6 – 填充了 UI 面板引用的 UI 控制器组件]
![img/Figure_4.06-uipanels-insp.jpg]
图 4.6 – 填充了 UI 面板引用的 UI 控制器组件
到目前为止,我们已经为 AR 应用程序创建了一个简单的 UI,将其组织在一个画布上作为一组单独的面板。我们的计划是每次只向用户展示一个面板,具体取决于应用程序正在做什么。我们还编写了一个 UIController 脚本来处理面板之间的切换。
淡出 UI 面板
我们可以做出的改进是在转换时淡入和淡出 UI,而不是突然隐藏/显示面板。目前,我们通过调用 SetActive 来更改面板的可见性。相反,我们可以使用面板的 CanvasGroup 组件并动画化其 Alpha 值,而 DOTween 库对此非常方便。(如果您不想安装 DOTween,可以跳过此修改)。为此,请按照以下步骤操作:
-
打开
UIController脚本进行编辑,并在文件顶部添加以下声明:using DG.Tweening; -
在类底部添加以下两个淡入器辅助函数:
void FadeIn(CanvasGroup panel) { panel.gameObject.SetActive(true); panel.DOFade(1f, 0.5f); } void FadeOut(CanvasGroup panel) { panel.DOFade(0f, 0.5f).OnComplete(() => panel gameObject.SetActive(false)); } -
然后,修改
ChangeUI函数以调用淡入器而不是SetActive,如下所示(注释中的行已被替换):void ChangeUI(CanvasGroup panel) { if (panel == currentPanel) return; if (currentPanel) FadeOut(currentPanel); //currentPanel.gameObject.SetActive(false); currentPanel = panel; if (panel) FadeIn(panel); //panel.gameObject.SetActive(true); }
最终,当你运行场景时,UI 面板在显示和隐藏时将淡入和淡出。
接下来,我们将编写一个交互控制器,它处理应用程序的交互模式,并使用 UI 控制器显示它需要的特定 UI。
创建交互控制器模式
对于我们的用户框架,我们将巧妙地使用带有模式脚本的 GameObject 来表示交互模式。模式将通过启用(和禁用)相应的对象来启用(和禁用)。我们将按照上一节中创建的 UI 面板的方式组织这些对象,但将其分开以保持“控制器”与“视图”分离,正如控制器/视图软件模式所规定。目前,我们将包括以下模式:
-
启动模式:在 AR 会话初始化期间处于活动状态,然后它启动扫描模式。
-
非 AR 模式:如果您希望应用程序即使在设备不支持 AR 的情况下也能运行,则应使用占位符。
-
扫描模式:此模式提示用户扫描可追踪特征,直到 AR 会话准备就绪,然后它启动主模式。
-
主模式:显示主菜单并处理非模态交互。
首先,我们将在交互控制器游戏对象下创建表示每个模式的对象层次结构。通过使用代表每个模式的单独 GameObject,我们将能够分别启用或禁用某个模式。
创建交互模式层次结构
要创建交互模式层次结构,请执行以下步骤:
-
从主菜单中选择
Interaction Controller。 -
右键单击
Startup Mode。 -
重复 步骤 2 三次以创建名为
NonAR Mode、Scan Mode和Main Mode的对象。
模式层次结构游戏对象现在看起来如下所示:

图 4.7 – 交互控制器模式层次结构
现在我们可以编写和设置 InteractionController 脚本。
编写交互控制器
我们交互控制器的角色是管理应用程序的最高级用户交互。我们将从以下脚本开始编写:
-
通过在
InteractionController上 右键单击 在Scripts/文件夹中创建一个新的脚本。 -
双击 文件以打开它进行编辑,并用以下声明替换默认内容:
using System.Collections; using UnityEngine; using RotaryHeart.Lib.SerializableDictionary; [System.Serializable] public class InteractionModeDictionary : SerializableDictionaryBase<string, GameObject> { } public class InteractionController : Singleton<InteractionController> { [SerializeField] InteractionModeDictionary interactionModes; GameObject currentMode; }在顶部,我们声明一个可序列化的字典,
InteractionModeDictionary,使用InteractionController作为MonoBehaviour类,我们将其声明为Singleton(它本身继承自MonoBehaviour)。然后我们声明
interactionModes变量为这种类型的字典。我们还声明一个currentMode变量,用于跟踪当前启用的模式。 -
接下来,向脚本中添加以下函数,通过遍历
interactionModes列表并调用SetActive(false)来确保在应用启动时所有模式都被禁用:protected override void Awake() { base.Awake(); ResetAllModes(); } void ResetAllModes() { foreach (GameObject mode in interactionModes Values) { mode.SetActive(false); } }注意,
Awake调用base.Awake(),因为父Singleton类也有一个Awake,必须调用它才能使此操作生效。然后调用ResetAllModes。 -
然后,向脚本中添加以下函数:
public static void EnableMode(string name) { Instance?._EnableMode(name); } void _EnableMode(string name) { GameObject modeObject; if (interactionModes.TryGetValue(name, out modeObject)) { StartCoroutine(ChangeMode(modeObject)); } else { Debug.LogError("undefined mode named " + name); } } IEnumerator ChangeMode(GameObject mode) { if (mode == currentMode) yield break; if (currentMode) { currentMode.SetActive(false); yield return null; } currentMode = mode; mode.SetActive(true); }_EnableMode是一个实例函数,它接受一个模式名称,然后调用ChangeMode。ChangeMode禁用当前模式,然后激活请求的模式。注意,
ChangeMode被调用时,只需简单地调用EnableMode类函数就调用实例的_EnableMode函数。这样,另一个脚本可以通过调用InteractionController.EnableMode(modename);来显示面板,而不需要直接引用实例。它使用空条件运算符 (docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-) 作为快捷方式,以确保在引用实例之前实例已定义。 -
最后,假设我们希望应用以
Startup模式启动,请添加以下内容:void Start() { _EnableMode("Startup"); }这假设我们将包括一个
"Startup"模式在interactionModes字典中。
UIController 将包含对应用程序每个模式游戏对象的引用。当应用需要切换模式时,它将使用模式的名称调用 InteractionController.EnableMode(modeName)。当前模式将被禁用,所需模式将被启用。
将脚本作为组件添加到交互控制器游戏对象上,并按照以下步骤设置其属性:
-
在层次结构窗口中,选择交互控制器游戏对象。
-
将
InteractionController脚本拖放到交互控制器上,添加它作为组件。 -
在检查器窗口中,在交互控制器组件上,展开交互模式字典列表。
-
点击交互模式列表右下角的+按钮。
-
在元素
Startup上。 -
展开元素,然后从层次结构窗口中,将启动模式游戏对象拖放到值槽中。
-
对于以下每个项目重复步骤 4 – 6 三次:
NonAR:Scan:Main: 主模式。交互控制器组件现在应该看起来如下:
![图 4.8 – 填充了交互模式对象引用的交互控制器组件]
图 4.8 – 填充了交互模式对象引用的交互控制器组件
-
交互控制器组件将响应用户输入,因此我们需要添加一个玩家输入组件(假设你的项目正在使用新的输入系统)。
在层次结构窗口中选择交互控制器,然后在检查器窗口中点击添加组件。
-
搜索
player inp ut并添加一个玩家输入组件。 -
定位到
Inputs/文件夹并将其拖放到玩家输入 | 动作槽中。(如本章前面在技术要求中所述,我假设你已经根据第二章,你的第一个 AR 场景创建了此资产)。 -
将玩家输入 | 行为设置为广播消息。
这很重要!我们需要确保玩家动作被转发到子模式对象。
在本节中,我们为交互模式创建了一个层次结构,组织在一个具有启用/禁用模式对象的脚本的交互控制器游戏对象下。我们的计划是允许一次只有一个模式处于活动状态。当然,我们仍然需要编写控制每个模式的脚本,并处理从特定模式过渡到不同模式的情况。
创建交互模式行为
当应用启用一个模式时,它将启用相应的游戏对象,该对象有一个脚本控制该模式的行为。当应用更改模式时,当前模式对象将被禁用,新的对象将被启用。每个模式负责以下内容:
-
显示其对应的用户界面
-
当满足特定条件时切换到不同的模式
我们将为每个模式编写模式脚本。
启动模式脚本
启动模式在应用程序启动时开始(它由InteractionController的Start()函数启用)。它显示启动 UI 面板。然后它等待ARSession状态变为就绪,并过渡到扫描模式。或者,如果ARSession报告当前设备不支持 AR,则过渡到非 AR 模式。
按照以下步骤创建启动模式:
-
在您的
Scripts/文件夹中通过右键单击并选择StartupMode创建一个新的脚本。 -
将
StartupMode脚本拖动到启动模式游戏对象在层次结构窗口上。 -
双击
StartupMode脚本文件以打开它进行编辑,并按照以下内容编写:using UnityEngine; using UnityEngine.XR.ARFoundation; public class StartupMode : MonoBehaviour { [SerializeField] string nextMode = "Scan"; void OnEnable() { UIController.ShowUI("Startup"); } void Update() { if (ARSession.state == ARSessionState.Unsupported) { InteractionController.EnableMode("NonAR"); } else if (ARSession.state >= ARSessionState.Ready) { InteractionController.EnableMode(nextMode); } } }
该脚本使用 AR Foundation 的ARSession类状态变量ARSession.state来确定会话何时初始化或 AR 是否不受支持。状态是一个enum ARSessionState,具有以下值之一:
-
None:会话尚未初始化。 -
Unsupported:设备不支持 AR。 -
CheckingAvailability:会话正在检查可用性。 -
NeedsInstall:设备需要安装或更新 AR 支持软件。 -
Installing:设备正在安装 AR 支持软件。 -
Ready:设备支持 AR,您可以启用ARSession组件。 -
SessionInitializing:AR 会话正在扫描环境并试图检测可追踪对象。 -
SessionTracking:AR 会话已找到可追踪对象,并可以确定设备在现实世界 3D 环境中的位置。
当state为Unsupported时,我们过渡到非 AR 模式。
当state为Ready(或更高)时,我们过渡到扫描模式。
扫描模式脚本
当设备正在扫描环境,试图在现实世界中检测可追踪特征时,扫描模式被启用。它显示一个提示,要求用户将相机指向房间并缓慢移动设备。
扫描模式结束的条件可能因 AR 应用程序而异。例如,它可能等待至少检测到一个水平或垂直平面,或识别到一个参考图像,或正在追踪自拍人脸。目前,我们将检查ARPlaneManager以确定是否检测到任何可追踪对象。
执行以下步骤以创建扫描模式:
-
在您的
Scripts/文件夹中通过右键单击并选择ScanMode创建一个新的脚本。 -
将
ScanMode脚本拖动到层次结构窗口中的扫描模式游戏对象上。 -
双击
ScanMode脚本文件以打开它进行编辑,并按照以下内容编写:using UnityEngine; using UnityEngine.XR.ARFoundation; public class ScanMode : MonoBehaviour { [SerializeField] ARPlaneManager planeManager; void OnEnable() { UIController.ShowUI("Scan"); } void Update() { if (planeManager.trackables.count > 0) { InteractionController.EnableMode("Main"); } } } -
将AR 会话原点对象从层次结构窗口拖动到扫描模式 | 平面管理器槽位。
当扫描模式启用时,在切换到主模式之前,planeManager.trackables.count > 0。
主模式脚本
主模式,正如其名称所暗示的,是应用程序的主要操作模式。它可能显示主菜单,例如,并处理主要用户交互。对于我们的默认框架,目前除了显示主 UI 面板外,没有太多事情要做。
执行以下步骤以创建主模式:
-
在你的项目
Scripts/文件夹中通过右键单击并选择MainMode来创建一个新的脚本。 -
将
MainMode脚本拖放到主模式游戏对象上,位于层次结构窗口中。 -
双击
MainMode脚本文件以打开它进行编辑,并按照以下内容编写:using UnityEngine; public class MainMode : MonoBehaviour { void OnEnable() { UIController.ShowUI("Main"); } }
最后,我们定义非 AR 模式。
NonARMode 脚本
当你运行的设备不支持 AR 时,将启用非 AR 模式。你可以简单地通知用户应用无法运行,并优雅地退出。或者,如果你的项目需要,你也可以在没有 AR 功能的情况下继续运行应用。
执行以下步骤以创建非 AR 模式占位符:
-
在你的项目
Scripts/文件夹中通过右键单击并选择NonARMode来创建一个新的脚本。 -
将
NonARMode脚本拖放到非 AR 模式游戏对象上,位于层次结构窗口中。 -
双击
NonARMode脚本文件以打开它进行编辑,并按照以下内容编写:using UnityEngine; public class NonARMode: MonoBehaviour { void OnEnable() { UIController.ShowUI("NonAR"); } }
大概就是这样。我们已经创建了一个层次结构,其中每个交互模式都是InteractionController.EnableMode()的子项,它禁用当前模式并激活一个新的模式。当一个模式被启用时,其模式脚本开始运行,显示其 UI,并可能与用户交互,直到满足特定条件,然后过渡到不同的模式。让我们尝试在你的设备上运行场景。
测试一下
现在是构建场景并确保一切按预期进行的好时机。执行以下步骤:
-
首先,务必使用文件 | 保存保存你的工作。
-
选择文件 | 构建设置以打开构建设置窗口。
-
点击
ARFramework场景到构建场景中,并确保它是列表中唯一带有勾选标记的场景。 -
确保你的目标设备已连接到 USB 端口,并且已准备好。
-
点击构建并运行以构建项目。
一旦项目无错误构建并在你的设备上以启动模式启动。你将首先看到来自启动UI 面板的初始化…文字。
一旦 AR 会话开始,应用将切换到扫描模式,你将看到文字扫描...请缓慢移动设备。
一旦水平平面开始跟踪,扫描模式将过渡到主模式。你将在屏幕上看到主模式运行...的文字。
如果一切顺利,该框架将按预期工作。为了实现这一点,我们已实现了用于用户界面的 Canvas UI 和子面板。我们使用脚本实现了每个模式所需的 UI 和交互,并实现了交互控制器和子模式控制器。所有这些都已经连接在一起。这是一个基本的 AR 项目框架,我们将用它来完成本书中的项目。
我们可以通过多种方式改进和构建这个框架。一方面,我们可以通过用 Unity 的 AR Onboarding UX 中的动画图形替换一些文本提示来使 UI 更有趣。
使用 Unity 入门 UX 资产
Unity 提供了一套 AR 入门 UX 资产,用于在 AR 应用中提示用户。入门指的是当您的应用启动并提示用户与 AR 功能交互时的用户体验。首先,我将解释这个包提供的一些内容。然后我们将准备用于我们自己的项目的资产。
介绍入门资产
入门 UX 资产是位于github.com/Unity-Technologies/arfoundation-demos的 AR Foundation Demos 项目的组成部分。(这与我们在第二章中探索的AR Foundation Samples项目不同)。其文档可以在该项目的 GitHub 页面上找到。
入门 UX 资产包括图标和视频图形,用于在扫描时提示用户。它会自动告诉用户跟踪可能失败的原因,例如房间太暗,或者摄像头视图没有看到足够的细节。它提供了管理此过程的组件,这些组件组合成一个名为ScreenspaceUI的示例预制件,可以根据您自己的项目进行定制。
例如,当应用正在扫描时,您可以使用动画图形提示在扫描房间时缓慢移动设备。如果有问题,它将显示原因,如以下图像的左侧面板所示(我在那里用手指遮住了摄像头镜头)。它说在该区域寻找更多纹理或细节。如果您想提示用户点击屏幕放置对象,有一个点击放置的动画图形,等等:

图 4.9 – 使用入门 UX 资产
此外,该包支持文本提示的本地化,如果您的项目需要支持多个国家的多语言,则可以使用。它还包括一些用于可视化 AR 平面和点云的默认资产,您可以使用。
该包包括以下组件。
-
ARUX 动画管理器:它显示指导图形动画,提示用户找到平面或点击放置,例如。
-
ARUX Reasons Manager:这个工具检查 AR 会话的状态,并显示可能导致跟踪失败的原因,作为对用户的提示。
-
Localization Manager:这个工具支持本地化文本和图形,以便将指导性和原因 UI 调整为不同的语言。
-
来自 AR Foundation Demos 项目的
UIManager脚本是一个有用的控制脚本,但它只是如何与ARUXAnimationManager接口的一个示例。阅读脚本是有信息的,但不可重用。在我们的框架中,我们已经为用户流程实现了自己的解决方案,以替换UIManager脚本。
UI Manager 允许您通过 Inspector 窗口设置一个或两个目标。目标可以是 Found a Plane 或 Placed an Object。然后您设置指导性 UI,提示用户执行当前活动,直到目标完成。
准备 Unity AR 入门资产
虽然 UX 入门资产也可以在 Unity Asset Store 中作为包提供,但我建议您克隆 GitHub 项目版本,因为它有更多的示例和资产,包括 Universal Render Pipeline (URP)着色器图。两个版本都是完整的 Unity 项目,所以无论哪种方式,您都需要在一个新的 Unity 项目中打开它,然后将资产导出为包,以便您可以将其导入到自己的项目中。
我们将克隆项目,然后将 AR Foundation Demos 资产导出为 .unitypackage 文件,我们可以将其导入到自己的项目中。我还会在 GitHub 仓库中提供包含此书文件的 Unity 包的副本。
要克隆项目并导出我们想要的文件夹,请执行以下步骤:
-
从 GitHub 克隆项目的副本到您的本地计算机。项目可以在
github.com/Unity-Technologies/arfoundation-demos找到。请使用您喜欢的任何克隆方法,例如 GitHub Desktop (desktop.github.com/) 或命令行 (git-scm.com/download/)。 -
在您的桌面上打开 Unity Hub 应用程序。
-
通过选择 Projects | Add,导航到克隆项目的根文件夹,然后按 Select Folder,将项目添加到 Unity Hub。
-
在 Unity Hub 的项目列表中,如果您看到一个表示克隆项目使用的 Unity 版本当前未安装在系统上的黄色警告图标,请使用 Unity Version 选择来选择您已安装的较新版本的编辑器(最好是相同的主版本号)。
-
通过从 Unity Hub 项目列表中选择它来打开项目。
-
我们将把选定的文件夹移动到一个名为
ARFoundationDemos的根文件夹中,这样我们就可以将其导出为包。在 Unity 中,在
ARFoundationDemos。 -
使用鼠标,将以下四个文件夹移动到这个
ARFoundationDemos/文件夹中:AddressableAssetsData、Common、Shaders 和 UX。 -
在
ARFoundationDemos/文件夹中,选择导出包。 -
导出包窗口将打开。点击导出。
-
选择此项目根目录之外的一个目录,并为文件命名(例如
ARF-OnboardingUX)。然后,点击保存。
在关闭ARFoundationDemos项目之前,您可能想查看包管理器窗口并注意给定项目中使用的AR Foundation包版本,以确保您的项目使用相同或更高版本的 AR Foundation。
您现在可以关闭ARFoundationDemos项目。您现在有一个可以在本项目和其他项目中使用的资源包。
安装依赖包
AR 导入 UX 对其他 Unity 包有依赖,您必须在您的项目中安装这些包:Addressables和Localization。现在打开您的 AR 项目并安装它们。
可寻址资源系统通过统一的方案简化了运行时加载资源。资源可以从任何具有唯一地址的位置加载,无论它们位于您的应用程序中还是在内容分发网络上。资源可以通过直接引用、传统资源包或Resource文件夹访问。Addressables包是导入 UX 资源所必需的。要了解更多信息,请参阅docs.unity3d.com/Packages/com.unity.addressables@1.16/manual/index.html。
要导入Addressables包,请执行以下步骤:
-
通过使用窗口 | 包管理器打开包管理器窗口。
-
确保从包管理器窗口左上角的包过滤器下拉菜单中选择Unity 注册表。
-
使用包管理器窗口右上角的搜索文本输入字段搜索
Addressables。 -
选择Addressables包并点击安装。
Addressables包现在已安装。
Localization包将文本字符串和其他资源翻译成本地语言。请参阅docs.unity3d.com/Packages/com.unity.localization@1.0/manual/index.html。要导入Localization包,请执行以下步骤(这些步骤在您阅读时可能已更改):
-
如果您尚未这样做,请通过导航到编辑 | 项目设置 | 包管理器设置并勾选启用预览包复选框来启用预览包。
-
然后,在包管理器窗口中,使用左上角的+按钮并选择从 Git URL 添加包。
-
然后,输入
com.unity.localization以开始安装包。信息:使用预览包和 Git URL
当我写这篇文章时,本地化包处于预览状态,也就是说,尚未由 Unity 完全发布。此外,它尚未包含在 Unity 包注册表中。要启用预览包,您必须在项目设置中点击启用预览包。另外,如果一个包不包含在内置的 Unity 注册表中,您可以从 Git URL、磁盘或 tarball 文件中添加一个包。
本地化包现在已安装。我们现在可以安装 AR 引导 UX 资产本身。
导入 OnboardingUX 包
我们将 AR Foundation Demos 项目导出的资产保存到一个名为OnboardingUX.unitypackage的文件中。导入包的过程很简单。按照以下步骤将其添加到您的项目中。回到您的 Unity 项目中,执行以下操作:
-
从您的资源管理器或 Finder 直接将
OnboardingUX.unitypackage文件选择到 Unity 的项目窗口中。 -
在导入 Unity 包窗口中,点击导入。
-
资产包括使用内置渲染管道的材料。由于我们的项目正在使用 URP,您需要通过选择
ARFoundationDemos/Common/Materials/文件夹,并在检查器窗口中,使用下拉菜单将着色器更改为ShaderGraphs/BlurredShadowPlane来转换这些材料。
引导 UX 资产现在已导入到您的项目中。我们现在可以将其添加到我们的框架场景中。
目前,我们的应用程序渲染一个带有文本的 UI 面板,提示用户扫描环境。该面板是一个在需要时启用的游戏对象。基本上,我们想要用动画图形替换面板文本。
编写 AnimatedPrompt 脚本
让我们从编写一个新的脚本AnimatedPrompt开始,该脚本在启用时显示特定的动画,在禁用时隐藏动画:
-
在您的
Scripts/文件夹中创建一个新的脚本,通过右键单击并选择AnimatedPrompt。 -
双击文件以打开它进行编辑,并替换默认内容,从以下声明开始:
using UnityEngine; public class AnimatedPrompt : MonoBehaviour { public enum InstructionUI { CrossPlatformFindAPlane, FindAFace, FindABody, FindAnImage, FindAnObject, ARKitCoachingOverlay, TapToPlace, None }; [SerializeField] InstructionUI instruction; [SerializeField] ARUXAnimationManager animationManager; bool isStarted;在此脚本中,我们声明一个公共属性
instruction,其值是一个enum InstructionUI类型,指示要播放哪个动画(从引导资产中的UIManager脚本借用,以保持一致性)。 -
当脚本启动或启用时,它将启动动画图形。相反,当对象禁用时,图形将关闭:
void Start() { ShowInstructions(); isStarted = true; } void OnEnable() { if (isStarted) ShowInstructions(); } void OnDisable() { animationManager.FadeOffCurrentUI(); }我已经添加了一个修复,以确保当在开始时同时调用
Start和OnEnable时,动画不会重新启动。 -
当脚本启用时,它会调用辅助函数
ShowInstructions,该函数会调用ARUXAnimationManager中的相应函数:void ShowInstructions() { switch (instruction) { case InstructionUI.CrossPlatformFindAPlane: animationManager. ShowCrossPlatformFindAPlane(); break; case InstructionUI.FindAFace: animationManager.ShowFindFace(); break; case InstructionUI.FindABody: animationManager.ShowFindBody(); break; case InstructionUI.FindAnImage: animationManager.ShowFindImage(); break; case InstructionUI.FindAnObject: animationManager.ShowFindObject(); break; case InstructionUI.TapToPlace: animationManager.ShowTapToPlace(); break; default: Debug.LogError("instruction switch missing, please edit AnimatedPrompt.cs " + instruction); break; } } }}
现在我们可以将其添加到场景中。
集成引导图形
要集成引导图形,我们可以从 AR Foundation Demos 包中添加演示预制件(不幸的是命名为ScreenspaceUI)。按照以下步骤操作:
-
在
ARFoundationDemos/UX/Prefabs/文件夹中,将 ScreenspaceUI 预制拖放到场景的 Hierarchy 窗口根目录。 -
给它一个更具指示性的名称;重命名对象
OnboardingUX。 -
我们的框架替换了演示中的 UI Manager 组件,因此你应该将其移除。
在 Hierarchy 中选择 OnboardingUX 对象,点击 Inspector 窗口右上角的 3-dot context menu 并选择 Remove Component。
我们现在可以使用 AnimatedPrompt 来替换我们的 UI 提示面板中的文本。要使用它,请执行以下步骤:
-
在
Animated Prompt。 -
将 Project 窗口中的
AnimatedPrompt脚本拖放到对象上。 -
将 Animated Prompt | Instruction 设置为 Cross-Platform Find A Plane。
-
从 Hierarchy 窗口,将 OnboardingUX 对象拖放到 Inspector 窗口的 Animation Manager 插槽中。
-
你可以禁用 Scan Prompt Panel 的 Text (TMP) 子元素,这样它就不会被渲染。
如果你再次 Build And Run 项目,当它进入扫描模式时,你将看到漂亮的动画图形而不是文本提示。
在拥有一个工作的 AR 用户框架后,让我们将这个场景制作成一个模板,以便在创建新场景时使用。
为新场景创建场景模板
我们可以将我们一直在工作的这个 ARFramework 场景保存为模板,用于在此 Unity 项目中创建新场景。要创建场景模板,请执行以下步骤。
-
在 ARFramework 场景打开时,选择 File | Save As Scene Template。
-
在
Scenes/文件夹中,验证模板名称(ARFramework.scenetemplate),然后按 Save。 -
随后,当你想要开始一个新的 AR 场景时,请使用此模板。默认情况下,Unity 会将场景中的任何依赖项复制到一个单独的文件夹中。在我们的例子中,这通常不是我们想要做的。
为了防止在模板中使用时克隆场景依赖项,点击你的
Assets/窗口中的这个新场景模板文件。 -
在其
ARFramework。只需记住,如果模板中添加了任何新资产,请检查模板中的 Dependencies 列表,因为这些将默认被克隆。
要在此项目中创建新场景时使用模板,像往常一样使用 File | New Scene。现在对话框将包含 ARFramework 模板作为选项。选择你的资产文件夹中的位置并按 Create。如果模板指定了任何要复制的资产,这些副本将被添加到与新场景同名的一个子文件夹中。
我们现在可以在此基础上构建本章所做的工作,使用 ARFramework 模板为新项目场景。
摘要
在本章中,我们开发了一个用于构建 AR 应用的框架,并将其保存为一个模板,我们可以用它来构建本书中的项目。该框架提供了一个状态机结构,用于实现模式并识别何时过渡到不同的模式。该框架还提供了一个控制器-视图设计模式,其中当某个模式处于活动状态时,其对应的 UI 界面可见,从而将模式控制对象与 UI 视图对象分离。
对于框架模板,我们实现了四种模式:启动模式、扫描模式、主模式和 NonAR 模式,以及四个 UI 面板:启动 UI、扫描 UI、主 UI 和非 AR UI。扫描模式使用来自 AR Foundation Demos 项目的 onboarding UX 资源,提示用户扫描可追踪特征,并报告检测和 AR 会话中的问题。
在下一章中,我将通过一个简单的演示项目展示这个框架的使用方法,并在随后的章节中更广泛地构建这个框架。
第五章:第五章:使用 AR 用户框架
在本章中,我们将学习如何使用ARFramework场景模板,我们将添加一个主菜单来在环境中放置虚拟对象。如果您跳过了那一章或只是浏览了一下,您可以在本书 GitHub 仓库提供的文件中找到场景模板和资产。
对于这个项目,我们将通过一个新的PlaceObject 模式扩展框架,提示用户轻触放置虚拟对象在房间中。用户可以从主菜单中选择对象。
在本章的后半部分,我将讨论一些高级 AR 应用程序问题,包括创建一个可选的 AR 项目、确定设备是否支持特定的 AR 功能以及向您的用户界面(UI)添加本地化。
本章将涵盖以下主题:
-
规划项目
-
从 ARFramework 场景模板开始
-
添加主菜单
-
添加 PlaceObject 模式和教学 UI
-
连接菜单按钮
-
进行构建和运行
-
在不需要时隐藏跟踪对象
-
创建一个可选的 AR 项目
-
在运行时确定设备是否支持特定的 AR 功能
-
向项目中添加本地化功能
到本章结束时,您将更熟悉为本书开发的 AR 用户框架,我们将在构建各种不同的 AR 应用程序项目时使用它。
技术要求
要在本章中实现项目,您需要在您的开发计算机上安装 Unity,并连接一个支持 AR 应用程序的移动设备(有关说明,请参阅第一章,为 AR 开发设置),包括以下内容:
-
通用渲染管线
-
输入系统包
-
目标设备的 XR 插件
-
AR Foundation 包
我们假设您已经安装了第二章**,您的第一个 AR 场景中创建的 Unity ARF-samples.unitypackage资产。
还可以从第二章,您的第一个 AR 场景中找到,我们创建了一个AR 输入动作资产,我们将在本项目中使用它,包含一个名为ARTouchActions的动作映射,包括(至少)一个PlaceObject动作。
我们还假设您已经安装了第四章**,创建 AR 用户框架中创建的ARF-samples.unitypackage资产,包括在第四章**创建 AR 用户框架开始部分详细说明的所有先决 Unity 包。模板和资产可以在本书的 GitHub 仓库中找到,网址为github.com/PacktPublishing/Augmented-Reality-with-Unity-AR-Foundation(不包括您应自行安装的第三方包)。
AR 用户框架需要以下先决条件,如第四章**,创建 AR 用户框架中详细说明,包括以下内容:
-
Addressables 包
-
本地化包
-
TextMesh Pro
-
Asset Store 中的 DOTween 包
-
Asset Store 中的 Serialized Dictionary Lite 包
本章完成的场景也可以在 GitHub 仓库中找到。
项目规划
对于这个项目,我们将从ARFramework场景模板开始创建一个简单的演示 AR 场景,并构建我们已设置的用户框架结构。
使用该框架,当应用首次启动时,启动模式被启用,AR 会话被初始化。一旦会话准备就绪,它将过渡到扫描模式。
如果 AR 会话确定当前设备不支持 AR,扫描模式将过渡到非 AR 模式。目前这只是在屏幕上显示一条文本消息。有关更多信息,请参阅本章末尾附近的创建可选 AR 项目部分。
在扫描模式中,用户被提示使用他们的设备相机缓慢扫描房间,直到检测到 AR 功能,即水平平面。ScanMode脚本检查任何追踪的平面,然后过渡到主模式。
因此,我们的计划是添加以下功能:
-
AR 会话将被配置为检测和跟踪水平平面。我们还将渲染点云。
-
主模式将显示一个主菜单,其中包含按钮,允许用户选择放置在真实世界环境中的对象。您可以在此处找到自己的模型来使用,但我们将包括三个按钮用于立方体、球体和病毒(在第二章**,您的第一个 AR 场景中创建)。
-
当选择放置对象按钮时,它将启用一个新的放置对象模式,提示用户点击以将对象放置在检测到的平面上。
-
点击追踪的水平平面将在场景中创建对象的实例。然后应用返回到主模式。
-
追踪的 AR 功能(平面和点云)将在主模式中隐藏,在放置对象模式中可见。
我选择提供立方体、球体和病毒(病毒模型在第二章**,您的第一个 AR 场景中创建)。您也可以自由地寻找并使用自己的模型。我将使用的预制资源如下:
-
Assets/ARF-samples/Prefabs/文件夹) -
Assets/ARF-samples/Prefabs/文件夹) -
Assets/_ARFBookAssets/Chapter02/Prefabs/文件夹)
这是一个简单的 AR 演示,将帮助您更熟悉我们开发的 AR 用户框架,并将在本书后续项目中使用。
让我们开始吧。
从 ARFramework 场景模板开始
首先,我们将使用以下步骤创建一个名为FrameworkDemo的新场景,使用ARFramework场景模板:
-
选择文件 | 新建场景。
-
在新场景对话框中,选择ARFramework模板。
-
按创建。
-
在你的项目“资产”文件夹中,选择
Scenes/文件夹,将其命名为FrameworkDemo,然后按保存。注意:意外的克隆依赖项
当从场景模板创建新场景时,如果你立即被提示输入文件保存的名称,这表明你的场景模板定义了一些克隆依赖项。如果不是你的意图,取消创建,在项目窗口中选择模板资产,并确保在依赖项列表中所有克隆复选框都被清除。然后再次尝试创建你的新场景。
新的 AR 场景已经从模板中包含了以下游戏对象:
-
AR 会话游戏对象
-
带有射线管理器和平面管理器组件的AR 会话起源装置。
-
UI Canvas是一个屏幕空间画布,包含启动 UI、扫描 UI、主 UI 和非 AR UI 子面板。它还具有我们编写的 UI 控制器组件脚本。
-
交互控制器是一个带有我们编写的交互控制器组件脚本的游戏对象,它帮助应用在启动、扫描、主和非 AR 模式之间切换。它还配置了一个带有我们之前创建的AR 输入动作资产的玩家输入组件。
-
来自 AR 基础演示项目的OnboardingUX预制件,它提供 AR 会话状态和功能检测状态消息,以及动画引导图形提示。
现在按照以下方式设置应用标题:
-
在层次结构窗口中,展开UI Canvas对象,然后展开其子应用标题面板。
-
选择标题文本对象。
-
在其
放置对象演示中。
默认的 AR 会话起源已经有一个 AR 平面管理器组件。让我们确保它只检测水平平面。让我们也添加点云可视化。按照以下步骤操作:
-
在层次结构窗口中,选择AR 会话起源对象。
-
在检查器中,通过首先选择无(以清除列表)然后选择水平,将AR 平面管理器 | 检测模式设置为水平。
-
点击
ar point cloud,然后添加一个AR 点云管理器组件。 -
找到一个点云可视化预制件,并将其设置为
Assets/ARF-samples/Prefabs/文件夹。 -
使用文件 | 保存保存你的工作。
我们已经基于 ARFramework 模板创建了一个新场景,并添加了 AR 轨迹管理器以处理点云和水平平面。接下来,我们将添加主菜单。
添加主菜单
主菜单 UI 位于场景层次结构中的菜单 UI 面板(在 UI Canvas 下)。我们将添加一个带有三个按钮的菜单面板,让你添加一个立方体、一个球体和一个病毒。我们将创建一个菜单子面板并将菜单按钮水平排列。按照以下步骤操作:
-
在层次结构中,展开UI Canvas,然后展开其子主 UI对象。
-
首先,删除临时的 主 模式文本元素。右键单击 子 文本 对象并选择 删除。
-
右键单击
主菜单。 -
在
175。 -
我将我的背景设置为
255。 -
选择
布局,然后选择 | 水平布局组。 -
在 水平布局组 组件中勾选 控制子大小 | 宽度 和 高度 复选框(保留其他默认值,使用子缩放 未勾选,子强制扩展 勾选)。在检查器中,主菜单面板看起来如下:

图 5.1 – 主菜单面板设置
现在我们将按照以下步骤添加三个按钮到菜单中:
-
右键单击
立方体按钮。 -
选择其子文本对象,并将
Cube和48设置为48。 -
右键单击
球体按钮并将其文本更改为Sphere。 -
再次重复 步骤 3,将其重命名为
Virus Button,并更改文本为Virus。
主菜单的最终场景层次结构如下截图所示:

图 5.2 – 主菜单层次结构
我决定更进一步,为每个模型添加按钮的精灵图像。我通过截取每个模型的视图来创建图像,使用 Photoshop 编辑它们,将它们保存为 PNG 文件,并在 Unity 中确保图像的 纹理类型 设置为 精灵(2D 和 UI)。然后我向按钮添加了一个子 图像 元素。以下是我的菜单的图像:
![]()
图 5.3 – 带图标按钮的主菜单
到目前为止,我们已经在主 UI 下创建了一个带有菜单按钮的主菜单面板。当应用进入主模式时,此菜单将显示。
接下来,我们将添加一个 UI 面板,提示用户触摸屏幕将对象放置到场景中。
添加带有说明 UI 的 PlaceObject 模式
当用户从主菜单中选择对象时,应用将启用 PlaceObject 模式。为此模式,我们需要一个 UI 面板来提示用户触摸屏幕放置对象。让我们首先创建 UI 面板。
创建 PlaceObject UI 面板
PlaceObject UI 面板应类似于 扫描 UI 面板,因此我们可以使用以下步骤进行复制和修改:
-
在 层次结构 窗口中,展开 UI 画布。
-
右键单击
PlaceObject UI。 -
展开 PlaceObject UI 并选择其子 动画提示。
-
在 检查器 中,将 动画提示 | 说明 设置为 点击放置。以下截图显示了生成的组件:
![图 5.4 – PlaceObject UI 面板的动画提示设置]()
图 5.4 – PlaceObject UI 面板的动画提示设置
-
现在我们将面板添加到 UI 控制器中。
在 层次结构 中选择 UI 画布 对象。
-
在检查器中,在UI Controller组件的右下角,点击+按钮向 UI 面板字典添加一个项。
-
在Id字段中输入
PlaceObject作为文本。 -
将PlaceObject UI游戏对象从层次结构拖到值槽中。UI 控制器组件现在看起来如下所示:
![Figure 5.5 – UI Controller's UI Panels list with PlaceObject added
![img/Figure_5.05-uicontroller-insp.jpg]
图 5.5 – 添加了 PlaceObject 的 UI 控制器 UI 面板列表
我们为PlaceObject UI 添加了一个指导性用户提示。当用户选择将对象添加到场景中时,此面板将显示。接下来,我们将添加PlaceObject模式和脚本。
创建 PlaceObject 模式
要将模式添加到框架中,我们在Interaction Controller下创建一个子 GameObject 并编写一个模式脚本。模式脚本将显示模式的 UI,处理任何用户交互,并在完成后过渡到另一个模式。对于 PlaceObject 模式,它将显示PlaceObject UI面板,等待用户触摸屏幕,实例化预制对象,然后返回到主模式。
让我们按照以下方式编写PlaceObjectMode脚本:
-
首先,在您的
Scripts/文件夹中创建一个新的脚本,使用右键点击PlaceObjectMode。 -
双击文件以打开它进行编辑,并替换默认内容,从以下声明开始:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.XR.ARFoundation; using UnityEngine.XR.ARSubsystems; public class PlaceObjectMode : MonoBehaviour { [SerializeField] ARRaycastManager raycaster; GameObject placedPrefab; List<ARRaycastHit> hits = new List<ARRaycastHit>();该脚本将使用
ARFoundation和ARSubsystems的 API,所以我们指定这些在脚本顶部的using语句中。它将使用ARRaycastManager来确定用户触摸了哪个跟踪平面。然后它将实例化placedPrefab到场景中。 -
当模式启用时,我们将显示PlaceObject UI面板:
void OnEnable() { UIController.ShowUI("PlaceObject"); } -
当用户从主菜单中选择一个对象时,我们需要根据以下代码告诉
PlaceObjectMode实例化哪个预制体:public void SetPlacedPrefab(GameObject prefab) { placedPrefab = prefab; } -
然后当用户触摸屏幕时,输入系统触发一个
OnPlaceObject事件(将touchPosition传递给PlaceObject函数,该函数执行Raycast以找到跟踪的水平面。如果找到,我们在hitPose位置和方向实例化placedPrefab。然后应用程序返回到主模式)。 -
保存脚本并返回到 Unity。
我们现在可以将模式添加到交互控制器中,如下所示:
-
在
PlaceObject Mode中。 -
将PlaceObjectMode脚本从项目窗口拖到PlaceObject Mode对象上,将其添加为组件。
-
将AR Session Origin对象从层次结构拖到Place Object Mode | Raycaster槽中。
现在我们将模式添加到Interaction Controller。
-
在层次结构中,选择Interaction Controller对象。
-
在检查器中,在Interaction Controller组件的右下角,点击+按钮向交互模式字典添加一个项。
-
在Id字段中输入
PlaceObject文本。 -
将 PlaceObject 模式 游戏对象从 层次结构 拖到 值 插槽。现在 交互控制器 组件看起来如下所示:

图 5.6 – 添加 PlaceObject 后的交互控制器交互模式列表
我们现在已添加了一个 OnPlaceObject 输入动作事件。在输入事件发生时,我们使用 Raycast 确定用户想在 3D 空间中的哪个位置放置对象,然后脚本实例化预制件并返回到主模式。
最后一步是将主菜单按钮连接起来。
连接菜单按钮
当用户按下主菜单按钮以向场景添加对象时,按钮将告诉 PlaceObjectMode 哪个预制件将被实例化。然后启用 PlaceObject 模式,提示用户轻触放置对象并处理用户输入动作。现在让我们按照以下步骤设置菜单按钮:
-
通过导航到 UI Canvas / Main UI / 主菜单 在 层次结构 中展开 主菜单 游戏对象,并选择 立方体按钮 对象。
-
在其 检查器 中,在 按钮 组件的 OnClick 部分,按下右下角的 + 按钮以添加事件动作。
-
从 层次结构 中将 PlaceObject Mode 对象拖到 OnClick Object 插槽。
-
在 函数 选择列表中,选择 PlaceObject Mode | SetPlacedPrefab。
-
在
Assets/ARF-samples/Prefabs/文件夹中,将 AR Placed Cube 预制件拖到 检查器 中此点击事件的 Game Object 插槽。 -
现在让按钮启用 PlaceObject 模式。在其 检查器 中,在 按钮 组件的 OnClick 部分,按下右下角的 + 按钮以添加另一个事件动作。
-
从 层次结构 中将 交互控制器 对象拖到 OnClick 事件的 Object 插槽。
-
在 函数 选择列表中,选择 InteractionController | EnableMode。
-
在字符串参数字段中,输入
PlaceObject。
立方体按钮对象的按钮组件现在具有以下 OnClick 事件设置:

图 5.7 – 立方体按钮的 OnClick 事件
对球体按钮和病毒按钮重复这些步骤。作为一个快捷方式,我们可以按以下方式复制/粘贴组件设置:
-
在 层次结构 中选择 立方体按钮,在 检查器 中,点击 按钮 组件的三点上下文菜单,并选择 复制组件。
-
在 层次结构 中选择 球体按钮 对象。
-
在其 检查器 中,点击 按钮 组件的三点上下文菜单,并选择 粘贴组件值。
-
在
Assets/ARF-samples/Prefabs/文件夹中,将 AR Placed Sphere 预制件拖到 检查器 中此点击事件的 Game Object 插槽。 -
同样,对
Prefabs文件夹重复步骤 1-4)。 -
使用文件 | 保存保存您的作品。
现在应该已经设置好了。我们使用PlaceObjectMode脚本创建了一个新场景,该脚本处理用户输入动作并实例化预制体,并将其所有内容连接到主菜单按钮。让我们试试吧!
执行建筑和运行
要构建和运行项目,请按照以下步骤操作:
-
使用文件 | 构建设置打开构建设置窗口。
-
如果
FrameworkDemo)尚未在构建场景列表中。 -
确保在构建场景列表中只选中
FrameworkDemo场景。 -
点击构建和运行以构建项目。
当项目构建成功时,它将在 AR 会话初始化时启动启动模式。然后它进入扫描模式,提示用户扫描环境,直到至少检测到一个水平平面并跟踪。然后它进入主模式并显示主菜单。以下图显示了在这些模式下在我的手机上运行的应用程序的屏幕截图:

![图 5.8 – 启动模式、扫描模式和主模式的屏幕截图]
当按下菜单按钮之一时,应用程序进入 PlaceObject 模式,提示用户轻触放置对象。轻触屏幕将在环境中指定位置实例化对象。然后应用程序返回主模式。
现在我们有一个工作演示 AR 应用程序,可以将各种虚拟对象放置在您环境中的水平表面上。一个可能的改进是在主模式中隐藏可跟踪对象,仅在需要时在 PlaceObject 模式中显示它们。
在不需要时隐藏跟踪对象
当应用首次开始跟踪时,我们会显示可跟踪的平面和点云。这对于应用首次启动时以及放置对象时向用户提供有用的反馈。但是,一旦我们在场景中放置了对象,这些可跟踪的可视化可能会分散注意力且不受欢迎。让我们只在 PlaceObject 模式中显示对象,并在至少放置了一个虚拟对象后隐藏它们。
在 AR Foundation 中,隐藏可跟踪对象需要两个单独的操作:隐藏已经检测到的现有可跟踪对象,并防止新的可跟踪对象被检测和可视化。我们将实现这两者。
为了实现这一点,我们可以在PlaceObject 模式上编写一个单独的组件,在启用时显示可跟踪对象,在禁用时隐藏它们。按照以下步骤操作:
-
在您的
Scripts/文件夹中创建一个新的 C#脚本,命名为ShowTrackablesOnEnable,并打开它进行编辑。 -
在类顶部,添加对
ARSessionOrigin、ARPlaneManager和ARPointCloudManager的变量引用。现在,我们将记住最近放置的对象在lastObject中,并在Awake中初始化它们,如下所示:using UnityEngine; using UnityEngine.XR.ARFoundation; public class ShowTrackablesOnEnable : MonoBehaviour { [SerializeField] ARSessionOrigin sessionOrigin; ARPlaneManager planeManager; ARPointCloudManager cloudManager; bool isStarted; void Awake() { planeManager = sessionOrigin.GetComponent<ARPlaneManager>(); cloudManager = sessionOrigin.GetComponent <ARPointCloudManager>(); } private void Start() { isStarted = true; }我还添加了一个
isStarted标志,我们将用它来防止在应用启动时隐藏可视化器。信息:
OnEnable和OnDisable可以在Start之前被调用在
MonoBehaviour组件的生命周期中,当对象被启用并激活时调用OnEnable。当脚本对象变为非活动状态时调用OnDisable。在脚本被启用后的第一帧调用Start,在Update之前。请参阅docs.unity3d.com/ScriptReference/MonoBehaviour.Awake.html。在我们的应用中,
OnDisable可能会在Start之前被调用(当我们从InteractionController初始化场景时)。为了防止在场景开始之前调用ShowTrackables(false),我们在脚本中使用一个isStarted标志。 -
当模式启用时,我们将显示可追踪对象,当禁用时,将使用以下代码隐藏它们:
void OnEnable() { ShowTrackables(true); } void OnDisable() { if (isStarted) { ShowTrackables(false); } } -
这些调用
ShowTrackables,我们按以下方式实现:void ShowTrackables(bool show) { if (cloudManager) { cloudManager.SetTrackablesActive(show); cloudManager.enabled = show; } if (planeManager) { planeManager.SetTrackablesActive(show); planeManager.enabled = show; } } }设置
SetTrackablesActive(false)将隐藏所有现有的可追踪对象。禁用可追踪管理器组件本身将防止添加新的可追踪对象。我们检查ARSessionOrigin中是否存在空管理器,以防组件不存在。 -
保存脚本。
-
回到 Unity,在 Hierarchy 中选择 PlaceObject 模式 游戏对象。
-
将
ShowTrackablesOnEnable脚本拖放到 PlaceObject 模式 对象上。 -
将 AR Session Origin 对象从 Hierarchy 拖到 Inspector,然后将其拖放到 Show Trackables On Enable | Session Origin 插槽中。
-
使用 文件 | 保存 保存场景。
现在当您再次点击 Build And Run 时,当 PlaceObject 模式启用时,可追踪对象将被显示,当禁用时将被隐藏。因此,当 Main 模式首次启用时,可追踪对象将是可见的,但在放置对象并应用回到 Main 模式后,可追踪对象将被隐藏。这是我们期望的行为。PlaceObject 模式和随后的 Main 模式在以下手机运行项目屏幕截图中显示:

图 5.9 – PlaceObject 模式和随后隐藏可追踪对象的主模式屏幕截图
小贴士:通过修改平面检测模式来禁用可追踪对象
要禁用平面检测,我使用的方法是禁用管理器组件。这是在 AR Foundation 示例项目中提供的 PlaneDetectionController.cs 脚本中给出的技术。作为替代,Unity ARCore XR 插件文档(docs.unity3d.com/Packages/com.unity.xr.arcore@4.1/manual/index.html)建议通过将 ARPlaneManager 检测模式设置为 PlaneDetectionMode.None 来禁用平面检测。
我们现在已经完成了一个简单的 AR 项目,使用我们的 AR 用户框架在环境中检测到的水平平面上放置各种虚拟对象。
您可以添加到项目中的进一步改进包括以下内容:
-
主菜单中的重置按钮,用于移除场景中已放置的任何虚拟对象。
-
一次只允许放置一个虚拟对象到场景中。
-
移动和调整现有对象大小的能力(参见第七章**,图库:编辑虚拟对象)。
-
您能想到更多的改进吗?请告诉我们。
在本章的剩余部分,我们将讨论一些您可能希望在以后的项目中包含的高级入门和用户体验功能。
高级入门问题
在本节中,我们将回顾一些与 AR 入门、AR 会话和设备相关的问题,包括以下内容:
-
创建一个可选 AR 的项目
-
确定设备是否支持特定的 AR 功能
-
为您的项目添加本地化支持
创建一个可选 AR 的项目
一些应用程序旨在专门使用 AR 功能运行,并且如果不受支持,应该(在向用户发出友好通知后)直接退出。但其他应用程序可能希望表现得像一个普通的移动应用,同时额外提供支持 AR 功能的可选能力。
例如,我最近创建的一个游戏,Epoch Resources(可在 Android play.google.com/store/apps/details?id=com.parkerhill.EpochResources&hl=en_US&gl=US 和 iOS apps.apple.com/us/app/epoch-resources/id1455848902 上找到)是一款行星演化的增量游戏,您可以在其中挖掘 3D 行星以获取资源。它提供了一个可选的 AR 观看模式,您可以将行星“弹出”到您的客厅,并在 AR 中继续玩游戏,如下面的图片所示。

图 5.10 – Epoch Resources 是一款可选 AR 的游戏
对于一个可选 AR 的应用程序,您的应用可能最初启动为一个普通的非 AR 应用。然后,在某个时刻,用户可能会选择开启 AR 特定功能。那时,您将激活 AR 会话并处理用户入门 UX。
本书中的所有项目都没有实现可选 AR,因此这只是一个信息性讨论。首先,您需要通过访问编辑 | 项目设置 | XR 插件管理并选择每个平台(ARCore 和 ARKit 分别设置)的需求 | 可选(而不是必需)来告诉 XR 插件 AR 是可选的。
你需要一个机制来决定是否运行 AR 或非 AR 模式。一种方法是有单独的 AR 和非 AR 场景,根据需要加载(见docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.html)。
在《纪元资源》游戏的情况下,我们没有创建两个独立的场景。相反,场景包含两个摄像头,一个是用于非 AR 模式的正常默认摄像头,另一个是用于 AR 模式的 AR 会话原点(带有子摄像头)。然后当用户切换查看模式时,我们在两个摄像头之间切换。
你可能会遇到的问题之一是在运行时确定用户的设备是否支持特定的 AR 功能。
确定设备是否支持特定的 AR 功能
可能你的应用程序需要特定的 AR 功能,但并非所有设备都支持。我们可以通过获取子系统描述符记录来询问 Unity AR 子系统支持哪些功能。
例如,假设我们感兴趣的是检测垂直平面。一些较旧的设备可能支持 AR,但只支持水平平面。以下代码说明了如何获取和检查平面检测支持:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARSubsystems;
public class CheckPlaneDetectionSupport : MonoBehaviour
{
void Start()
{
var planeDescriptors = new List<XRPlaneSubsystemDescriptor>();
SubsystemManager. GetSubsystemDescriptors(planeDescriptors);
Debug.Log("Plane descriptors count: " + planeDescriptors.Count);
if (planeDescriptors.Count > 0)
{
foreach (var planeDescriptor in planeDescriptors)
{
Debug.Log("Support horizontal: " + planeDescriptor. supportsHorizontalPlaneDetection);
Debug.Log("Support vertical: " + planeDescriptor. supportsVerticalPlaneDetection);
Debug.Log("Support arbitrary: " + planeDescriptor. supportsArbitraryPlaneDetection);
Debug.Log("Support classification: " + planeDescriptor.supportsClassification);
}
}
}
}
AR Foundation 中可用的描述符类型包括以下几种(它们的功能从其名称中显而易见):
-
XRPlaneSubsystemDescriptor -
XRRaycastSubsystemDescriptor -
XRFaceSubsystemDescriptor -
XRImageTrackingSubsystemDescriptor -
XREnvironmentProbeSubsystemDescriptor -
XRAnchorSubsystemDescriptor -
XRObjectTrackingSubsystemDescriptor -
XRParticipantSubsystemDescriptor -
XRDepthSubsystemDescriptor -
XROcclusionSubsystemDescriptor -
XRCameraSubsystemDescriptor -
XRSessionSubsystemDescriptor -
XRHumanBodySubsystemDescriptor
AR 子系统 API 和这些描述符记录的文档可以在docs.unity3d.com/Packages/com.unity.xr.arsubsystems@4.2/api/UnityEngine.XR.ARSubsystems.html找到。例如,我们这里使用的XRPlaneSubsystemDescriptor记录在docs.unity3d.com/Packages/com.unity.xr.arsubsystems@4.2/api/UnityEngine.XR.ARSubsystems.XRPlaneSubsystemDescriptor.Cinfo.html有文档说明。
如果你计划在不同国家分发你的应用程序,你可能也会对本地化感兴趣。
添加本地化
本地化是将文本字符串和其他资产翻译成本地语言的过程。它还可以指定日期和货币格式、国家国旗的替代图形等,以适应国际市场和用户。Unity 本地化包提供了一套标准工具和数据结构,用于本地化您的应用程序。更多信息可以在 docs.unity3d.com/Packages/com.unity.localization@0.10/manual/QuickStartGuide.html 找到。在本书中,我们除了使用导入的资产(如 AR Foundation Demos 项目的 Onboarding UX 资产)已经支持本地化的项目外,不使用本地化。
Unity Onboarding UX 资产内置了对用户提示和扫描问题解释的本地化支持。例如,与 Onboarding UX 项目一起提供的 ReasonsUX 本地化表格可以通过选择 窗口 | 资产管理 | 本地化表格 来打开,如下面的截图所示。例如,您可以看到第二行的 INIT 键在英文中是 Initializing augmented reality,以及翻译成多种其他语言的相同字符串:

图 5.11 – 包含在 Onboarding UX 资产中的 ReasonsUX 本地化表格
在代码中,例如,使用如下调用检索 Initializing augmented reality 消息:
string localizedInit = reasonsTable.GetEntry("INIT").GetLocalizedString();
当我们将 onboarding UX 预制件 (ARFoundationDemos/UX/Prefabs/ScreenspaceUI) 添加到场景中时,我让您禁用了 本地化管理器 组件,因为它在设置好之前会引发运行时错误。假设您已经通过 包管理器 安装了 本地化 包,如本章前面所述,我们现在可以使用以下步骤为项目设置它:
-
通过访问 编辑 | 项目设置 | 本地化 打开 本地化 设置窗口。
-
在
Assets/ARFOundationDemos/Common/Localization/中,将LocalizationSettings资产拖放到 位置设置 槽中(或使用 甜甜圈 图标打开 位置设置选择 对话框)。 -
在设置窗口中,点击 添加全部。
-
在 层次结构 窗口中,选择 OnboardingUX 对象,并在 检查器 中启用 本地化管理器 组件。
-
使用 窗口 | 资产管理 | 可寻址 | 组 打开 可寻址组 窗口。
-
从 可寻址组 菜单栏中选择 构建 | 新构建 | 默认构建脚本。您需要为每个构建的目标平台执行此操作(例如,一次为 Android,一次为 iOS)。
正如您在最后一步看到的,本地化包使用 Unity 的新 Addressables 系统来管理、打包和从本地或互联网上的任何位置加载资源 (docs.unity3d.com/Packages/com.unity.addressables@1.12/manual/index.html)。
注意,在我写这篇文档的时候,入职 UX 的 LocalizationManager 脚本在运行时不会选择语言。语言必须在检查器中设置,并编译到您的构建中。
本章中构建的 AR UI 框架可以用作新场景的模板。Unity 使其设置变得简单。
概述
在本章中,我们有机会使用在上一章第四章**,创建 AR 用户框架中开发的 AR 用户框架,在一个简单的 AR 放置对象演示项目中。我们使用 ARFramework 场景模板创建了一个新场景,该模板实现了一个状态机机制来管理用户交互模式。它使用控制器-视图设计模式处理用户交互,将控制脚本与 UI 图形分离。
默认情况下,场景包括 AR Foundation 所需的 AR Session 和 AR Session Origin 组件。场景配置了一个包含单独面板的 Canvas UI,每个交互模式将显示不同的面板。它还包括一个引用单独模式对象的交互控制器,每个交互模式一个。
模式(以及相应的 UI)包括启动、扫描、主和非 AR。使用此框架的应用首先在启动模式开始,此时 AR 会话正在初始化。然后它进入扫描模式,提示用户扫描环境中的可追踪特征,直到检测到水平面。然后它进入主模式并显示主菜单。
对于这个项目,我们添加了一个在主模式期间显示的主菜单,其中包含将各种虚拟对象放置在环境中的按钮。按下按钮将启用我们添加到场景中的新 PlaceObject 模式。当 PlaceObject 模式启用时,它显示一个指导性的动画提示,用户需要点击以在场景中放置对象。添加对象后,应用返回主模式,可追踪对象被隐藏,这样您就可以在没有额外干扰的情况下在真实世界中看到您的虚拟对象。
在下一章中,我们将超越简单的演示项目,开始构建一个更完整的 AR 应用程序——一个照片画廊,您可以在家中或办公室的乏味墙壁上放置您最喜欢的照片的相框。
第三部分 – 构建更多 AR 项目
在本节中,我们将构建各种 AR 项目,每个项目都使用 AR Foundation 支持的不同特征检测技术,包括平面检测、图像识别和面部追踪。每个项目的成果是一个可改进和进一步开发成更完整应用的工作演示。
本节包含以下章节:
-
第六章**, 展厅:构建 AR 应用
-
第七章, 展厅:编辑虚拟对象
-
第八章, 行星:跟踪图像
-
第九章, 自拍:制作搞笑表情
第六章:第六章:画廊:构建 AR 应用
在本章中,我们将开始构建一个完整的增强现实(AR)应用,一个 AR艺术画廊,让你能在现实世界的墙上挂上虚拟的框架照片。
首先,我们将定义项目的目标,并讨论项目规划和用户体验(UX)设计的重要性。当用户在主菜单中按下添加按钮时,他们将看到一个选择图像菜单。当他们选择一个时,他们将被提示将图像的框架副本放置在他们的现实世界墙上。
要实施项目,我们将从本书早期创建的 AR 用户框架场景模板开始。我们将构建一个选择图像 UI 面板和交互模式,并定义应用使用的图像数据。
在本章中,我们将涵盖以下主题:
-
指定新的项目和 UX 设计
-
使用数据结构和数组,以及在对象之间传递数据
-
创建一个带有按钮网格的详细 UI 菜单面板
-
创建用于在 AR 场景中实例化的预制件
-
基于给定的用户故事实现完整场景
到本章结束时,你将拥有一个实现一个场景的工作原型:在墙上放置图片。然后我们将在下一章继续构建和改进项目。
技术要求
要在本章中实现项目,您需要在您的开发计算机上安装 Unity,并将其连接到一个支持 AR 应用程序的移动设备(有关说明,请参阅第一章,为 AR 开发设置)。我们还假设您已安装了ARFramework模板及其先决条件;请参阅第五章**,使用 AR 用户框架。完成的项目可以在本书的 GitHub 存储库中找到,github.com/PacktPublishing/Augmented-Reality-with-Unity-AR-Foundation。
指定艺术画廊项目的 UX
在开始任何新项目之前的一个重要步骤是在事先做一些设计和规范。这通常意味着将其写入文档。对于游戏,这可能被称为游戏设计文档(GDD)。对于应用程序,它可能是一个软件设计文档(SDD)。无论你称之为什么,其目的是在开发开始之前将项目的蓝图写入文档。一个 Unity AR 项目的详细设计文档可能包括以下细节:
-
项目概述:总结项目的概念和目的,确定主要受众,并可能包括一些关于项目存在的原因以及它将如何成功的背景信息。
-
用例:确定产品将解决的实际生活问题。通常,定义代表应用程序不同用户类型、他们的主要目标和如何使用应用程序来实现这些目标的单独用户角色(可以是真实或虚构的名字)是有效的。
-
关键特性:确定为用户提供价值的离散功能区域,可能强调其与其他类似解决方案的区别。
-
UX 设计:用户体验(UX)设计可能包括各种用户场景,详细说明特定的工作流程,通常以故事板的形式呈现,使用抽象的铅笔或线框草图。如果没有绘画技巧,白板会议的照片捕捉和便利贴可能就足够了。
此外,你还可以包括 UI 图形设计,定义实际的风格指南和图形,例如,色彩方案、字体、按钮图形等。
-
资产:收集和分类你预计需要的图形资产,包括概念艺术、3D 模型、效果和音频。
-
技术计划:这包括将要使用的软件架构和设计模式,开发工具(例如 Unity、Visual Studio 和 GitHub),Unity 版本,第三方包(例如,通过包管理器),以及 Unity 服务和其它云服务(如广告、网络和数据存储)。
-
项目计划:实施计划可能显示预期的项目阶段、生产和发布时间表。这可能涉及使用诸如 Jira 或 Trello 之类的工具。
-
商业计划:非技术规划可能包括项目管理、营销、资金、货币化、用户获取和社区建设计划。
对于非常大的项目,这些部分可能是单独的文档。对于小型项目,整个文档可能只有几页长,包含项目符号。记住,主要目的是在编写代码之前思考你的计划。话虽如此,不要过度设计。记住我非常喜欢爱因斯坦的一句话:
"尽可能使一切变得尽可能简单,但不能过于简单。"
假设随着项目的发展,事情可能会并且将会发生变化。快速迭代、利益相关者的频繁反馈和吸引真实用户可能会重申你的计划。或者,它可能会暴露原始设计中的严重缺陷,并将项目引向新的、更好的方向。正如我对我客户和学生的建议:
"你对项目了解最少的时候是在项目开始时!"
在这本书中,我将在每个项目的开始提供一个简化的设计计划,试图捕捉最重要的点,而不涉及太多细节。让我们从这个 AR 画廊项目开始,并具体说明项目目标、用例、一个 UX 设计和一组定义项目关键特性的用户故事。
项目目标
我们将构建一个 AR 艺术画廊项目,允许用户使用 AR 将他们最喜欢的照片作为虚拟相框图像放置在他们的家或办公室的墙上。
用例
人物:杰克。杰克在家工作,没有时间装饰他那单调的公寓。杰克希望通过在墙上添加一些漂亮的图片来装饰墙壁。但他的房东不允许在墙上钉钉子。约翰也希望能够经常更换他挂的照片。杰克每天花费很多时间使用他的手机,所以通过手机看墙壁是令人满意的。
人物:吉尔。吉尔有一大堆喜欢的照片。她希望将它们挂在办公室的墙上,但这在办公环境中并不太合适。此外,她有点强迫症,因此希望经常重新排列照片并交换图片。
UX 设计
此应用程序的用户体验(UX)必须包括以下要求和场景:
-
当用户想要在墙上放置照片时,他们从菜单中选择一张图片,然后轻触屏幕,指示放置照片的位置。
-
当用户想要修改已经放置在墙上的照片时,他们可以轻触照片以启用编辑功能。然后用户可以拖动来移动,捏合来调整大小,选择不同的照片或相框,或者滑动来移除照片。
-
当相框照片渲染时,它会匹配当前的房间照明条件,并在真实世界的表面上投下阴影。
-
当用户退出并重新打开应用时,他们放置在房间中的所有照片都将被保存并恢复到它们的位置。
我请了一位专业的 UX 设计师(也是我的朋友)Kirk Membry (kirkmembry.com/) 为这本书的项目准备 UX 线框草图。以下图像显示了整个故事板的一些框架:

图 6.1 – UX 设计线框草图
最左侧的框架显示了当用户选择将新照片添加到场景中时出现的图像库菜单。中间的框架描述了用户选择在墙上挂照片的位置。最右侧的框架显示了用户正在编辑现有的图片,包括用于移动和调整大小的手势,以及屏幕底部的其他编辑选项菜单。
这样的故事板可以用来与图形设计师、编码人员和利益相关者沟通设计意图。它可以作为讨论的基础,以消除用户工作流程中的瑕疵和用户界面的不一致性。它可以大大提高项目管理效率,通过在成本最高的阶段——在功能实现之后——防止不必要的返工。
在设计草案足够的情况下,我们现在可以选出一些在构建项目时将使用的资产。
用户故事
将功能分解成一系列“用户故事”或小块功能,可以逐步实现,一次构建项目的一部分,这是很有用的。在一个敏捷管理的项目中,团队可能会选择在一到两周的冲刺中完成的一组特定的故事。这些故事可以在共享的项目板上进行管理和跟踪,例如 Trello (trello.com/) 或 Jira (www.atlassian.com/software/jira)。以下是本项目的几个故事:
-
当应用启动时,我被提示扫描房间,同时设备在环境中检测和跟踪垂直墙壁。
-
在跟踪建立后,我可以看到一个主菜单,其中有一个添加按钮。
-
当我按下添加按钮时,我可以看到一组照片的选择。
-
当我从选择中选择一张照片时,我可以看到跟踪的垂直平面,并被提示在墙上挂一个带框的图片(图片)。
-
当图片实例化时,它垂直地紧贴墙面。
-
当我点击现有的虚拟图片以开始编辑图片时。
-
当编辑图片时,我可以看到一个编辑菜单,可以选择更改照片、更改框架或删除带框的图片。
-
当编辑图片时,我可以将图片拖动到新位置。
-
当编辑图片时,我可以使用捏合(使用两个手指)来调整大小。
这看起来像是一组很好的功能。我们将尝试在本章中完成它们的前半部分,并在下一章中完成它们。让我们开始吧。
开始
要开始,我们将使用ARFramework场景模板创建一个名为ARGallery的新场景,以下是步骤:
-
选择文件 | 新场景。
-
在新场景对话框中,选择ARFramework模板。
-
选择创建。
-
在你的项目
Assets文件夹中的Scenes/文件夹中,将其命名为ARGallery,并选择保存。
新的 AR 场景已经包含以下对象:
-
一个AR 会话游戏对象。
-
一个带有射线管理器和平面管理器组件的AR 会话起源装置。
-
UI Canvas是一个屏幕空间画布,包含子面板启动 UI、扫描 UI、主 UI 和非 AR UI。它具有我们编写的 UI 控制器组件脚本。
-
交互控制器是一个具有我们编写的交互控制器组件脚本的游戏对象,它帮助应用在交互模式之间切换,包括启动、扫描、主和非 AR 模式。它还配置了玩家输入组件,该组件使用我们之前创建的AR 输入动作资产。
-
来自 AR Foundation Demos 项目的OnboardingUX预制件,它提供 AR 会话状态和特征检测状态消息,以及动画引导图形提示。
我们现在为 AR 画廊项目制定了计划,包括目标声明、用例和带有一些用户故事的 UX 设计以实现。有了这个场景,我们就准备好了。让我们找到我们可以工作的照片集合并将它们添加到项目中。
收集图像数据
在 Unity 中,图像可以导入用于各种目的。纹理是可以用于渲染 3D 对象表面的材质的图像。UI 使用图像作为按钮和面板图形的精灵。对于我们的相框照片,我们将使用图像作为…图像。
在您的应用程序中使用图片的最基本方法是将它们导入到您的Assets文件夹中,并将它们作为 Unity 纹理进行引用。一个更高级的解决方案是在运行时动态查找和加载它们。在本章中,我们将使用前者技术并将图像列表构建到应用程序中。让我们首先导入您想要使用的照片。
导入照片以使用
请从您的收藏夹中选择一些图像用于您的画廊。或者,您可以使用本书 GitHub 仓库中包含的图像,这些图像是从 Unsplash.com([unsplash.com/](https://unsplash.com/))找到的免费可用的自然照片集合,以及我自己的一张名为WinterBarn.jpg的照片。
要将图像导入到您的项目中,请按照以下步骤操作:
-
在
Photos中,通过右键单击,然后选择创建 | 文件夹。 -
从您的 Windows 资源管理器或 OSX Finder 中找到您想要使用的图像。然后将图像文件拖入 Unity,将其放入
Photos/文件夹中。 -
在检查器窗口中,您可以检查导入的图像的大小。因为我们将在 AR 和相对低分辨率的移动设备上使用它,所以让我们将最大尺寸限制为 1,024 像素。请注意,Unity 要求将纹理导入到 2 的幂次大小,以获得最佳压缩和运行时优化。在检查器中,确保选择了默认选项卡,并选择最大尺寸 | 1024。
现在我们将添加一种在场景中引用您的图像的方法。
将图像数据添加到场景
要将图像数据添加到场景中,我们将创建一个包含图像列表的ImagesData脚本的空 GameObject。首先,在您的项目Scripts/文件夹中创建一个新的 C#脚本,命名为ImagesData,并编写如下:
using UnityEngine;
[System.Serializable]
public struct ImageInfo
{
public Texture texture;
public int width;
public int height;
}
public class ImagesData : MonoBehaviour
{
public ImageInfo[] images;
}
脚本首先定义一个包含图像Texture和图像像素尺寸的ImageInfo数据结构。它是public的,因此可以从其他脚本中引用。然后ImagesData类在images变量中声明了这个数据结构的数组。ImageInfo结构需要[System.Serializable]指令,以便在 Unity 检查器中显示。
现在我们可以通过以下步骤将图像数据添加到场景中:
-
从主菜单中选择
Images Data(使用三点上下文菜单和重置来整理其变换)。 -
将 ImagesData 脚本拖放到 Images Data 对象上,使其成为一个组件。
-
要填充
images数组,在 检查器 中输入你计划使用的图像数量,或者简单地按右下角的 + 按钮以递增方式向数组中添加元素。 -
通过展开 Images 列表中的一个 Element,逐个添加导入的图像文件,然后从 项目 窗口将图像文件拖放到元素的 Texture 槽中。请务必输入每个图像的 Width 和 Height 像素值。
我的 Images Data 在 检查器 中的样子如下:

图 6.2 – 带有图像列表的图像数据组件
使用 ScriptableObjects
提供图像列表的另一种,可能更好的方法,是使用 ScriptableObjects 而不是 GameObjects。ScriptableObjects 是存在于你的 Assets/ 文件夹中的数据容器对象,而不是在场景层次结构中。你可以在 docs.unity3d.com/Manual/class-ScriptableObject.html 和 learn.unity.com/tutorial/introduction-to-scriptable-objects 上了解更多关于 ScriptableObjects 的信息。
手动输入每张图像的像素尺寸有点繁琐。如果能有一种更好的方法那就太好了,因为这样做并不容易。
获取图像的像素尺寸
不幸的是,当 Unity 将图像导入为纹理时,它会将其缩放到 2 的幂以优化运行时性能和压缩,并且原始尺寸数据不会被保留。有几种方法可以解决这个问题,但没有一种是特别美观的:
-
要求开发者手动为每个图像指定像素尺寸。这是我们在这里采取的方法。
-
告诉 Unity 在导入图像时不进行缩放。为此,选择一个图像资产,并在其
Texture.width和Texture.height。 -
采用第一种方法,但使用编辑器脚本来自动确定像素大小。Unity 允许你编写仅在编辑器中运行的脚本,而不是在运行时。编辑器在将原始图像文件导入为纹理之前,可以访问你的 Assets 文件夹中的原始图像文件。因此,可以使用系统 I/O 函数或可能是(未记录的)Unity API(见
forum.unity.com/threads/getting-original-size-of-texture-asset-in-pixels.165295/)来读取和查询这些信息。
既然如此,我们将在本章中坚持手动方法,你可以自己探索其他选项。
也许你还在想,如果我不想将图像构建到我的项目中,想在运行时查找和加载它们怎么办?
在运行时加载图片列表
在运行时从构建外部加载资源是一个高级主题,并且超出了本章的范围。我将简要描述几种不同的方法,并将提供更多信息的链接:
-
在资源包中包含图像:在 Unity 中,你可以选择将资源打包到资源包中,应用程序在用户安装应用程序后可以下载这些资源,作为可下载内容(DLC)。请参阅
docs.unity3d.com/Manual/AssetBundlesIntro.html。 -
从网络 URL 下载图像:如果你有图像文件的网址,你可以使用网络请求在运行时下载图像,并将其用作应用程序中的纹理。请参阅
docs.unity3d.com/ScriptReference/Networking.UnityWebRequestTexture.GetTexture.html。 -
从设备的照片应用获取图像:对于像我们的图库这样的应用程序,从用户的照片应用中获取照片是很自然的。要访问移动设备上其他应用程序的数据,你需要一个具有本地访问权限的库。这也可能需要你的应用程序从用户那里获得额外的权限。在 Unity Asset Store 中搜索包。
如果你想实现这些功能,那就由你自己来决定。
我们现在已经导入了我们计划使用的照片,创建了一个包含每个图像像素尺寸的 C# ImageInfo数据结构,并在场景中填充了这些图像数据。让我们创建一个包含默认图像和可以放置在墙面上的照片框架的装框照片预制体。
创建一个装框照片预制体
用户将在他们的墙上放置一个装框的照片。因此,我们需要创建一个预制游戏对象,它将被实例化。我们希望使其易于更改图像和框架,以及根据不同的方向(横向与纵向)和图像宽高比进行缩放。对于默认框架,我们将从一个压扁的 3D 立方体创建一个简单的块,并将照片安装在其表面上。对于默认图像,你可以选择自己的,或者使用 GitHub 仓库中本章文件包含的一个。
创建预制体层次结构
首先,在项目文件夹的Assets/中创建一个名为FramedPhoto的空预制体。按照以下步骤操作:
-
在
Prefabs/文件夹中(如果需要,请创建一个)。然后在该文件夹中右键单击并选择创建 | 预制体。 -
将新预制体重命名为
FramedPhoto。 -
双击 FramedPhoto 资产(或在检查器窗口中点击其打开预制体按钮)。
我们现在正在编辑一个空预制体。
-
添加一个子
AspectScaler。 -
让我们使用一个压扁的立方体来创建一个看起来现代的黑色矩形框架。使用
Frame。 -
给框架一些厚度。在框架的
0.05(这是以米为单位)。 -
同样,通过设置
-0.025将其从墙上偏移。 -
要给这个框架一个黑色表面,创建并添加一个新的材质,如下所示。
在
Materials/文件夹中(如果需要,请创建一个)。然后 右键单击 文件夹并选择Black Frame Material。 -
将其 基础地图 颜色设置为炭黑色。
-
然后,在 层次结构 中,选择 Default Frame 对象,并将 Black Frame Material 拖放到它上面。
以下截图显示了当前的框架属性:

图 6.3 – FramedPhoto 的框架属性
接下来,我们将向包含在此书文件中的 WinterBarn.jpg 添加一个默认图像。使用以下步骤创建一个使用此照片作为纹理图像的材质图像对象:
-
使用
Image。一个 quad 是最简单的 Unity 3D 基本对象,是一个只有四个边且朝一个方向的面板。 -
要将你的图像作为 quad 上的纹理添加,我们需要创建一个材质。在
Materials/文件夹中,右键单击 文件夹并选择Image Material。 -
将你的图像文件(
WinterBarn.jpg)从 项目 窗口拖动到 检查器 窗口,将其放置在 基础地图 属性左侧的小正方形“芯片”槽中。 -
将 Image Material 拖动到 Image 游戏对象上。
-
将图像 quad 偏移,使其略微位于框架立方体的平面之前。设置其
-0.06。 -
你现在应该能看到图像。但是框架是隐藏的,因为图像 quad 被缩放到了与框架相同的大小。通过设置其
0.9来缩小图像。
预制件层次结构现在看起来如下截图所示,其中图像当前被选中并在 检查器 中可见:

图 6.4 – FramedPhoto 预制件
接下来,让我们添加一个简单的脚本,它将帮助我们的其他代码设置 FramedPhoto 对象的图像。
编写 FramedPhoto 脚本
我们将需要为 SetImage 函数的每个实例设置各种属性,该函数获取此图片的图像数据。
创建一个名为 FramedPhoto 的新 C#脚本,打开它进行编辑,并编写以下脚本:
using UnityEngine;
public class FramedPhoto : MonoBehaviour
{
[SerializeField] Transform scalerObject;
[SerializeField] GameObject imageObject;
ImageInfo imageInfo;
public void SetImage(ImageInfo image)
{
imageInfo = image;
Renderer renderer = imageObject.GetComponent<Renderer>();
Material material = renderer.material;
material.SetTexture("_BaseMap", imageInfo.texture);
}
}
在 FramedPhoto 类的顶部,我们声明了两个属性。imageObject 是对子 scalerObject 的引用,scalerObject 是对 AspectScaler 的引用,当脚本需要更改其纵横比时(我们在本章末尾这样做)。
当将 SetImage 用于更改 Renderer,然后获取其 Material,接着设置其基础纹理。
我们现在可以将此脚本按以下方式添加到预制件中:
-
在编辑 FramedPhoto 预制件时,将 FramedPhoto 脚本拖动到 FramedPhoto 根对象上,使其成为一个组件。
-
从 层次结构 中,将 AspectScaler 对象拖动到 检查器 并将其放置在 Framed Photo | Scaler Object 槽中。
-
从 层次结构 中,将 Image 对象拖动到 Framed Photo | Image Object 槽中。
我们的预制件现在几乎准备好使用了。当然,我们使用的图片并不是真的应该是正方形的,所以让我们将其缩放。
缩放图片的形状
我默认使用的照片是横向的,但我们的框架是正方形的,所以看起来被压扁了。为了修复它,我们需要获取图像的原始像素大小并计算其宽高比。例如,GitHub 上包含在此书中的WinterBarn.jpg图像是 4,032x3,024(宽度 x 高度),或 3:4(高度:宽度的横向比例)。现在让我们根据图像的宽高比(0.75)进行缩放。按照以下步骤操作:
-
在层次结构窗口中,选择缩放器对象。
-
设置其
0.75(如果你的图像是竖直的,则缩放1.0)。正确缩放的预制件现在看起来如下:
![Figure 6.5 – FramedPhoto prefab with corrected 3:4 landscape aspect ratio]
![img/Figure_6.05-FramedPhoto-aspect.jpg]
Figure 6.5 – FramedPhoto prefab with corrected 3:4 landscape aspect ratio
-
通过点击场景窗口右上角的保存按钮保存预制件。
-
使用<按钮返回场景编辑器,该按钮位于层次结构窗口的左上角。
-
设置
1, 1, 1),无论其中照片的宽高比或框架的厚度如何。这将有助于放置和缩放场景中带框照片的用户界面。 -
3:4 宽高比的高度为
0.75。 -
0.05大小的边框,所以0.9。 -
图像的前后偏移量也将取决于框架的模型。在这种情况下,我将它移得更近,从
-0.06到-0.025单位,因此它稍微位于框架表面之前。
当组装预制件时,思考它如何避免后续的麻烦。
在本节中,我们创建了一个可缩放的Assets文件夹,以便当用户在墙上放置图片时,可以在场景中实例化副本。预制件包括一个FramedPhoto脚本,该脚本管理预制件的一些行为方面,包括设置其图像纹理。这个脚本将在本章后面进行扩展。我们现在有一个带有框架的FramedPhoto预制件。我们准备好添加放置图片在墙上的用户交互。
在墙上挂虚拟照片
对于这个项目,应用会扫描环境中的垂直平面。当用户想要在墙上挂一幅画时,我们会显示一个 UI 面板,指导用户点击放置对象,并使用动画图形。一旦用户点击屏幕,AddPicture模式就会实例化一个FramedPhoto预制件,使其看起来挂在墙上,垂直且与墙面平面齐平。许多这些步骤与我们之前在第五章**,使用 AR 用户框架中做的类似,所以这里我会提供较少的解释。我们将从一个类似的脚本开始,然后进行增强。
检测垂直平面
由于 AR 会话原点已经有一个 AR Plane Manager 组件(在默认的ARFramework模板中提供),请按照以下步骤设置场景以扫描垂直平面(而不是水平平面):
-
在层次结构窗口中,选择AR 会话原点对象。
-
在其检查器窗口中,首先选择Nothing(清除所有选择),然后选择Vertical,将AR Plane Manager | Detection Mode设置为垂直。
现在让我们创建一个AddPicture UI 面板,提示用户点击垂直平面以放置新图片。
创建 AddPicture UI 面板
AddPicture UI面板类似于场景模板中包含的Scan UI,因此我们可以复制并按以下方式修改它:
-
在层次结构窗口中,展开UI Canvas。
-
右键单击
AddPicture UI。 -
展开AddPicture UI并选择其子项,动画提示。
-
在检查器中,将动画提示 | 指示设置为点击放置。
-
要将面板添加到 UI 控制器中,在层次结构中,选择UI Canvas对象。
-
在检查器中,在UI Controller组件的右下角点击+按钮,向 UI Panels 字典中添加一个项。
-
在Id字段中输入
AddPicture。 -
从层次结构中将AddPicture UI游戏对象拖动到值槽位。
我们为AddPicture UI 添加了一个指导性用户提示。当用户选择将图片添加到场景中时,我们将进入AddPicture模式,并显示此面板。现在让我们创建AddPicture模式。
编写初始 AddPictureMode 脚本
要将模式添加到框架中,我们在Interaction Controller下创建一个子 GameObject 并编写一个模式脚本。模式脚本将显示模式的 UI,处理任何用户交互,并在完成后过渡到另一个模式。对于 AddPicture 模式,它将显示AddPicture UI面板,等待用户点击屏幕,实例化预制对象,然后返回主模式。
该脚本开始时类似于我们在第五章**中编写的PlaceObjectMode脚本,使用AR 用户框架*。然后我们将增强它以确保框架中的图片对象与墙面平面对齐,面向房间,并垂直悬挂。
让我们编写AddPictureMode脚本,如下所示:
-
首先,通过右键单击并选择
AddPictureMode在你的项目的Scripts/文件夹中创建一个新的脚本。 -
双击文件以打开它进行编辑。粘贴以下代码,它与您可能已经拥有的PlaceObjectMode脚本相同,但有差异突出显示。脚本的前半部分如下:
using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.XR.ARFoundation; using UnityEngine.XR.ARSubsystems; public class AddPictureMode : MonoBehaviour { [SerializeField] ARRaycastManager raycaster; [SerializeField] GameObject placedPrefab; List<ARRaycastHit> hits = new List<ARRaycastHit>(); void OnEnable() { UIController.ShowUI("AddPicture"); } -
脚本的后半部分实际上与
PlaceObjectMode脚本没有变化:public void OnPlaceObject(InputValue value) { Vector2 touchPosition = value.Get<Vector2>(); PlaceObject(touchPosition); } void PlaceObject(Vector2 touchPosition) { if (raycaster.Raycast(touchPosition, hits, TrackableType.PlaneWithinPolygon)) { Pose hitPose = hits[0].pose; Instantiate(placedPrefab, hitPose.position, hitPose.rotation); InteractionController.EnableMode("Main"); } } }
在 AddPictureMode 的顶部,我们声明了一个 placedPrefab 变量,它将引用 ARRaycastManager 和一个私有的 ARRaycaseHit hits 列表,我们将在 PlaceObject 函数中使用它。
当模式启用时,我们显示 AddPicture UI 面板。然后,当有 OnPlaceObject 用户输入动作事件时,PlaceObject 对可追踪平面进行 Raycast。如果有碰撞,它将在场景中实例化一个 FramedPhoto 的副本,然后返回到主模式。
让我们先使用这个初始脚本,稍后修复我们发现的任何问题。下一步是将 AddPicture 模式添加到应用中。
创建 AddPicture 模式对象
我们现在可以通过在 Interaction Controller 下创建一个 AddPicture Mode 对象来将 AddPicture 模式添加到场景中,如下所示:
-
在
AddPicture Mode。 -
将
AddPictureMode脚本从 Project 窗口拖动到 AddPicture Mode 对象上,将其添加为组件。 -
将 AR Session Origin 对象从 Hierarchy 拖动到 Add Picture Mode | Raycaster 插槽。
-
在 Project 窗口中定位你的 FramedPhoto Prefab 资产,并将其拖动到 Add Picture Mode | Placed Prefab 插槽。现在 AddPicture Mode 组件看起来如下(注意,此截图还显示了我们在本章末尾添加到脚本中的两个更多参数,Image Data 和 Default Scale):
![图 6.6 – 场景中添加了 AddPicture 模式]()
图 6.6 – 场景中添加了 AddPicture 模式
-
现在我们将模式添加到 Interaction Controller。在 Hierarchy 中选择 Interaction Controller 对象。
-
在 Inspector 中,在 Interaction Controller 组件的右下角点击 + 按钮向 Interaction Modes 字典添加一个项。
-
在 Id 字段中输入
AddPicture。 -
将 AddPicture Mode 游戏对象从 Hierarchy 拖动到 Value 插槽。交互控制器组件现在看起来如下:

图 6.7 – 在交互模式字典中添加了 AddPicture 模式的交互控制器
现在我们有一个 Addpicture 模式,当用户点击 Add 按钮时将从 Main 模式启用。现在让我们创建这个按钮。
创建主菜单添加按钮
当应用处于主模式时,Main UI 面板显示。在这个面板上,我们将有一个 Add 按钮供用户在想要在场景中放置新图片时按下。我将使用一个大的加号作为其图标,以下步骤:
-
在 Hierarchy 窗口中,展开 UI Canvas 对象,然后展开其子 Main UI 对象。
-
面板中的默认子文本是一个临时占位符;我们可以将其删除。右键点击 子 Text 对象并选择 Delete。
-
现在我们添加一个按钮。右键单击
Add Button。 -
选择添加按钮,在其检查器窗口中使用锚点菜单(左上角)选择底右锚点。然后按Shift + Alt +点击底右以在该角落设置其枢轴和位置。
-
调整按钮大小和位置,可以使用以下截图中的矩形工具(
175, 175),和-30, 30),如下所示:![图 6.8 – 添加按钮矩形变换设置]()
图 6.8 – 添加按钮矩形变换设置
-
在层次结构窗口中,通过点击其三角形图标展开添加按钮,并选择其子对象,文本(TMP)。
-
设置其
+并设置其192。 -
您可以添加另一个文本元素来标记按钮。右键单击
Add,24,55。我们现在按钮看起来如下:
![图 6.9 – 添加按钮]()
图 6.9 – 添加按钮
-
要设置按钮以启用放置图片模式,在层次结构中选择添加按钮。在其检查器中,在按钮组件的OnClick部分,按下底部的+按钮以添加事件动作。
-
从层次结构中拖动交互控制器并将其放置在OnClick 动作的对象槽中。
-
在函数选择列表中,选择交互控制器 | 启用模式。
-
在其字符串参数字段中,输入文本
AddPicture。
现在的OnClick属性看起来如下:

图 6.10 – 当点击添加按钮时,它调用 EnableMode("AddPicture")
我们现在已将AddPicture 模式添加到我们的框架中。当点击添加按钮时,交互控制器将启用它。启用后,脚本显示AddPicture指导 UI,然后等待放置对象输入动作事件。然后它使用射线投射来确定用户想在 3D 空间中的哪个位置放置对象,实例化预制体,然后返回主模式。让我们试试。
构建和运行
保存场景。如果您想尝试并查看其外观,现在可以构建和运行,如下所示:
-
选择文件 | 构建设置。
-
点击
ARGallery)是否已在场景在构建列表中。 -
确保在场景在构建列表中只选中了
ARGallery场景。 -
点击构建和运行以构建项目。
应用程序将启动并提示您扫描房间。慢慢移动您的设备以扫描房间,集中注意您想放置照片的墙壁大致区域。
什么因素有助于良好的平面检测?
当 AR 平面检测使用设备内置的白色光摄像头扫描 3D 环境时,它依赖于摄像头图像的良好视觉保真度。房间应该光线充足。被扫描的表面应该具有独特且随机的纹理,以帮助检测软件。例如,如果您的墙壁太光滑,我们的 AR 画廊项目可能难以检测垂直平面。(较新的设备可能包括其他传感器,例如基于激光的LIDAR深度传感器,这些传感器不受这些限制)。如果您的设备在检测垂直墙面平面时遇到困难,请尝试在墙上战略性地添加一些贴纸或其他标记,使表面对软件更具辨识度。
当至少检测到一个垂直平面时,扫描提示将消失,你将看到主 UI 的添加按钮。点击添加按钮将启用添加图片模式,显示带有点击放置说明图形的添加图片UI 面板。当你点击一个跟踪到的平面时,FramedPhoto预制件将在场景中实例化。以下是我的样子,在左侧:
![图 6.11 – 在墙上放置一个物体(左)和校正表面法线和垂直(右)
![图 6.11 – 在墙上放置一个物体(左)和校正表面法线和垂直(右)
图 6.11 – 在墙上放置一个物体(左)和校正表面法线和垂直(右)
哎呀!图片垂直地从墙上突出出来,如前一张截图(左侧所示)。我们希望它像右侧图像中的墙上的画一样悬挂。让我们更新脚本以处理这个问题。
完成 AddPictureMode 脚本
我们需要实施一些改进来完成AddPictureMode脚本的编写,包括以下内容:
-
将图片旋转,使其垂直并平贴在墙面平面上。
-
告诉图片在其框架中显示哪张图片。
-
当图片首次放置在墙上时,包括一个默认的缩放比例。
AddPictureMode脚本中包含以下设置旋转为hitPose.rotation的代码行:
Instantiate(placedPrefab, hitPose.position, hitPose.rotation);
如您在上一张截图中所见,跟踪平面的“向上”方向垂直于平面表面,因此,使用此代码图片看起来像是从墙上突出出来。对于水平平面,您希望物体站立在地板或桌子上,因此使用此默认向上方向实例化放置的物体是有意义的。但在本项目,我们不想这样做。我们希望图片与墙面朝向相同。我们希望它垂直向上/向下悬挂。
我们不应该使用hit.pose.rotation,而应该使用平面的法线向量(pose.up)来计算旋转。然后我们调用Quaternion.LookRotation函数来创建具有指定前进和向上方向的旋转(见docs.unity3d.com/ScriptReference/Quaternion.LookRotation.html)。
四元数
四元数是一种可以用于表示计算机图形中旋转的数学结构。作为 Unity 开发者,你只需要知道 Transform 中的旋转使用Quaternion类。参见docs.unity3d.com/ScriptReference/Quaternion.html。然而,如果你想要了解底层数学的解释,可以查看3Blue1Brown的精彩视频,例如在www.youtube.com/watch?v=zjMuIxRvygQ上互动解释的四元数和 3D 旋转。
我们还需要的是能够告诉 FramedPhoto 显示哪张图片的能力。我们将为imageInfo添加一个公共变量,它将由 Image Select 菜单(在本章下一节开发)设置。
此外,我们还将添加一个defaultScale属性,在实例化图片时对其进行缩放。如果你还记得,我们定义的预制件是按 1 单位最大尺寸归一化的,这会使它在墙上宽 1 米,除非我们对其进行缩放。我们只缩放 X 和 Y 轴,将 Z 轴保留为1.0,这样框架的深度就不会被缩放太多。我将默认缩放设置为0.5,但你可以稍后在检查器中更改它。
按照以下方式修改AddPictureMode脚本:
-
在类的顶部添加以下声明:
public ImageInfo imageInfo; [SerializeField] float defaultScale = 0.5f; -
将
PlaceObject函数替换为以下内容:void PlaceObject(Vector2 touchPosition) { if (raycaster.Raycast(touchPosition, hits, TrackableType.PlaneWithinPolygon)) { ARRaycastHit hit = hits[0]; Vector3 position = hit.pose.position; Vector3 normal = -hit.pose.up; Quaternion rotation = Quaternion.LookRotation (normal, Vector3.up); GameObject spawned = Instantiate(placedPrefab, position, rotation); FramedPhoto picture = spawned.GetComponent<FramedPhoto>(); picture.SetImage(imageInfo); spawned.transform.localScale = new Vector3(defaultScale, defaultScale, 1.0f); InteractionController.EnableMode("Main"); } } -
保存脚本并返回 Unity。
注意,我必须取反墙面的法向量(-hit.pose.up),因为在创建我们的预制件时,按照惯例,图片是朝向负 Z 方向的。
当你放置一张图片时,它现在应该垂直悬挂并紧贴墙面,如本节顶部截图的右侧面板所示。
在添加图片模式下显示追踪平面
另一个可能的增强是,在主模式下隐藏追踪的平面,在添加图片模式下显示它们。这将使用户能够在没有这种干扰的情况下享受他们的图片库。查看我们在第五章的在不需要时隐藏追踪对象主题中是如何做到的,使用AR 用户框架。当时,我们编写了一个脚本ShowTrackablesOnEnable,现在我们也可以使用它。按照以下步骤操作:
-
在层次结构中(在交互控制器下)选择添加图片模式游戏对象。
-
在
ShowTrackablesOnEnable脚本中,并将其拖动到添加图片模式对象上。 -
从层次结构中,将AR 会话原点对象拖动到检查器中,并将其放置在启用时显示可追踪对象 | 会话原点槽位上。
这就是我们实现这个功能所需的所有内容。
回顾一下,我们配置了场景以检测和跟踪垂直平面,用于你的房间墙壁。然后我们创建了一个 AddPictureMode 脚本。当用户在垂直平面上轻触时,该脚本实例化一个 FramedPhoto 预制件的副本。然后我们通过确保图片在墙上平放且直立来改进脚本。该脚本还允许我们更改框架中的图像及其缩放。最后,我们在 AddPicture 模式下显示可跟踪的平面,并在返回到主模式时隐藏它们。
下一步是为用户提供在墙上挂新画之前选择图像的选择。我们现在可以继续创建一个图像选择菜单,供用户选择使用。
选择要使用的图像
我们接下来要做的事情是创建一个包含图像按钮的图像选择菜单,用户可以在将其添加到场景之前选择一张照片。当使用你已添加到项目中的 Images 列表(图像数据 游戏对象)构建菜单的 ImageButtons 脚本时。然后我们将 SelectImage 模式插入到 AddPicture 模式之前,这样选中的图像就是放置在墙上的图像。让我们首先定义 SelectImage 模式。
创建 SelectImage 模式
当用户启用 SelectImage 模式时,我们只需要显示 SelectImage UI 菜单面板,其中包含供用户选择的按钮。点击按钮将通过调用公共函数 SetSelectedImage 通知模式脚本,该函数反过来告诉 AddPictureMode 使用哪张图像。
创建一个名为 SelectImageMode 的新 C# 脚本,并按照以下内容编写:
using UnityEngine;
public class SelectImageMode : MonoBehaviour
{
void OnEnable()
{
UIController.ShowUI("SelectImage");
}
}
简单。当 SelectImageMode 被启用时,我们显示 SelectImage UI 面板(包含按钮菜单)。
现在,我们可以按照以下方式将其添加到 Interaction Controller:
-
在
SelectImage 模式中。 -
将
SelectImageMode脚本从 Project 窗口拖到 SelectImage Mode 对象上,将其添加为组件。 -
现在,我们将此模式添加到 Interaction Controller。在 Hierarchy 中选择 Interaction Controller 对象。
-
在 Inspector 中,在 Interaction Controller 组件的右下角,点击 + 按钮向 Interaction Modes 字典添加一个项。
-
在 Id 字段中输入
SelectImage。 -
将 SelectImage Mode 游戏对象从 Hierarchy 拖到 Value 槽。现在 Interaction Controller 组件看起来如下所示:
.jpg)
图 6.12 – 添加了 SelectImage 模式的交互控制器
接下来,我们将添加此模式的 UI。
创建 Select Image UI 面板
要创建 SelectImage UI 面板,我们将复制现有的 Main UI 并进行适配。该面板将包括一个标题和 取消 按钮。按照以下步骤操作:
-
在
SelectImage UI中。使用 右键点击 删除 删除任何子对象,包括 添加按钮。 -
将面板大小设置为略小于全屏,使其看起来像模态弹出窗口。在
50处设置,将150留出空间用于应用标题。 -
我们希望这个面板有一个实心背景,因此在其图像组件中,从主菜单中选择组件 | UI | 图像。
-
创建一个菜单标题子面板。在
Header。 -
定位并拉伸
100。 -
右键点击标题文本。
-
在
Select Image,48,对齐设置为居中和中间,锚点预设设置为拉伸-拉伸。同时,Alt + Shift + 点击拉伸-拉伸。 -
我们还将添加一个取消按钮。
-
设置取消按钮的
80和-20。还将其图像 | 颜色设置为浅灰色。 -
对于
X和48。 -
在层次结构中选择取消按钮,在其检查器中点击按钮 | OnClick动作右下角的+按钮。
-
将交互控制器游戏对象从层次结构拖放到OnClick 对象槽中。
-
从文本参数字段中的
Main。现在,X 取消按钮将带您返回主模式。
SelectImage UI 面板的标题如下截图所示:
![图 6.13 – SelectImage UI 的标题面板]
![img/Figure_6.13-SelectImageHeader.jpg]
图 6.13 – SelectImage UI 的标题面板
接下来,我们将添加一个面板来包含将显示供用户选择的图片的图像按钮。这些按钮将按网格排列。使用以下步骤:
-
在
Image Buttons。 -
在图像按钮面板上,取消选中其图像组件或删除它。我们不需要单独的背景。
-
其
100。 -
选择
layout并添加一个网格布局组组件。 -
在
20, 20, 20, 20处设置200, 200,并将20, 20设置为20, 20。将子对齐设置为上居中。
现在我们有一个带有标题和图像按钮容器的ImageSelect UI面板。当前层次结构的一部分如下截图所示:
![图 6.14 – 带有 SelectImage UI 的 UI 画布和具有 SelectImage 模式的交互控制器]
![img/Figure_6.14-uicanvas-selectimage-hier.jpg]
图 6.14 – 带有 SelectImage UI 的 UI 画布和具有 SelectImage 模式的交互控制器
最后,我们需要按照以下步骤将面板添加到 UI 控制器中:
-
要将面板添加到 UI 控制器中,在层次结构中选择UI Canvas对象。
-
在检查器中,在UI 控制器组件的右下角,点击+按钮向UI 面板字典中添加一个项目。
-
在Id字段中输入
SelectImage。 -
将SelectImage UI游戏对象从层次结构拖放到值槽中。
现在我们有一个包含图像按钮容器的 UI 面板。为了制作按钮,我们将创建一个预制件,然后编写一个脚本以填充图像按钮面板。
创建图像按钮预制件
我们将定义一个图像按钮为预制件,以便它可以复制,为用户在选择菜单中提供的每个图像。创建按钮如下:
-
在
Image Button中。 -
在图像按钮下,删除其子文本元素。
-
在图像按钮上,移除其图像组件(使用右键单击和移除组件),然后按添加组件。搜索并添加一个原始图像组件。
-
其按钮组件需要对其刚刚替换的图形的引用。在检查器中,将原始图像组件拖到按钮 | 目标图形槽中。
-
现在将默认图像纹理,例如
WinterBarn资产,从Photos/文件夹拖到检查器,并将其放置在原始图像 | 纹理槽中。UI 图像与原始图像
图像组件用于其图形的图像精灵。原始图像组件用于其图形的纹理。精灵是用于 UI 和 2D 应用的小型、高效、预处理的图像。纹理通常更大,具有更多的像素深度和保真度,用于 3D 渲染和摄影图像。您可以使用图像文件的检查器属性在导入的图像之间以及其他类型之间进行更改。为了同时使用相同的照片资产(PNG 文件)作为 FramedPhoto 预制件和按钮,我们在按钮上使用了一个原始图像组件。
-
让我们保存
Prefabs/文件夹。这会创建一个预制资产,并将其在层次结构中的颜色改为蓝色,表示它是一个预制实例。 -
在层次结构窗口中,右键单击图像按钮对象,选择复制(或按Ctrl/Option + D键),并制作几个副本。因为按钮由具有网格布局组的Image Buttons面板作为父级,所以它们以网格形式渲染,如下面的截图所示:

图 6.15 – 带有网格布局的图像按钮的图像选择面板
接下来,我们将编写一个脚本,用我们想要使用的实际图像填充按钮。
编写 ImageButtons 脚本
ImageButtons脚本将是ImageButtons上的一个组件,打开它进行编辑,并按照以下方式编写:
using UnityEngine;
using UnityEngine.UI;
public class ImageButtons : MonoBehaviour
{
[SerializeField] GameObject imageButtonPrefab;
[SerializeField] ImagesData imagesData;
[SerializeField] AddPictureMode addPicture;
void Start()
{
for (int i = transform.childCount - 1; i >= 0; i--)
{
GameObject.Destroy( transform.GetChild(i).gameObject);
}
foreach (ImageInfo image in imagesData.images)
{
GameObject obj = Instantiate(imageButtonPrefab,transform);
RawImage rawimage = obj.GetComponent<RawImage>();
rawimage.texture = image.texture;
Button button = obj.GetComponent<Button>();
button.onClick.AddListener(() => OnClick(image));
}
}
void OnClick(ImageInfo image)
{
addPicture.imageInfo = image;
InteractionController.EnableMode("AddPicture");
}
}
让我们遍历这个脚本。在类的顶部,我们声明了三个变量。imageButtonPrefab将是一个对imagesData对象的引用,该对象包含我们的图像列表。addPicture是对每个按钮的AddPictureMode的引用,以告诉哪个图像已被选中。
Start()首先做的事情是清除按钮面板中的任何子对象。例如,我们创建了许多按钮的副本以帮助我们开发和可视化面板,当它运行时,除非我们首先删除它们,否则它们仍然会出现在场景中。
然后,Start遍历每个图像,并为每个图像创建一个onClick事件。
当其中一个按钮被点击时,我们的OnClick函数将被调用,并将该按钮的image作为参数传递。我们将此image数据传递给AddPictureMode,该模式将在AddPictureMode实例化新的FramedPhoto对象时使用。
按照以下方式将脚本添加到场景中:
-
在层次结构中,选择图像按钮对象(位于UI Canvas/选择图片面板)。
-
将ImageButtons脚本拖动到Image Buttons对象上,使其成为一个组件。
-
从项目窗口中,将Image Button预制拖动到检查器中,并将其放置在Image Buttons | Image Button Prefab槽位上。
-
从层次结构中,将AddPicture Mode对象拖动到检查器中,并将其放置在Image Buttons | Add Picture槽位上。
-
同样从层次结构中,将Images Data对象拖动到Image Buttons | Images Data槽位上。
Image Buttons组件现在看起来如下截图所示:
![Figure 6.16 – Image buttons panel with the ImageButtons script that builds the menu at runtime]
![Figure 6.16-imagebuttons-insp.jpg]
图 6.16 – 带有在运行时构建菜单的 ImageButtons 脚本的图像按钮面板
好的。当应用启动时,AddPictureMode选择了哪张图片,然后启用了添加图片模式。
重定向添加按钮
在我们尝试之前,还有最后一步。目前,主菜单的添加按钮直接启用 AddPicture 模式。我们需要将其更改为调用SelectImage,如下所示:
-
在层次结构中,选择添加按钮(位于UI Canvas / 主 UI)。
-
在检查器中,在按钮 | 点击动作列表中,将EnableMode参数从AddPicture更改为SelectImage。
-
保存场景。
如果你把这些都做对了,你应该能够构建并运行场景,并运行完整的场景:按下添加按钮将显示一个选择图片菜单。点击一个图片,选择面板将被替换为提示点击以将图片及其框架放置在墙上的提示。以下是我手机上的截图显示了左边的选择图片菜单。选择图片并放置在墙上后,结果在右边显示。然后应用返回主菜单:
![Figure 6.17 – Pressing the Add button, I see an image menu (left), and the result after placing (right)]
![Figure 6.17-PlaceImageRun.jpg]
图 6.17 – 按下添加按钮,我看到一个图像菜单(左),放置后的结果(右)
总结一下,在本节中,我们添加了ImageButtons脚本,为应用中要包含的每个图片实例化按钮。点击其中一个按钮会将所选图片数据传递给AddPicture模式。当用户点击放置并实例化FramedPhoto时,我们将图片设置为用户所选的图片。我们还菜单中包含了一个取消按钮,以便用户可以取消添加操作。
目前看起来还不错。我们有一个问题是所有图片都在相同大小的横幅框架中渲染,因此可能看起来扭曲。让我们修复这个问题。
调整图像宽高比
目前,我们正在忽略图像的实际大小,并将所有图像都调整到 3:4 的宽高比,使其适应横幅方向。幸运的是,我们在ImageInfo中包含了图像的实际(原始)像素尺寸。我们现在可以使用它来相应地缩放图片。我们可以将这个更改应用到位于FramedPhoto预制件上的FramedPhoto脚本中。
计算宽高比的算法可以作为一个实用函数在ImagesData脚本中分离。打开ImagesData脚本并添加以下代码:
public static Vector2 AspectRatio(float width, float height)
{
Vector2 scale = Vector2.one;
if (width == 0 || height == 0)
return scale;
if (width > height)
{
scale.x = 1f;
scale.y = height / width;
}
else
{
scale.x = width / height;
scale.y = 1f;
}
return scale;
}
当width大于height时,图片是横幅,我们将保持 X 缩放为1.0并缩小 Y。当height大于width时,它是肖像,我们将保持 Y 缩放为1.0并缩小 X。如果它们相同或为零,我们返回(1,1)。该函数被声明为static,因此可以使用ImagesData类名来调用它。
打开FramedPhoto脚本进行编辑,并对以下内容进行修改:
public void SetImage(ImageData image)
{
imageData = image;
Renderer renderer = imageObject.GetComponent<Renderer>();
Material material = renderer.material;
material.SetTexture("_BaseMap", imageData.texture);
AdjustScale();
}
public void AdjustScale()
{
Vector2 scale = ImagesData.AspectRatio(imageInfo.width, imageInfo.height);
scalerObject.localScale = new Vector3(scale.x, scale.y, 1f);
}
如果你记得,SetImage函数在ImageData.AspectRatio之后立即由AddPictureMode调用,以获取新的局部缩放并更新scalerObject变换。
当图片不是正方形时,你可能注意到框架的宽度在水平方向和垂直方向上略有不同。解决这个问题需要额外调整1.0 – 0.01/aspectratio。我将把这个实现的细节留给你。
当你再次运行项目并在墙上放置图片时,它将根据你选择的图片具有正确的宽高比。你可以添加的一个改进是调整选择图片面板按钮上的图像大小,使它们也不会被压缩。我将这个练习留给你。
摘要
在本章的开头,我给你提供了这个增强现实画廊项目的需求和计划,包括项目目标、用例、用户体验设计和用户故事。你开始使用在第四章**,创建 AR 用户框架中创建的ARFramework模板进行实现,并在此基础上实现将带框照片放置在墙上等新功能。
为了实现这个功能,你创建了一个你创建的ImageButton预制件。点击图片后,你会被提示在 AR 追踪的墙上轻触,然后在该墙上放置一个新的带框照片,并正确缩放以适应图像的宽高比。
我们现在有一个有趣项目的良好开端。还有很多工作可以完成。例如,目前图片可以重叠放置,这将是错误的。此外,能够移动、调整大小和删除图片会更好。我们将在下一章中添加这个功能。
第七章:第七章:画廊:编辑虚拟对象
在本章中,我们将继续构建我们在第六章中开始的项目,画廊:构建 AR 应用程序,在那里我们创建了一个 AR 画廊,允许用户将虚拟相框照片放置在他们的现实世界墙壁上。在本章中,我们将构建更多与交互和编辑已添加到场景中的虚拟对象相关的功能。具体来说,我们将允许用户选择一个对象进行编辑,包括移动、调整大小、删除和替换相框中的图像。在这个过程中,我们将添加新的输入动作,利用 Unity 碰撞检测,并看到更多使用 Unity API 的 C# 编码技术。
本章中,我们将涵盖以下主题:
-
检测碰撞以避免相交对象
-
构建编辑模式和编辑菜单 UI
-
使用物理射线投射选择对象
-
添加触摸输入动作以拖动移动和捏合缩放
-
C# 编码和 Unity API,包括碰撞钩子和矢量几何
到本章结束时,你将拥有一个具有许多用户交互功能的运行中的 AR 应用程序。
技术要求
要完成本章的项目,你需要在你的开发计算机上安装 Unity,并将其连接到一个支持增强现实应用程序的移动设备(有关说明,请参阅第一章,为 AR 开发设置)。我们还将假设你已经创建了我们在第六章,画廊:构建 AR 应用程序中开始创建的 ARGallery 场景。你将在 技术要求 部分找到有关该场景的详细信息。你可以在本书的 GitHub 存储库中找到该场景,以及我们将在本章中构建的场景,网址为 github.com/PacktPublishing/Augmented-Reality-with-Unity-AR-Foundation。
注意,在本书的存储库中,本章的一些脚本(和类)已被后缀加上 07,例如 AddPictureMode07,以区分它们与为上一章编写的相应脚本。在你自己的项目中,当你编辑本章中描述的现有脚本时,可以保留未后缀的名称。
创建编辑模式
要开始本章,你应该在 Unity 中打开ARGallery场景,我们在第六章的末尾停止,画廊:构建 AR 应用。为了回顾,启动应用后,它首先初始化 AR 会话并扫描以检测真实世界环境中的特征。一旦检测到垂直平面(墙壁),主菜单将显示出来。在这里,用户可以轻触添加按钮,这将打开一个图像选择菜单,用户可以从中选择要使用的照片。然后,用户将被提示轻触可追踪的垂直平面以放置框架照片。一旦照片挂在他们的墙上,用户将返回主模式。
在本章中,我们将允许用户修改已添加到场景中的现有虚拟框架照片。第一步是用户从主模式中选择要编辑的现有对象,然后激活所选对象的编辑图片模式。当对象被选中并进行编辑时,应该突出显示,以便明显知道哪个对象已被选中。
使用为本书开发的 AR 用户框架,我们将首先向场景中添加一个 EditPicture 模式 UI。首先,我们将创建编辑菜单用户界面,包括用于各种编辑功能的多个按钮,以及一个用于管理的 Edit 模式控制器脚本。
创建编辑菜单 UI
要创建放置图片的 UI,我们将创建一个新的EditPicture UI面板。复制现有的Main UI并适应它会更简单。执行以下步骤:
-
在
EditPicture UI中,通过右键单击| 删除删除任何子对象,包括添加按钮。 -
通过在
编辑菜单上右键单击来创建菜单的子面板。 -
使用
175。 -
我将我的背景设置为
55。 -
选择
布局,然后选择水平布局组。 -
在水平布局组组件上,勾选控制子大小 | 宽度和高度复选框。(将其他选项保留为默认值,使用子缩放未勾选,子强制扩展勾选)。在检查器窗口中,编辑菜单面板看起来如下所示:
![图 7.1 – 编辑菜单面板设置]()
图 7.1 – 编辑菜单面板设置
-
现在,我们将向菜单中添加四个按钮。首先,在
替换图像按钮上右键单击。 -
选择其子文本对象,设置
替换图像并将48。 -
在替换图像按钮上右键单击并选择复制(或Ctrl + D)。重复此操作两次,以便总共有四个按钮。
-
重命名按钮并更改按钮上的文本,使其显示为
替换框架、移除图片和完成。 -
我们可能不会很快使用替换框架功能,因此通过在按钮组件中取消勾选其交互性复选框来禁用该按钮。结果菜单将如下所示:

图 7.2 – 编辑菜单按钮
按照以下步骤将面板添加到 UI 控制器中:
-
要将面板添加到 UI 控制器中,在 层次结构 窗口中选择 UI Canvas 对象。
-
在 检查器 窗口中,在 UI 控制器 组件的右下角,点击 + 按钮向 UI 面板字典中添加一个条目。
-
在 Id 字段中输入
EditPicture。 -
将 EditPicture UI 游戏对象从 层次结构 窗口拖动到 值 插槽。
下一步是创建一个 EditPicture 模式对象和控制器脚本。
创建 EditPicture 模式
如你所知,我们的框架通过激活交互控制器下的游戏对象来管理交互模式。每个模式都有一个控制脚本,用于显示该模式的 UI 并处理任何用户交互,直到满足某些条件;然后,它将过渡到不同的模式。就我们的 EditPicture 模式而言,其控制脚本将有一个 currentPicture 变量,用于指定正在编辑的图片,一个 DoneEditing 函数,用于将用户返回到主模式,以及其他功能。
创建一个新的 C# 脚本,命名为 EditPictureMode 并开始编写,如下所示:
using UnityEngine;
public class EditPictureMode : MonoBehaviour
{
public FramedPhoto currentPicture;
void OnEnable()
{
UIController.ShowUI("EditPicture");
}
}
现在,我们可以将其添加到我们的 交互控制器 对象中,如下所示:
-
在
EditPicture Mode。 -
将
EditPictureMode脚本从 项目 窗口拖动到 EditPicture Mode 对象上,将其添加为组件。 -
现在,我们将模式添加到交互控制器中。在 层次结构 窗口中,选择 交互控制器 对象。
-
在 检查器 窗口中,在 交互控制器 组件的右下角,点击 + 按钮向 交互模式 字典中添加一个条目。
-
在 Id 字段中输入
EditPicture。 -
将 EditPicture Mode 游戏对象从 层次结构 窗口拖动到 值 插槽。
这样,我们就创建了一个 UIController。在此之后,我们创建了一个由 InteractionController 控制的 EditPictureMode 脚本。
使用这个设置,接下来我们必须增强主模式,使其能够检测用户是否点击了现有的 FramedPhoto,并可以为选定的对象启动 EditPicture 模式。
选择要编辑的图片
当处于主模式时,用户应该能够点击现有的图片进行编辑。利用 Unity 输入系统,我们将添加一个新的 SelectObject 输入动作。然后,我们将让 MainMode 脚本监听该动作的消息,使用 Raycast 找到被点击的图片,并启用该图片的编辑模式。让我们开始吧!
定义 SelectObject 输入动作
我们将首先通过以下步骤将 SelectObject 动作添加到 AR 输入动作 资产中:
-
在
Assets/Inputs/文件夹中打开它进行编辑(或者使用其 编辑资产 按钮)。 -
在中间的
SelectObject。 -
在最右侧的 属性 部分,选择 动作类型 | 值 和 控制类型 | 向量 2。
-
在中间的动作部分,选择<无绑定>子项。然后,在属性部分,选择路径 | 触摸屏 | 主要触摸 | 位置以将此动作绑定到主屏幕触摸。
-
按保存资产(除非已启用自动保存)。
更新的AR 输入动作资产如图所示:
![图 7.3 – 添加了 SelectObject 动作的 AR 输入动作资产
![img/Figure_7.03-imputaction-selectobject.jpg]
图 7.3 – 添加了 SelectObject 动作的 AR 输入动作资产
虽然我们使用与之前创建的PlaceObject动作相同的触摸屏绑定来定义此动作(触摸屏主要位置),但它具有不同的用途(点击选择与点击放置)。例如,也许在未来,如果你决定使用双击来选择项目而不是单击,你可以简单地更改其输入动作。
现在,我们可以添加这个动作的代码。
替换 MainMode 脚本
首先,因为我们正在偏离ARFramework模板中提供的默认MainMode脚本,我们应该为这个项目创建一个新的、独立的脚本。执行以下步骤以复制和编辑新的GalleryMainMode脚本:
-
在“项目”窗口的“脚本/”文件夹中,选择MainMode脚本。然后,从主菜单栏中选择编辑 | 复制。
-
将新文件重命名为
GalleryMainMode。 -
你会在
MainMode类中看到一个命名空间错误。打开
GalleryMainMode,如图所示:using UnityEngine; using UnityEngine.InputSystem; public class GalleryMainMode : MonoBehaviour { void OnEnable() { UIController.ShowUI("Main"); } } -
保存脚本。然后,在 Unity 中,在层次窗口中,选择Main Mode游戏对象(在交互控制器下)。
-
将GalleryMainMode脚本拖放到Main Mode对象上,添加为新组件。
-
从Main Mode对象中移除之前的Main Mode组件。
现在,我们准备好增强主模式的行为。
选择主模式下的对象
当用户点击屏幕时,GalleryMainMode脚本将获取触摸位置并使用 Raycast 来确定是否选择了PlacedPhoto对象之一。如果是,它将启用该图片的编辑图片模式。
我们之前在点击放置脚本中看到过 Raycasts,包括AddPictureMode。在那个例子中,我们的脚本使用了Physics.Raycast函数(docs.unity3d.com/ScriptReference/Physics.Raycast.html)。作为 Unity 物理系统的一部分,它要求射线投射对象具有Collider(FramedPhoto具有,我很快会向你展示)。
此外,我们还将使用 AR 摄像头的ScreenPointToRay函数来定义与将要进行 Raycast 的触摸位置相对应的 3D 射线。
要添加此功能,打开GalleryMainMode脚本进行编辑,并按照以下步骤操作:
-
我们将监听输入系统事件,因此首先需要为该命名空间添加一个
using语句。确保以下行位于文件顶部:using UnityEngine.InputSystem; -
我们需要一个引用来告诉
EditPictureMode要编辑哪个对象。将其添加到类的顶部,如下所示:public class GalleryMainMode : MonoBehaviour { [SerializeField] EditPictureMode editMode; -
我们将使用
Camera.main快捷方式。(这要求 AR 相机被标记为MainCamera,这应该从场景模板中完成。)在类的顶部添加一个私有变量,并使用Start进行初始化:Camera camera; void Start() { camera = Camera.main; } -
现在我们来处理任务的主体部分 - 添加以下
OnSelectObject和FindObjectToEdit函数:public void OnSelectObject(InputValue value) { Vector2 touchPosition = value.Get<Vector2>(); FindObjectToEdit(touchPosition); } void FindObjectToEdit(Vector2 touchPosition) { Ray ray = camera.ScreenPointToRay(touchPosition); RaycastHit hit; int layerMask = 1 << LayerMask.NameToLayer("PlacedObjects"); if (Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask)) { FramedPhoto picture = hit.collider. GetComponentInParent<FramedPhoto>(); editMode.currentPicture = picture; InteractionController. EnableMode("EditPicture"); } }
让我们一起分析这段代码。当使用SelectObject输入系统动作时,OnSelectObject函数会自动调用(On前缀是 Unity 中事件接口的标准约定)。它从输入值中获取Vector2 touchPosition并将其传递给我们的私有FindObjectToEdit函数。(您不需要将其分成两个函数,但我这样做是为了清晰。)
FindObjectToEdit通过调用camera.ScreenPointToRay获取与触摸位置对应的 3D 射线。将其传递给Physics.Raycast以在场景中找到一个与射线相交的对象。我们不会对每个可能的对象进行投射,而是将其限制在名为PlacedObjects的图层上,使用其layermask。(为此,我们需要确保FramedPhoto被分配到这个图层,我们很快就会这样做。)
信息 - 图层名称、图层编号和图层遮罩
通过将LayerMask.NameToLayer左移多次来管理项目中的图层并查看每个图层编号分配了什么名称。要管理项目中的图层并查看每个图层编号分配了什么名称,请点击编辑器右上角的图层按钮。
如果射线投射命中,我们必须获取预制体中FramedPhoto组件的引用并将其传递给EditPictureMode组件。然后,应用将过渡到编辑图片模式。
保存脚本。现在,让我们修复我提到的游戏对象上的家务事:将相机标签设置为MainCamera,设置PlacedObjects图层,并确保FramedPhoto具有碰撞器组件。在 Unity 中,执行以下操作:
-
在层次结构窗口中,选择主模式游戏对象,将编辑图片模式对象从层次结构窗口拖动到检查器窗口,并将其放置在画廊主模式 | 编辑模式槽中。
-
在场景层次结构中,展开AR 会话原点并选择其子AR Camera。在检查器窗口的左上角,验证标签(位于检查器窗口顶部)是否设置为MainCamera。如果不是,现在设置它。
-
接下来,通过在项目窗口中双击资产来打开FramedPhoto预制体进行编辑。
-
在其根
PlacedObjects。如果名为
PlacedObjects的图层不存在,选择PlacedObjects并将其分配给一个空槽。在PlacedObjects中。你将随后被提示问题你想要将所有子对象层设置为 PlacedObjects 吗?。点击是,更改子对象。
-
当我们在这里时,让我们也验证预制件是否有一个碰撞器,这是
Physics.Raycast所必需的。如果你还记得,当我们构建预制件时,我们从一个空的游戏对象作为根开始,并为AspectScaler添加了另一个空子对象。然后,我们为框架对象添加了一个 3D 立方体。点击这个框架对象。 -
在检查器窗口中,你会看到框架对象已经有一个盒子碰撞器。完美。注意,如果你按下它的编辑碰撞器按钮,你可以在场景窗口中看到(并编辑)碰撞器的形状,如图所示,其边缘被勾勒出来,并且有一些小把手可以移动面。但在这里我们不需要改变它:
![图 7.4 – 编辑框架对象的盒子碰撞器]()
图 7.4 – 编辑框架对象的盒子碰撞器
-
保存预制件并退出预制件编辑器回到场景层次结构。
如果你现在要构建并运行场景,然后向墙上添加一张图片,当你点击那张图片时,它应该隐藏主菜单并显示编辑菜单。现在,我们需要一种方法从编辑模式回到主模式。让我们连接完成按钮。
连接完成编辑按钮
在本节中,我们将设置InteractionController中的EnableMode。按照以下步骤操作:
-
在层次结构窗口中,选择完成按钮,它应该位于UI Canvas | EditPicture UI | Edit Menu下。
-
在检查器窗口中,点击按钮 | OnClick区域右下角的+按钮添加一个新的事件动作。
-
从层次结构窗口中将Interaction Controller对象拖动到OnClick动作的对象槽中。
-
在函数选择列表中,选择InteractionController | EnableMode。
-
在模式字符串参数槽中输入
Main。
现在,如果你构建并运行的场景中有一个图片实例化,并点击图片,你会切换到编辑模式并看到编辑菜单。点击完成按钮回到主模式。
这是个进步。但如果你的墙上有多张图片,当前正在编辑的是哪一张并不明显。我们需要突出显示当前选中的图片。
突出显示选中的图片
在 Unity 中有许多方法可以突出显示对象,以指示用户已选择该对象。通常,你会发现自定义着色器可以做到这一点(在资源商店中有很多)。决策取决于你想要的“外观”。你想要改变所选对象的着色,绘制线框轮廓,还是创建其他效果?为了保持简单,我将只介绍一个“高亮”游戏对象,在FramedPhoto预制件中作为一个从框架边缘延伸的细黄色框。让我们现在就来做这个:
-
通过在项目窗口中双击它来编辑FramedPhoto预制件。
-
在
Highlight中。 -
设置其
1.05, 1.05, 0.005),使其变薄并延伸到框架的边缘。 -
设置其
0, 0, -0.025)。 -
创建一个黄色材质。在
Materials/文件夹中(如果需要则创建一个)并选择Highlight Material。 -
设置高亮材质 | 着色器 | 通用渲染管线 | 无光照。
-
将其基础图颜色(使用颜色样本)设置为黄色。
-
将高亮材质拖放到高亮游戏对象上。现在场景视图应该如下所示:
![图 7.5 – 带有高亮的框架照片]

图 7.5 – 带有高亮的框架照片
我们现在可以从FramedPhoto脚本中控制这个。你可能出于不同的原因想要突出显示图片,但在这个项目中,我决定当对象被选择并突出显示时,这意味着它正在被编辑。因此,我们可以在使对象可编辑时切换高亮。在你的编辑器中打开脚本并做出以下更改:
-
声明一个
highlightObject变量:[SerializeField] GameObject highlightObject; bool isEditing; -
添加一个切换高亮的函数:
public void Highlight(bool show) { if (highlightObject) // handle no object or app end object destroyed highlightObject.SetActive(show); } -
确保图片在开始时不会被突出显示:
void Awake() { Highlight(false); } -
添加一个
BeingEdited函数。当对象正在编辑时,将调用此函数。它将突出显示对象,并在稍后启用其他编辑行为。同样,当我们停止编辑并传递false值时,对象将被取消突出显示:public void BeingEdited(bool editing) { Highlight(editing); isEditing = editing; } -
保存脚本。在 Unity 中,选择根FramedPhoto对象。
-
将高亮对象从层次结构窗口拖动到Framed Photo | 高亮对象槽中。
这很棒!现在,我们可以更新EditPictureMode来告诉图片它是否正在被编辑。打开EditPictureMode脚本并做出以下修改:
-
将
BeingEdited调用添加到OnEnable中:void OnEnable() { UIController.ShowUI("EditPicture"); if (currentPicture) currentPicture.BeingEdited(true); } -
此外,当它不是正在编辑时(即退出编辑模式时),将
BeingEdited调用添加到OnDisable中:void OnDisable() { if (currentPicture) currentPicture.BeingEdited(false); }注意,尽管我们绝不会故意在没有定义
currentPicture的情况下进入编辑模式,但我已经添加了空值检查,以防在应用程序启动或关闭序列中激活或关闭该模式。
如果你现在播放场景并添加一张图片,当你通过主模式触摸图片时,编辑模式将变为启用,图片将被突出显示。当你退出到主模式时,图片将被取消突出显示。
让我们继续。假设你在墙上有多张图片。目前,当你正在编辑一张图片,并想要编辑另一张时,你必须按下EditMode脚本。
从编辑模式中选择对象
当在一个图片的编辑模式下时,为了让用户在不退出编辑模式的情况下选择另一张图片,我们可以使用相同的EditPictureMode脚本进行编辑,并做出以下更改:
-
我们将监听输入系统事件,因此首先,我们需要为该命名空间添加一个
using语句。确保以下行位于文件顶部:using UnityEngine.InputSystem; -
在类的顶部添加一个私有的
camera变量,并在Start方法中初始化它:Camera camera; void Start() { camera = Camera.main; } -
OnSelectObject动作监听器将调用FindObjectToEdit。就像在GalleryMainMode中一样,它对PlacedObjects层进行 Raycast。但现在,我们必须检查它是否击中了除当前图片之外的对象。如果是这样,我们必须停止编辑currentPicture并使新选择成为当前选择:public void OnSelectObject(InputValue value) { Vector2 touchPosition = value.Get<Vector2>(); FindObjectToEdit(touchPosition); } void FindObjectToEdit(Vector2 touchPosition) { Ray ray = camera.ScreenPointToRay(touchPosition); RaycastHit hit; int layerMask = 1 << LayerMask.NameToLayer("PlacedObjects"); if (Physics.Raycast(ray, out hit, 50f, layerMask)) { if (hit.collider.gameObject != currentPicture.gameObject) { currentPicture.BeingEdited(false); FramedPhoto picture = hit.collider. GetComponentInParent<FramedPhoto>(); currentPicture = picture; picture.BeingEdited(true); } } }
总结来说,当你有多个正在编辑的currentPicture对象时。
这里还有一个问题:如果你一直在玩这个项目,你可能已经注意到你可以将图片放在彼此的上面,或者实际上,在里面,因为它们似乎没有任何物理存在!哎呀。让我们修复这个问题。
避免相交的对象
在 Unity 中,为了指定一个对象应参与 Unity 物理系统,你必须向 GameObject 添加一个Rigidbody组件。添加 Rigidbody 会给对象质量、速度、碰撞检测和其他物理属性。我们可以使用它来防止对象相交。在许多游戏和 XR 应用中,Rigidbody 对于将运动力应用到对象上以使它们在碰撞时弹跳非常重要,例如。
在我们的项目中,如果一张图片与另一张图片发生碰撞,它应该简单地让开,这样它们就永远不会相交。但它也应该与墙面保持齐平。尽管 Rigidbody 允许你沿X、Y和Z方向中的任何一个方向约束运动,但这些是正交的世界空间平面,而不是任意角度的墙面平面。最终,我决定在检测到碰撞时手动定位图片,而不是使用物理力。我的解决方案是约束所有图片的位置(和旋转),这样物理力就不会移动它们。然后,我可以用碰撞作为触发器,手动将图片移开。
信息 - 碰撞与触发检测对比
当两个 GameObject 具有OnCollisionEnter、OnCollisionStay和OnCollisionExit以挂钩到这些事件时。
然而,你可以通过将 Collider 标记为OnTriggerEnter、OnTriggerStay和OnTriggerExit来完全禁用 Unity 应用物理力,以挂钩到这些事件。
要将碰撞检测添加到FramedPhoto预制体,请按照以下步骤操作:
-
在项目窗口中,找到并双击****FramedPhoto预制体以打开它进行编辑。
-
确保你在层次结构窗口中选择了根框架照片对象。
-
在
刚体中,并为对象添加一个刚体。 -
展开约束属性并检查所有六个框;即冻结位置:X, Y, Z和冻结旋转:X, Y, Z。
-
取消勾选其使用重力复选框。(这并不是必要的,因为我们已经设置了约束,但我还是喜欢明确这一点。)
-
我们需要一个碰撞体。正如我们所见,框架子对象上有一个。因此,选择框架游戏对象。
-
在检查器窗口中,在其盒子碰撞体组件中,勾选是触发器复选框。
-
为了避免任何问题,禁用(或删除)预制件中的其他碰撞体。具体来说,从图像中删除网格碰撞体,从高亮中删除盒子碰撞体。
现在,我们可以处理碰撞触发器,并在另一张图片在同一空间时将其移开。我们只想确保它沿着墙壁移动。我们可以利用墙壁平面的法向量(垂直于平面表面的向量)也是我们图片预制件的向前方向向量的事实,因为我们最初就是将其放置那里的。此外,我们只想考虑与放置对象平面上的对象(例如,不是 AR 跟踪平面对象)的碰撞。
我的算法确定这张图片与其他相交图片之间的距离,在 3D 中。然后,它通过将距离向量投影到墙壁平面上并缩放它来找到移动这张图片的方向。图片将继续移动,直到不再与框架相交。
让我们编写这段代码。打开FramedPhoto脚本进行编辑并按照以下步骤操作:
-
首先在类的顶部添加对
collider和layer编号的引用,如下所示:[SerializeField] Collider boundingCollider; int layer; -
从其名称初始化
层编号。提前初始化这很好,因为OnTriggerStay可能会每帧被调用:void Awake() { layer = LayerMask.NameToLayer("PlacedObjects"); Highlight(false); } -
我们将在这里使用
OnTriggerStay,它在对象与另一个对象碰撞时每次更新时被调用,如下所示:void OnTriggerStay(Collider other) { const float spacing = 0.1f; if (isEditing && other.gameObject.layer == layer) { Bounds bounds = boundingCollider.bounds; if (other.bounds.Intersects(bounds)) { Vector3 centerDistance = bounds.center - other.bounds.center; Vector3 distOnPlane = Vector3.ProjectOnPlane(centerDistance, transform.forward); Vector3 direction = distOnPlane.normalized; float distanceToMoveThisFrame = bounds.size.x * spacing; transform.Translate(direction * distanceToMoveThisFrame); } } } -
保存脚本。在 Unity 中,从层次结构窗口中将框架对象(具有盒子碰撞体)拖动到框架照片 | 边界碰撞体槽中。框架照片组件现在看起来如下:
![图 7.6 – 框架照片组件属性,包括边界碰撞体]()
图 7.6 – 框架照片组件属性,包括边界碰撞体
-
保存预制件并返回到场景层次结构。
当你现在播放场景时,将一张图片放在墙上,然后在同一空间放置另一张图片,新图片将远离第一张图片,直到它们不再碰撞。
现在我们可以在墙上放置许多图片,你可能想学习如何从场景中删除一张图片。我们将在下一节中探讨这个问题。
删除图片
删除正在编辑的图片很简单。我们只需要销毁currentPicture GameObject 并返回 Main-mode。执行以下步骤:
-
打开
EditPictureMode脚本并添加以下函数:public void DeletePicture() { GameObject.Destroy(currentPicture.gameObject); InteractionController.EnableMode("Main"); } -
保存脚本。
-
在 Unity 中,在Hierarchy窗口中,选择Remove Button(位于UI Canvas | EditPicture UI | Edit Menu下)。
-
在Inspector中,点击Button | OnClick区域右下角的+按钮。
-
将EditPicture Mode对象从Hierarchy窗口拖到OnClick Object槽中。
-
从函数选择中,选择EditPictureMode | DeletePicture。
当您播放场景时,创建一个图片,进入 EditPicture-mode,然后点击Remove Picture按钮,图片将从场景中删除,您将回到 Main-mode。
现在我们有两个编辑菜单按钮正在运行——Remove Picture和Done。现在,让我们添加一个功能,允许您从Image Select菜单面板更改现有FramedPhoto中的图片。
替换图片的图像
当您从主菜单添加图片时,将显示选择图片菜单。从这里,您可以挑选一张图片。此时,您将被提示使用所选图片在场景中添加一个FramedPhoto。我们通过添加一个单独的SelectImage 模式来实现这一点。现在我们希望这个模式能够服务于两个目的。当您在场景中添加新的、带框的图片时,它从 Main-mode 调用,当您想要替换场景中已经存在的带框图片的图片时,它从 EditPicture-mode 调用。这要求我们重构代码。
目前,当我们构建选择图片按钮(在ImageButtons脚本中)时,我们直接配置并启用 AddPicture-mode。相反,现在它需要依赖于 SelectImage-mode 的使用方式,因此我们将从ImageButtons移动到SelectImageMode的代码,如下所示:
-
编辑
SelectImageMode脚本,并在类顶部添加对AddPictureMode的引用:[SerializeField] AddPictureMode addPicture; -
然后,添加一个公共的
ImageSelected函数:public void ImageSelected(ImageInfo image) { addPicture.imageInfo = image; InteractionController.EnableMode("AddPicture"); } -
编辑
ImageButtons脚本,并在类顶部添加对SelectImageMode的引用:[SerializeField] SelectImageMode selectImage; -
然后,将
OnClick代码替换为对ImageSelected的调用,这是我们刚刚编写的:void OnClick(ImageInfo image) { selectImage.ImageSelected(image); }这次重构没有添加任何新功能,但它重构了
SelectImageMode的代码结构,以决定如何使用模态菜单。现在,让我们再次编辑SelectImageMode并添加替换currentPicture图像的支持。 -
在
SelectImageMode脚本顶部添加以下声明:[SerializeField] EditPictureMode editPicture; public bool isReplacing = false; -
然后,更新
ImageSelected函数,如下所示:public void ImageSelected(ImageInfo image) { currentPicture object. Otherwise, it behaves as it did previously for AddPicture-mode.Now, we need to make sure the `isReplacing` flag is set to `false` when *adding* and set to `true` when *replacing*. Again, this requires some refactoring. Currently, the main menu's `SelectImageToAdd` function in the `GalleryMainMode` script. -
在
GalleryMainMode类顶部添加对SelectImageMode的引用:[SerializeField] SelectImageMode selectImage; -
然后,添加一个
SelectImageToAdd函数,如下所示:public void SelectImageToAdd () { selectImage.isReplacing = false; InteractionController.EnableMode("AddPicture"); }我们只需要记得在我们完成之前更新Add按钮的OnClick动作。
-
同样,现在,我们可以在
EditPictureMode脚本中添加一个SelectImageToReplace函数。在类的顶部声明selectImage:[SerializeField] SelectImageMode selectImage;然后,添加以下功能:
public void SelectImageToReplace() { selectImage.isReplacing = true; InteractionController.EnableMode("SelectImage"); }
保存所有脚本。现在,我们需要在 Unity 中连接它们,包括设置添加和替换图片按钮的OnClick动作,然后设置新的SelectImage Mode参数。回到 Unity 中,从添加按钮开始,按照以下步骤操作:
-
在层次结构窗口中,选择位于UI 画布 | Main UI下的添加按钮。
-
从层次结构窗口,将Main Mode游戏对象(位于交互控制器下)拖动到按钮 | OnClick动作的对象槽位。
-
在功能选择器中,选择Gallery Main Mode | 选择添加的图片。
-
现在,我们将连接替换图片按钮,该按钮位于UI 画布 | EditPicture UI | 编辑菜单下。
-
在检查器窗口中,在其按钮组件上,点击OnClick动作右下角的+按钮。
-
从层次结构窗口,将EditPicture Mode游戏对象拖动到OnClick 对象槽位。
-
在功能选择器中,选择Edit Picture Mode | 选择替换的图片。
按钮现在已设置好。我们现在只需分配其他引用。
-
在层次结构窗口中,选择Main Mode游戏对象(位于交互控制器下)。
-
将SelectImage Mode对象从层次结构窗口拖动到选择图片槽位。
-
在层次结构窗口中选择SelectImage Mode游戏对象(位于交互控制器下)。
-
将AddPicture Mode对象从层次结构窗口拖动到添加图片槽位。
-
将EditPicture Mode对象从层次结构窗口拖动到编辑图片槽位。
-
在层次结构窗口中,选择EditPicture Mode游戏对象(位于交互控制器下)。
-
将SelectImage Mode对象从层次结构窗口拖动到选择图片槽位。
-
在层次结构窗口中,选择位于UI 画布 | SelectImage UI下的Image Buttons游戏对象。
-
将SelectImage Mode对象从层次结构窗口拖动到选择图片槽位。
这样就完成了!
总结一下,我们已经重构了ImageButtons脚本,以便在按钮按下时调用SelectImageMode.ImageSelected。SelectImageMode将知道用户是添加新图片还是用现有图片替换图片。在前一种情况下,模式是从 Main-mode 调用的。在后一种情况下,模式是从 EditPicture-mode 调用的,并且设置了isReplacing标志。
继续构建并运行场景。添加一张图片,然后编辑它。然后,点击替换图片按钮。应该会出现选择图片菜单。此时,你可以选择另一张图片,它将替换当前选中的FramedPhoto中的图片。你还可以为这个项目添加更多功能,包括让用户为他们的图片选择不同的相框。
替换相框
我们必须实现的最后一个编辑按钮是替换相框。我将把这个功能留给你来构建,因为在这个时候,你可能已经具备了自己解决这个挑战的技能。一个基本的解决方案可能是保留当前的FramedPhoto预制体,并让用户只需为相框选择不同的颜色。或者,你可以在FramedPhoto预制体中定义单独的相框对象,可能使用在资产商店或其他地方找到的模型,并选择一个允许一个或另一个相框对象的功能。以下是一些关于在哪里找到模型的建议:
-
经典相框:
assetstore.unity.com/packages/3d/props/furniture/classic-picture-frame-59038 -
Turbosquid:
www.turbosquid.com/3d-model/free/picture-frame
到目前为止,我们一直是通过编辑菜单按钮间接与放置的对象交互。接下来,我们将考虑直接与虚拟对象交互。
通过交互编辑图片
我们现在将实现移动和调整我们在 AR 场景中放置的虚拟对象大小的能力。为此,我决定让正在编辑的对象负责自己的交互。也就是说,当FramedPhoto正在编辑时,它将监听输入动作事件并移动或调整自己。
我还决定将这些功能作为独立的组件实现,分别是MovePicture和ResizePicture,在FramedPhoto预制体上。这将在FramedPhoto正在编辑时启用。首先,让我们确保实例化的FramedPhoto对象接收输入动作消息,以便它们能够响应用户输入。
确保 FramedPhoto 对象接收输入动作消息
我们目前正在使用 Unity 输入系统,它允许你定义和配置用户输入动作,以及通过玩家输入组件监听这些动作事件。目前,场景中有一个玩家输入组件,附加到交互控制器游戏对象上。该组件配置为向下广播消息。因此,如果我们想让FramedPhoto脚本接收输入动作消息(我们现在就是这样做的),我们必须确保FramedPhoto对象实例是交互控制器的子对象。让我们简单地将FramedPhoto对象作为其实例化的AddPicture Mode游戏对象的子对象,如下所示:
-
编辑
AddPictureMode脚本。 -
在
PlaceObject函数中,通过添加以下代码行将生成的对象的父对象设置为AddPicture Mode游戏对象:GameObject spawned = Instantiate(placedPrefab, position, rotation); spawned.transform.SetParent( transform.parent);
实例化的FramedPhoto预制件现在将由AddPicture Mode游戏对象作为父对象。
信息 - 场景组织和输入动作消息
建议您考虑如何组织场景对象层次结构以及放置实例化对象的位置。例如,通常,我更喜欢将所有我们的FramedPhotos保存在一个单独的根对象容器中。如果我们现在这么做,我们就必须将Player Input Behavior设置为调用事件,而不是向下广播消息到本地层次结构。然后,响应这些输入动作的脚本将订阅(添加监听器)到这些消息(见docs.unity3d.com/Packages/com.unity.inputsystem@1.1/manual/Components.html#notification-behaviors)。另一方面,对于本书中的教程项目等,我决定使用内置的输入动作消息会更简洁且更容易解释。
让我们从创建空脚本并将它们添加到场景中开始。然后,我们将构建它们。
添加交互组件
为了加快实现速度,我们必须首先通过执行以下步骤来创建脚本文件:
-
在你的
MovePicture。 -
创建另一个新的 C#脚本,命名为
ResizePicture。 -
打开FramedPhoto预制件进行编辑。
-
将MovePicture脚本和ResizePicture脚本从Project资产文件夹拖到根FramedPhoto对象上。
-
在你的代码编辑器中编辑
FramedPhoto脚本。在类的顶部添加以下声明:MovePicture movePicture; ResizePicture resizePicture; -
在
Awake中初始化它,并开始时禁用组件:void Awake() { movePicture = GetComponent<MovePicture>(); resizePicture = GetComponent<ResizePicture>(); movePicture.enabled = false; resizePicture.enabled = false; layer = LayerMask.NameToLayer("PlacedObjects"); Highlight(false); } -
然后,在编辑时启用这些组件:
public void BeingEdited(bool editing) { Highlight(editing); movePicture.enabled = editing; resizePicture.enabled = editing; isEditing = editing; }
我们现在已经准备好向FramedPhoto对象添加移动和调整大小直接操作功能。这些将作为单独的组件,仅在编辑图片模式下启用。
好的。让我们先通过在屏幕上用手指拖动图片来交互式地沿墙移动图片。
用我们的手指移动图片
我们将首先通过向AR Input Actions资产添加MoveObject动作来实现拖动移动功能。就像我们已有的SelectObject动作(和PlaceObject)一样,这将绑定到触摸屏的主要触摸位置。我们将把这个动作与其他动作分开,例如,如果你决定使用不同的交互技术,比如触摸并保持以开始拖动操作,我们可以这么做。但就目前而言,我们只需复制另一个,如下所示:
-
在
Assets/Inputs/文件夹中打开它进行编辑(或使用其Edit Asset按钮)。 -
在中间部分,右键单击SelectObject动作并选择Duplicate。
-
将新脚本重命名为
MoveObject。 -
按 Save Asset(除非已启用 Auto-Save)。
现在,我们可以添加监听此操作的代码。编辑 MovePicture 脚本并写下以下内容:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
public class MovePicture : MonoBehaviour
{
ARRaycastManager raycaster;
List<ARRaycastHit> hits = new List<ARRaycastHit>();
void Start()
{
raycaster = FindObjectOfType<ARRaycastManager>();
}
void Start(){ }
public void OnMoveObject(InputValue value)
{
if (!enabled) return;
if (EventSystem.current.IsPointerOverGameObject(0)) return;
Vector2 touchPosition = value.Get<Vector2>();
MoveObject(touchPosition);
}
void MoveObject(Vector2 touchPosition)
{
if (raycaster.Raycast(touchPosition, hits, TrackableType.PlaneWithinPolygon))
{
ARRaycastHit hit = hits[0];
Vector3 position = hit.pose.position;
Vector3 normal = -hit.pose.up;
Quaternion rotation = Quaternion.LookRotation(normal, Vector3.up);
transform.position = position;
transform.rotation = rotation;
}
}
}
这段代码与 AddPictureMode 脚本中的代码非常相似。它使用 AR Raycast Manager 来找到一个跟踪平面并将对象放置在平面与对象垂直对齐的位置。不同之处在于我们不是实例化一个新对象,我们只是更新现有对象的变换。我们这样做是持续的,只要输入动作事件正在生成(也就是说,只要用户在触摸屏幕)。
如果接收到输入动作消息但此组件未启用,则跳过 OnMoveObject 函数。它还检查用户是否没有轻触 UI 元素(一个事件系统对象),例如我们编辑菜单按钮之一。
尝试一下。如果你播放场景,创建一个图片,并开始编辑它,你应该能够用手指拖动图片,它会沿着墙面移动。实际上,由于我们每次更新都会进行射线投射,它可能会在你拖动时找到一个新的、更精确的跟踪平面,甚至将图片移动到另一面墙上。
正如我们之前提到的,如果你在任意一个已跟踪平面上轻触屏幕,当前图片将“跳转”到该位置。如果这不是你期望的行为,我们可以在开始更新变换位置之前检查初始触摸是否在当前图片上。修改后的代码如下:
-
声明并初始化对
camera和layerMask的引用:Camera camera; int layerMask; void Start() { raycaster = FindObjectOfType<ARRaycastManager>(); camera = Camera.main; layerMask = 1 << LayerMask.NameToLayer("PlacedObjects"); } -
在
MoveObject中添加射线投射以确保在移动之前触摸在图片上:
void MoveObject(Vector2 touchPosition)
{
Ray ray = camera.ScreenPointToRay(touchPosition);
if (Physics.Raycast(ray, Mathf.Infinity, layerMask))
{
if (raycaster.Raycast(touchPosition, hits, TrackableType.PlaneWithinPolygon))
{
ARRaycastHit hit = hits[0];
Vector3 position = hit.pose.position;
Vector3 normal = -hit.pose.up;
Quaternion rotation = Quaternion.LookRotation(normal, Vector3.up);
transform.position = position;
transform.rotation = rotation;
}
}
}
目前,我们只在 AddPicture 模式下显示跟踪平面。我认为在 Edit 模式下显示它们也会很有用。我们可以使用之前章节中编写的相同的 ShowTrackablesOnEnable 脚本,该脚本已经应用于 AddPicture 模式 游戏对象。按照以下步骤添加:
-
在 Hierarchy 窗口中,选择 EditPicture 模式 游戏对象(位于 Interaction Controller 下)。
-
在你的项目
Scripts/文件夹中找到ShowTrackablesOnEnable脚本。 -
从 Hierarchy 窗口中,将 AR Session Origin 游戏对象拖到 Show Trackables On Enable | Session Origin 插槽中。
-
将脚本拖到 EditPicture 模式 对象上,添加为组件。
现在,当 EditPicture 模式 启用时,可跟踪的平面将会显示。当它被禁用并且你回到主模式时,它们将再次隐藏。
接下来,我们将实现捏合调整大小功能。
捏合以调整图片大小
要实现捏合调整大小,我们也将使用一个输入动作,但这需要一个两指触摸。因此,动作不仅仅返回一个单一值(例如,Vector2)。所以这次,我们将使用 PassThrough 动作类型。按照以下步骤添加它:
-
编辑 AR Input Actions 资产,就像我们之前做的那样。
-
在中间的
ResizeObject。 -
在最右侧的属性部分,选择动作类型 | 传递,以及控制类型 | 向量 2。
-
在中间的动作部分,选择<无绑定>子项。然后,在属性部分,选择属性 | 路径 | 触摸屏 | 触摸 #1 | 位置以将此动作绑定到第二个手指屏幕触摸。
-
按下保存资产(除非已启用自动保存)。
现在,我们可以添加代码来监听这个动作。编辑ResizePicture脚本,并按照以下方式编写。在脚本的第一个部分,我们声明了几个属性,我们可以使用这些属性在 Unity 检查器中调整脚本的行为。pinchspeed允许你调整捏合的灵敏度,而minimumScale和maximumScale允许你分别限制用户最终将图片缩放到多小或多大的程度。按照以下步骤操作:
-
以以下代码开始脚本:
using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.InputSystem; public class ResizePicture : MonoBehaviour { [SerializeField] float pinchSpeed = 1f; [SerializeField] float minimumScale = 0.1f; [SerializeField] float maximumScale = 1.0f; float previousDistance = 0f; void Start() { }注意,我声明了一个空的
Start()函数。这是必需的,因为没有Start或Update函数的MonoBehaviour组件无法被禁用(如果你从代码中移除Start并在检查器窗口中查看,你会看到启用复选框不见了)。 -
OnResizeObject函数是输入动作消息的监听器。因为我们指定了动作类型为Touchscreen以获取第一和第二根手指的触摸。然后,我们可以将这些触摸位置传递给我们的TouchToResize函数:public void OnResizeObject() { if (!enabled) return; if (EventSystem.current. IsPointerOverGameObject(0)) return; Touchscreen ts = Touchscreen.current; if (ts.touches[0].isInProgress && ts.touches[1].isInProgress) { Vector2 pos = ts.touches[0].position.ReadValue(); Vector2 pos1 = ts.touches[1].position.ReadValue(); TouchToResize(pos, pos1); } else { previousDistance = 0; } } -
TouchToResize算法很简单。它获取两个手指触摸之间的距离(屏幕像素),并将其与之前的距离进行比较。将新距离除以旧距离给出百分比变化,我们可以直接使用它来修改变换的缩放。对我来说,这似乎工作得相当好:void TouchToResize(Vector2 pos, Vector2 pos1) { float distance = Vector2.Distance(pos, pos1); if (previousDistance != 0) { float scale = transform.localScale.x; float scaleFactor = (distance / previousDistance) * pinchSpeed; scale *= scaleFactor; if (scale < minimumScale) scale = minimumScale; if (scale > maximumScale) scale = maximumScale; Vector3 localScale = transform.localScale; localScale.x = scale; localScale.y = scale; transform.localScale = localScale; } previousDistance = distance; } }
尝试一下。如果你播放场景,创建一个图片,并开始编辑它,你应该能够使用两只手指来调整图片大小,通过捏合手指来缩小图片,通过分开手指来增大图片的大小。以下是我手机上的屏幕截图,展示了我在餐厅墙上排列的一些图片,它们的大小各不相同:
![图 7.7 – 我餐厅墙上排列的虚拟相框照片]

图 7.7 – 我餐厅墙上排列的虚拟相框照片
在本节中,我们探讨了如何直接与虚拟对象交互。使用输入动作,我们添加了使用触摸屏拖动和移动墙上图片的功能,以及捏合来调整图片大小的功能。
我们可以通过添加一个取消编辑功能来改进这一点,该功能可以将图片恢复到编辑前的状态。一种方法是在对象进入编辑模式时创建一个临时副本,然后在用户取消或保存更改时恢复或丢弃它。
另一个值得考虑的功能是在会话之间持久化图片对象排列,以便在退出应用时保存你的图片,并在重新启动应用时恢复它们。这是一个高级主题,我不会在本书中介绍,因为它超出了 Unity AR Foundation 本身。每个提供商都有自己的专有解决方案。如果你感兴趣,可以查看ARCore Cloud Anchors,它由 Unity ARCore Extensions支持(developers.google.com/ar/develop/unity-arf/cloud-anchors/overview)和ARKit ARWorldMap(developer.apple.com/documentation/arkit/arworldmap),如 Unity ARKit XR 插件(docs.unity3d.com/Packages/com.unity.xr.arkit@4.0/api/UnityEngine.XR.ARKit.ARWorldMap.html)所公开。
这就结束了我们对构建 AR 照片画廊项目的探索和构建。
摘要
在本章中,你扩展了我们从第六章**,画廊:构建 AR 应用开始的 AR 画廊项目。该项目使我们能够将装裱的照片放置在墙上。在本章中,你增加了在场景中编辑虚拟对象的能力。
你实现了在主模式中选择现有虚拟对象的能力,所选对象被突出显示,应用进入编辑图片模式。在这里,有一个编辑菜单,包含替换图片、替换框架、删除图片和完成(返回主模式)按钮。替换图片功能显示了与我们在创建(添加)新图片时使用的相同的选择图片模态菜单。我们必须重构代码以使其可重用。
当你在墙上放置和移动图片时,你实现了一个功能,以避免重叠或碰撞的对象,自动将图片移开。之后,你通过使用触摸事件拖动图片到新位置,实现了与虚拟对象的直接交互。你还实现了捏合来调整墙上图片的大小。最后,你学习了如何从 C#中使用更多的 Unity API,包括碰撞触发钩子和矢量几何。
在下一章中,我们将开始一个新的项目,同时使用不同的 AR 跟踪机制——跟踪图像——在构建一个可视化 3D 数据的项目时;即,我们太阳系中的行星。
第八章:第八章:行星:跟踪图像
在本章中,我们将使用增强现实进行数据可视化和教育。我们将构建一个项目,用户可以了解我们太阳系中的行星。假设您有一本关于太阳系的儿童科学书籍及其配套移动应用程序。例如,在关于地球的页面,读者可以将他们的移动设备指向页面上的图片,地球的 3D 渲染就会从页面中弹出。
我们将要使用的 AR 机制被称为图像跟踪。使用图像跟踪,您准备一个参考图像库,这些图像在运行时可以在真实世界中识别和跟踪。当用户的设备摄像头检测到这些图像之一时,可以在图像位置实例化一个虚拟对象。
我已经为您提供了“行星卡”,每张卡上都有图片和独特的标记,这些标记是我从网络上可用的免费资源中创建的,供您打印并用于应用程序。为了渲染行星的球形表面皮肤,我们将使用实际行星的免费纹理图像。
本章我们将涵盖以下主题:
-
理解 AR 图像跟踪
-
指定“行星”项目并开始
-
定义和跟踪参考图像
-
创建和实例化一个虚拟地球预制件
-
绕其轴旋转行星
-
通过添加多个行星来扩展项目
-
制作响应式 UI
到本章结束时,您将拥有一个可以检测提供的行星卡上的图像、渲染给定行星的 3D 模型并提供有关行星的附加信息详情的工作应用程序。
技术要求
要实现本章的项目,您需要在您的开发计算机上安装 Unity,并将其连接到一个支持增强现实应用程序的移动设备(有关说明,请参阅第一章,为 AR 开发设置)。我们还假设您已安装ARFramework模板及其所有先决条件。有关更多详细信息,请参阅第五章**,使用 AR 用户框架。完成的项目可以在本书的 GitHub 仓库中找到,网址为github.com/PacktPublishing/Augmented-Reality-with-Unity-AR-Foundation。
理解 AR 图像跟踪
在我们开始构建项目之前,让我们花一点时间来了解 AR 图像跟踪是如何工作的。在本节中,我将介绍图像识别和跟踪背后的基本原理,以及为什么某些图像比其他图像更适合这个目的。
正如我们所知,增强现实背后的原理涉及使用计算机制来识别现实世界中的特征,确定它们在三维空间中的位置和方向,相对于并锚定在这个三维空间中实例化虚拟对象,并跟踪用户在空间中的移动。现代设备可以使用其视频摄像头和设备内置的其他传感器来执行环境的实时空间映射。另一种方法是设备跟踪预定的图像。这正是本章项目中我们将使用的方法。
增强现实技术诞生于 20 世纪 90 年代,当时使用类似二维码的标记图像进行跟踪。以下图像展示了示例:

图 8.1 – 基本 AR 标记符
标记图像可用于在现实世界中触发和定位虚拟对象。这些简单但视觉上独特的标记易于检测,即使是低端设备也能轻松识别。这些标记易于检测,因为它们具有独特的细节、高对比度边缘和不对称的形状——也就是说,这是一个易于识别的图像,具有明确的顶部、底部、左侧和右侧。因此,检测软件可以确定哪个标记图像在视图中,以及相机相对于标记在三维空间中的方向。
将这一概念提升到更高层次,例如 Merge Cube (mergeedu.com/cube)这样的产品,在其六个面上都有标记,就像你可以用手拿的物理立方体一样。用户可以找到具有广泛游戏、学习和探索体验的配套应用程序。Merge 为开发者提供 Unity 包,以便你也能为其构建自己的项目。以下图像展示了 Merge Cube:

图 8.2 – Merge Cube 在每个面上都有标记,提供 3D 跟踪立方体
标记符可以与自然图像结合,以提供令人愉悦且信息丰富的视觉上独特的图像,同时充当 AR 标记符。你经常在 AR 增强故事书中甚至谷物盒上看到这种做法。这正是我在本章中采用的方法。
虽然标记提供了最高的可靠性,但它们对于图像跟踪并非必不可少。普通摄影图像也可以使用。在 AR 术语中,这些被称为自然特征图像。用于跟踪的图像必须具有使标记可靠的同种特征——独特的细节、高对比度边缘和不对称的形状。关于选择图像的最佳实践已经有很多讨论。例如,AR Core 开发者指南(developers.google.com/ar/develop/java/augmented-images/)包含了关于使用参考图像的额外提示,包括以下内容:
-
使用至少 300 x 300 像素的图像分辨率。然而,非常高的分辨率并不帮助识别。
-
颜色信息没有被使用,所以彩色或灰度图像都一样好。
-
避免具有很多几何特征的图像,或者太少的图像。
-
避免重复的图案。
AR Core SDK 附带一个arcoreimg工具,可以评估图像,并为每个图像返回 0 到 100 之间的质量分数,其中至少 75 分的分数是推荐的。同样,Unity 在编译您的构建中的图像参考库时使用类似的工具(我们将在本章后面了解更多关于这一点)。
基于对在增强现实应用中使用图像跟踪的一般理解,让我们首先定义一个有趣且有趣的项目——可视化我们的太阳系行星。
指定行星项目
我们将构建一个行星信息应用,允许用户扫描行星卡以可视化太阳系中每个行星的 3D 模型。想象一下,这将是交易卡收藏的一部分或儿童科学书的配套应用。当用户将设备的相机对准行星卡之一时,他们可以看到行星的 3D 渲染。按下信息按钮后,用户可以获取有关该行星的更多信息。在本节中,我将定义一般用户体验流程,为您提供准备行星卡以供自己使用的说明,并帮助您收集在本项目中使用的资产。
用户体验流程
通用用户入职工作流程将如下进行:
-
启动模式:应用将启动,检查设备是否支持 AR,并请求相机权限(取决于操作系统)。一旦读取,应用将进入扫描模式。
-
扫描模式:用户被提示将相机对准图像进行检测和跟踪。当至少有一个图像正在跟踪时,应用进入主模式。
-
主模式:这是应用对新或更新的跟踪图像做出响应并允许用户与行星交互的地方。当图像被跟踪时,它确定哪个行星与图像相对应,并实例化行星的游戏对象。如果丢失跟踪,应用可能会回到扫描模式以提示用户。如果跟踪不同的图像,当前行星将被新的图像的行星所取代。
这个工作流程比我们在前几章中实现的工作流程要简单一些。在那个情况下,我们需要用户在进入主模式之前扫描环境以寻找可跟踪的平面。然后,用户被要求故意点击屏幕以在场景中放置一个虚拟对象。此外,在 AR 画廊项目中,我们添加了编辑模式来修改用户添加的图片。在这个项目中,许多这些都是不必要的;过程更加自动化,因为我们让设备检测图像,并相应地实例化一个虚拟对象。
准备行星卡
对于本项目,我们使用打印的 行星卡片 作为标记图像,以便我们可以选择一个行星进行可视化。您可以在本章的项目文件中找到一个包含这些卡片的 PDF 文件(在名为 Printables/ 的文件夹中)。为了准备这些卡片以供本项目使用,请按照以下步骤操作:
-
打印出
PlanetCards.pdf文件。 -
然后,将纸张裁剪成单独的卡片。
-
我建议您使用厚纸打印或把打印件粘贴到纸板上,以避免变形,这可能会影响软件在运行时识别图像的能力。
以下照片显示了准备这些卡片以供使用的情况:

图 8.3 – 为本项目裁剪打印的行星卡片
这些卡片是由网络上可以免费找到的资源组合而成的。我在 Kids Flashcards 网站上找到了原始的闪卡。在访问 kids-flashcards.com/en/free-printable/solar-system-flashcards-in-english 后,我下载了 Solar System flashcards free PDF 文件。
首先,我尝试直接使用这些闪卡,但图片不够独特,无法单独识别。因此,我决定为每一张卡片添加 ArUco 标记。ArUco 是一个带有宽黑色边框和内部二进制矩阵的方形标记,其 ID 基于 OpenCV(开源计算机视觉库,由西班牙科尔多瓦大学开发;见 docs.opencv.org/3.2.0/d5/dae/tutorial_aruco_detection.html)。我使用了在线 ArUco 标记生成器 chev.me/arucogen/ 为每个行星制作单独的标记。
然后,我使用 Photoshop 将标记与行星闪卡结合,制作出本项目最终的行星卡片。(Photoshop PSD 文件也包含在本章的 GitHub 文件中。)
每张行星卡片也是一个单独的 PNG 图像。这些图像已提供给您,位于 Image Library/ 文件夹中。在本章的后面部分,我们将创建一个图像参考库并添加这些图像。图像的命名格式为 [planetname]-MarkerCard.png;例如,Earth-MarkerCard.png。我们将在代码中利用这种命名约定。
当应用程序检测到行星卡片时,应用程序将实例化一个行星模型。为此,我们需要行星材料的纹理图像。
收集行星纹理和数据
我们需要纹理图像来用作每个行星的球形网格的行星皮肤。我们使用的这些图像我是在有趣的 太阳系范围 项目网站上找到的 (www.solarsystemscope.com/)。这些文件包含在本书 GitHub 存储库的该章节文件中,可以从 www.solarsystemscope.com/textures/ 下载。话虽如此,您可以在 Unity 资产商店 (assetstore.unity.com/?q=solar%20system&orderBy=1) 中找到替代资产,包括经典的 地球免费 包 (assetstore.unity.com/packages/3d/environments/sci-fi/planet-earth-free-23399),它包括云层。
关于行星的附加元数据,我在 NASA.gov 网站上找到了 行星事实表 (nssdc.gsfc.nasa.gov/planetary/factsheet/index.html) 以及更多详细信息在 nssdc.gsfc.nasa.gov/planetary/planetfact.html。我们可以在渲染和动画我们的模型时直接使用其中的一些细节,例如行星直径(千米)、自转周期(小时)和倾斜(轨道倾角)。
拥有我们的行星卡、行星皮肤纹理和其他行星细节后,我们就可以开始构建项目了。
入门
首先,我们将使用 ARFramework 场景模板创建一个名为 PlanetsScene 的新场景。按照以下步骤操作:
-
选择 文件 | 新场景。
-
在 新场景 对话框中,选择 ARFramework 模板。
-
按 创建。
-
在您的
Assets项目中选择Scenes/文件夹,将其命名为PlanetsScene,然后点击 保存。
新的 AR 场景已经设置了以下内容:
-
AR 会话 游戏对象。
-
配有射线投射管理器和平面管理器的 AR 会话起源 装配。
-
UI Canvas 是一个带有子面板的屏幕空间画布;即启动 UI、扫描 UI、主 UI 和非 AR UI。它还包含我们编写的 UI 控制器组件脚本。
-
交互控制器 是一个包含我们编写的交互控制器组件脚本的游戏对象,它帮助应用在交互模式之间切换,包括启动、扫描、主和非 AR 模式。它还有一个配置了 AR 输入动作 资产的 玩家输入 组件。
-
来自 AR Foundation Demos 项目的 OnboardingUX 预制件,它提供 AR 会话状态和功能检测状态消息,以及动画引导图形提示。
我们现在可以设置应用标题,如下所示:
-
在 层次结构 窗口中,展开 UI Canvas 对象,然后展开其子 应用标题面板。
-
选择 标题文本 对象。
-
在其
行星探索器中。
以这个场景为基础,我们将用 AR 跟踪图像管理器替换 AR 可跟踪组件。
跟踪参考图像
我们的开场景包括 AR 会话原点,以及用于玩家输入和 AR 射线管理器的组件。它还包括我们在这个项目中不需要的组件,用于检测和跟踪平面,我们将用 AR 跟踪图像管理器来替换它。有关AR 跟踪图像管理器的文档可以在docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.1/manual/tracked-image-manager.html找到。然后,我们将为我们的行星卡片图像创建一个图像参考库。
添加 AR 跟踪图像管理器
要配置 AR 会话以跟踪图像,请执行以下步骤:
-
在层次结构窗口中,选择AR 会话原点游戏对象。
-
在检查器窗口中,在AR 平面管理器上使用3 点上下文菜单(或右键点击)并选择移除组件。
-
使用
AR并添加一个AR 跟踪图像管理器组件。
你会注意到组件上有一个用于参考图像库的序列化库槽。我们将在下一步创建它。
创建参考图像库
参考图像库包含应用程序将在现实世界中检测和跟踪的每个图像的记录。在我们的情况下,我们将添加行星卡片图像。在 GitHub 存储库中提供的本书提供的资产中,有一个名为Image Library/的文件夹,其中已经包含了我们将添加到库中的行星卡片图像。我们在这里将仅从地球卡片开始;我们将在本章的后面添加其他行星。
我们可以通过执行以下步骤来创建库:
-
在
图像库/。 -
在
图像库/文件夹上右键点击并选择创建 | XR | 参考图像库。 -
在ReferenceImageLibrary资产选中时,在检查器窗口中,点击添加图像。
-
将
地球-MarkerCard图像从项目窗口拖动到正方形图像纹理槽中。 -
打开指定大小复选框。
-
如果你从提供的 PDF 中打印了行星卡片,按比例,宽度约为 8 厘米,或 0.08 米。否则,使用尺子测量你打印的地球行星卡片。
-
然后,在X字段中输入宽度(
0.08)。Y值将根据 PNG 图像的像素尺寸自动更新。 -
打开在运行时保留纹理复选框。
以下截图显示了生成的参考图像库设置:

图 8.4 – 添加了地球的参考图像库
现在,我们可以更新AR 跟踪图像管理器组件,如下所示:
-
在层次结构窗口中,选择AR 会话原点对象。
-
将参考图像库资产从项目窗口拖放到AR 跟踪图像管理器的序列化库槽位。
-
在我们设置好这个项目的同时,当检测到图像时,我们将实例化现有的预制件对象。
例如,将
ARF-samples/Prefabs/文件夹拖放到跟踪图像预制件槽位(或另一个类似的对象)。
AR 跟踪图像管理器组件现在应如下所示:
![Figure 8.5 – The AR Tracked Image Manager with the reference image library assigned]
![img/Figure_8.05_B15145.jpg]
图 8.5 – 配置了参考图像库的 AR 跟踪图像管理器
您现在有一个 AR 场景,可以识别和跟踪在参考库中定义的图像。目前,库中只包含地球标记卡图像。当在运行应用程序时识别到图像,将在地球行星卡上放置一个简单的立方体。
我们几乎准备好尝试这个了。但首先,让我们配置用户框架的 UI 和模式。
配置用户交互模式和 UI
场景模板ARFramework,我们从那里开始,提供了一个简单的框架,用于启用用户交互模式并显示对应模式的 UI 面板。此项目将在 AR 会话初始化时以启动模式开始,以便我们可以验证设备是否支持 AR。然后,它将过渡到扫描模式,其中它将尝试找到其中一个参考图像。一旦找到,它将过渡到主模式,在那里我们可以支持与应用程序内容的附加用户交互。
扫描参考图像
在扫描模式中,我们将显示一个指导图形,提示用户将摄像头指向带有行星和标记图像的行星卡。执行以下步骤以配置此操作:
-
在层次结构窗口中,展开UI Canvas游戏对象及其子扫描 UI。选择子动画提示对象。
-
在检查器窗口中设置动画提示 | 查找图像说明。
现在将在OnboardingUX对象上播放我们定义的查找图像剪辑,该对象由 Unity Onboarding UX 资产提供,并且已经存在于我们的场景层次结构中。以下屏幕截图显示了您可以期待的内容。左侧是启动模式,其中正在初始化 AR 会话。右侧是扫描模式,用户被提示查找图像(您看不到视频流,因为我覆盖了摄像头,以便在屏幕截图中使提示更加明显)。
![Figure 8.6 – Screen captures of Startup mode (left) and Scan mode (right)]
![img/Figure_8.06_B15145.jpg]
图 8.6 – 启动模式(左)和扫描模式(右)的屏幕截图
现在,我们需要设置扫描模式的脚本,以便知道何时找到图像并过渡到主模式。我们将用引用ARTrackedImageManager而不是ARTrackedPlaneManager的类似脚本替换默认的ScanMode脚本,如下所示:
-
在
Scripts/文件夹中,通过右键单击并选择ImageScanMode。 -
编辑
ImageScanMode并替换其内容,如下所示:using UnityEngine; using UnityEngine.XR.ARFoundation; public class ImageScanMode : MonoBehaviour { [SerializeField] ARTrackedImageManager imageManager; private void OnEnable() { UIController.ShowUI("Scan"); } void Update() { if (imageManager.trackables.count > 0) { InteractionController.EnableMode("Main"); } } } -
保存脚本。然后,回到 Unity 中,在层次结构窗口中,选择扫描模式游戏对象(位于交互控制器下)。
-
在检查器窗口中,使用3 点上下文菜单并选择移除组件来移除原始扫描模式组件。
-
将ImageScanMode脚本拖动到扫描模式对象上,将其添加为新组件。
-
从层次结构窗口中,将AR 会话原点对象拖动到检查器窗口,并将其放置在图像扫描模式 | 图像管理器槽中。
该组件现在将如下所示:

图 8.7 – 图像扫描模式组件
目前,我们已经使用ARFramework模板创建了一个新场景,并将其修改为使用AR 跟踪图像管理器并提示用户扫描图像。当检测到图像(例如,地球标记卡片)时,将实例化一个通用游戏对象(例如,AR 放置立方体预制件)。让我们在目标设备上测试我们迄今为止所完成的工作。
构建 和 运行
要在目标设备上构建和运行场景,请执行以下步骤:
-
通过转到文件 | 保存确保您已保存当前场景上的工作。
-
选择文件 | 构建设置以打开构建设置窗口。
-
点击添加打开场景将当前场景添加到构建场景列表中(如果尚未添加)。
-
从列表中取消选中除当前场景
PlanetsScene之外的所有场景。 -
然后,点击构建和运行以构建项目。
当应用启动时,将您的设备相机对准打印的地球行星卡片。您的虚拟立方体应该在该位置实例化,如下面的手机屏幕截图所示:

图 8.8 – 地球卡片已被检测到,立方体已被实例化
现在我们已经设置了一个基本的 AR 场景,其中图像检测已设置为识别地球行星卡片,并在该位置实例化一个样本预制件。现在,让我们制作一个地球模型,我们可以用它来代替这个愚蠢的立方体。
创建并实例化一个虚拟地球预制件
在本节中,我们将为每个行星创建预制件游戏对象。由于每个行星都有类似的行为(例如,它们会旋转),我们首先创建一个通用的行星预制件,然后使每个特定行星成为该预制件的变体。在 Unity 中,Planet脚本来动画行星的旋转并处理其他行为。每个行星将有自己的“皮肤”,由一个材质定义,以及一个基础纹理图,这是我们之前从网上下载的。
在本节中,我们将创建一个通用的星球预制体对象,创建一个地球预制体作为变体,通过编写“星球”组件脚本添加星球元数据,并实现星球旋转动画。
创建通用星球预制体
星球预制体包含一个 3D 球体,每个星球都会使用其纹理图像进行渲染。星球沿着其轴线旋转,因此我们将设置一个具有倾斜变换的层次结构来定义这个倾斜轴线。按照以下步骤操作:
-
在你的“星球预制体”。
-
双击(或在检查器窗口中选择打开预制体)以打开预制体进行编辑。
-
从主菜单中选择“倾斜”。
-
右键点击“星球”。
-
在场景中实例化的任何星球都位于特定的层上会有用。我将把这个层命名为“放置对象”。(我在前一章中介绍了并讨论了层)。以它的根“放置对象”。
如果“放置对象”层不存在,请选择“放置对象”并将其放入一个空槽中。在“放置对象。”
然后你会被提示问题,你想要将层设置为放置对象的所有子对象吗? 点击是,更改子对象。
-
保存预制体。
目前这非常简单(只有一个球形子对象被倾斜变换所包含),但它将作为我们添加的每个星球预制体的模板。星球预制体层次结构在以下屏幕截图中显示:

图 8.9 – 星球预制体层次结构
每个星球都将使用代表该星球实际视图的皮肤进行渲染。在创建地球预制体之前,让我们花一点时间来了解渲染材质和我们将要使用的纹理图像。
理解等距圆柱图像
当 Unity 渲染 3D 模型时,它从一个描述几何形状的 3D 网格开始。就像渔网一样,网格是由顶点和向量组成的集合,向量将这些顶点连接起来,组织成三角形(有时是四边形的四边形),这些三角形定义了网格的表面。以下插图显示了左边的球体网格的线框视图。在右边是球体的渲染视图,地球纹理映射到其 3D 表面上:

图 8.10 – 球形网格(左)和带有纹理的渲染球形(右)
纹理图像只是一个 2D 图像文件(例如,一个 PNG 文件),当它渲染时,在 3D 网格的表面上进行计算映射。想象一下将地球展开成一个 2D 地图,就像地图学家几个世纪以来所做的那样。一个常见的 2D 投影被称为等距圆柱,其中中心(赤道)处于正确的比例,并且当接近顶部和底部极点时,图像会越来越拉伸。以下图像显示了前面地球的等距圆柱纹理(由Stefan Kuhn绘制):
![图 8.11 – 定义球体皮肤的等经圆纹理
![img/Figure_8.11_B15145.jpg]
图 8.11 – 定义球体皮肤的等经圆纹理
信息 – 等经圆图像也用于 360 度媒体和 VR
等经圆图像也被称为 360 度图像,并用于虚拟现实应用中。在 VR 中,图像被有效地映射到球体的内部,你从内部而不是地球仪的外部进行观看!
对于我们的项目,我们为每个行星都有纹理图像。例如,火星的一个如下所示:
![图 8.12 – 火星纹理图
![img/Figure_8.12_B15145.jpg]
图 8.12 – 火星纹理图
要为特定的行星(如地球)创建预制体,我们需要创建一个使用地球纹理图像的材质。我们现在将构建它。
创建地球预制体
地球预制体将是行星预制体的一个变体,具有自己的地球材质。通过以下步骤创建它:
-
在
Earth Prefab。 -
双击****地球预制体(或在检查器窗口中选择打开预制体)。
-
在
Materials/文件夹中(如果需要则创建一个)并选择Earth Material。 -
将地球材质从项目窗口拖动并放置到地球游戏对象上。
-
在项目资产中的
Planet Textures/earth)中定位,并将其拖放到表面输入 | 基础地图纹理芯片上。以下屏幕截图显示了地球材质在检查器窗口中:![图 8.13 – 地球材质与基础地图纹理定义![img/Figure_8.13_B15145.jpg]
图 8.13 – 定义了基础地图纹理的地球材质
-
让我们在将地球添加到场景时选择一个默认大小。除非你想要将直径为 1 米的地球放置到你的场景中(!),我们需要设置其缩放。
在层次窗口中选择地球子对象。
-
在其
0.1, 0.1, 0.1)。 -
同样地,为了将地球放置在图像的表面上,我们可以将其 Y 位置设置为
0.05。但为了让它稍微悬停在上方,我们将设置为0.075。 -
保存预制体并退出到场景层次。
使用此预制体而不是 AR 放置立方体预制体在 AR 跟踪图像管理器组件上的 AR 会话原点对象上。稍后,我们将使用脚本更正确地管理它,但现在让我们先试试:
-
在层次窗口中,选择AR 会话原点游戏对象。
-
将地球预制体从项目窗口拖动到检查器窗口,并将其放入AR 跟踪图像管理器 | 跟踪图像预制体槽中。
-
构建并运行场景。
这次,当你将相机指向地球行星卡片时,地球预制体将出现,如下面的屏幕截图所示:
![图 8.14 – 在跟踪地球行星卡片时,应用实例化了一个地球预制体
![img/Figure_8.14_B15145.jpg]
图 8.14 – 在跟踪地球行星卡片时,应用实例化了一个地球预制体
这看起来相当不错。预制件还可以包含有关行星的其他信息。我们将在下一节中查看如何做到这一点。
添加行星元数据
每个行星预制件都可以包含有关该行星的附加信息。我们可以在预制件的Planet脚本中捕获这些信息,如下所示:
-
从项目窗口中打开星球预制件进行编辑。
-
在
Scripts/文件夹中,创建一个名为Planet的新 C#脚本。 -
将
Planet脚本拖放到根星球预制件游戏对象上,将其添加为组件。 -
在您的代码编辑器中打开
Planet脚本并编写以下内容:using UnityEngine; public class Planet : MonoBehaviour { public string planetName; public string description; } -
保存脚本。然后,在 Unity 中,保存预制件。
尽管我们对星球预制件做了所有这些更改,但由于它是预制件变体,地球预制件继承了所有内容。
-
现在,打开地球预制件进行编辑。
-
在
Earth。 -
在
地球是距离太阳第三颗行星,也是已知能够孕育和维持生命的唯一天体。 -
保存预制件。
我们还可以将行为赋予行星预制件,例如绕其轴旋转。
动画行星的旋转
行星会自转。有的转得快,有的转得慢。水星几乎不转——它每 59 个地球日转一次,而它绕太阳公转需要 88 个地球日!而且行星的旋转轴并不完全垂直(相对于其绕太阳的轨道)。例如,地球倾斜了 23.4 度,而金星以 177.4 度的角度侧卧旋转!好了,足够了科学小知识——让我们来动画化我们的地球模型。我们将向行星预制件添加一个Planet行为脚本,使其沿旋转轴旋转。按照以下步骤进行操作:
-
在您的代码编辑器中打开
Planet脚本并添加以下代码:[SerializeField] private float inclineDegrees = 23.4f; [SerializeField] private float rotationPeriodHours = 24f; [SerializeField] private Transform incline; [SerializeField] private Transform planet; public float animationHoursPerSecond = 1.0f; void Start() { incline.Rotate(0f, 0f, inclineDegrees); } void Update() { float speed = rotationPeriodHours * animationHoursPerSecond; planet.Rotate(0f, speed * Time.deltaTime, 0f); }
在类的顶部,我们将声明inclineDegrees(地球为 23.4)和rotationPeriodHours(地球为 24)的变量。我们还将定义预制件的incline和planet子对象的引用。
此外,还有一个公共的animationHoursPerSecond,它设置动画速度。我将其初始化为1.0,这意味着地球将在 24 秒内完成一次旋转。
Start()函数通过沿 Z 轴旋转设置倾斜角度。这只需要做一次。
Update()函数使星球绕其局部 Y 轴旋转。由于星球是Time.deltaTime的父级,因此每个Update都是 Unity 中计算对象 Transform 从一个帧到下一个帧的变化的常用惯用语,其中deltaTime是自上次Update以来的一秒的分数。
保存脚本后,回到 Unity 中,执行以下操作:
-
从项目窗口中打开星球预制件进行编辑。
-
确保在层次结构窗口中选中了根平面预制件游戏对象。
-
在将斜面游戏对象拖放到星球 | 斜面槽之前,先从层次结构窗口将其拖入检查器窗口。
-
将星球对象拖放到星球 | 星球槽中。
在检查器窗口中,行星组件现在将看起来像这样:

图 8.15 – 行星预制体上的行星组件
请现在构建并运行项目。当地球被实例化时,它将以每 24 秒一个完整旋转的速度倾斜和旋转。
到目前为止,我们有一个基本的 AR 场景,具有图像跟踪功能。它允许 AR 跟踪图像管理器在检测到图像时直接实例化我们的地球预制体。目前,它无法区分检测到的是哪种图像(假设你在参考库中有多个图像)并且始终实例化地球预制体。我们需要使应用更加健壮,并且我们可以从主模式中做到这一点。
构建应用的主模式
正如你所知,ARTrackedImageManager有一个“跟踪图像预制体”字段;然而,这并不是用于内容" (docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.2/manual/tracked-image-manager.html)。目前,当任何参考图像被识别时,地球预制体将始终被实例化。
相反,当应用处于主模式时,我们应该确定正在跟踪哪个行星卡片图像,并为该卡片实例化相应的行星预制体。到目前为止,我们在图像参考库中只有一个行星,地球。然而,在本章的后面部分,我们将扩展项目以包含所有行星。我们可以从移除AR 跟踪图像管理器组件中的预制体开始,如下所示:
-
在层次结构窗口中,选择AR 会话原点游戏对象。
-
在检查器窗口中,删除AR 跟踪图像管理器 | 跟踪图像预制体槽的内容,如下截图所示:

图 8.16 – 默认预制体字段已清除的 AR 跟踪图像管理器
当在它上面的ARTrackedImage组件中没有指定预制体时。现在,我们可以将其作为子对象实例化预制体。
在我们的场景框架中,应用以启动模式开始,一旦 AR 会话准备就绪,就进入扫描模式。当扫描模式检测到参考图像时,通过在交互控制器下启用主模式游戏对象,进入主模式。这会显示主 UI面板。现在让我们构建这个面板。
编写 PlanetsMainMode 脚本
在本节中,我们将编写一个新的PlanetsMainMode脚本,以替换默认场景模板中提供的默认MainMode脚本。像我们框架中的其他模式一样,当启用时,它将显示适当的 UI 面板。然后,当跟踪到图像时,它将找到相应的行星预制体并实例化它。
该脚本需要确定 AR 软件找到了哪个图像,并决定将哪个预制体实例化为跟踪图像的子对象。在我们的例子中,我们将使用检测到的图像文件名来确定哪个行星卡片被识别(按照设计,每个卡片图像都以行星的名称为前缀;例如,Earth-MarkerCard)。脚本将实现一个可序列化的字典,我们可以使用它来查找每个行星名称的行星预制体,使用序列化字典轻量版资产包(因为你已经安装了这个包,因为ARFramework也需要它。有关更多信息,请参阅assetstore.unity.com/packages/tools/utilities/serialized-dictionary-lite-110992)。
首先执行以下步骤:
-
在你的
Scripts/文件夹中,创建一个名为PlanetsMainMode的新 C#脚本。 -
在层次窗口中,选择主模式游戏对象(位于交互控制器下)。
-
在其检查器窗口中,使用3 点上下文菜单选择移除组件来移除默认的主模式组件。
-
将
PlanetMainMode脚本从项目窗口拖动到主模式对象上,将其添加为新组件。 -
双击
PlanetMainMode脚本以打开它进行编辑。 -
首先在文件顶部添加以下
using程序集声明:using UnityEngine; using RotaryHeart.Lib.SerializableDictionary; using UnityEngine.XR.ARFoundation; using TMPro; using UnityEngine.UI; -
当跟踪到图像时,我们需要找到要实例化的行星预制体。在文件顶部定义一个
PlanetPrefabDictionary如下,并为它声明一个planetPrefab变量:[System.Serializable] public class PlanetPrefabDictionary : SerializableDictionaryBase<string, GameObject> { } public class PlanetsMainMode : MonoBehaviour { [SerializeField] PlanetPrefabDictionary planetPrefabs; -
当此模式启用时,类似于原始的
MainMode脚本,我们将显示主 UI 面板:private void OnEnable() { UIController.ShowUI("Main"); } -
同样,在扫描模式确定开始跟踪图像后,我们将进入主模式。因此,
OnEnable也应该为跟踪的图像实例化行星。在类的顶部添加对imageManager的引用:[SerializeField] ARTrackedImageManager imageManager;然后,更新
OnEnable:void OnEnable() { UIController.ShowUI("Main"); InstantiatePlanet for each one. -
实现以下
InstantiatePlanet:void InstantiatePlanet(ARTrackedImage image) { string name = image.referenceImage.name.Split('-')[0]; if (image.transform.childCount == 0) { GameObject planet = Instantiate(planetPrefabs[name]); planet.transform.SetParent(image.transform, false); } else { Debug.Log($"{name} already instantiated"); } }InstantiatePlanet函数通过假设图像遵循我们的命名约定来确定跟踪图像文件名中的行星名称(例如,Earth-MarkerImage)。它确保场景中还没有行星对象。如果没有,则实例化行星预制体并将其作为跟踪图像对象的父对象。(我们传递false作为第二个参数,以便行星相对于跟踪图像变换进行定位。有关更多信息,请参阅docs.unity3d.com/ScriptReference/Transform.SetParent.html)。 -
保存脚本。
-
在 Unity 中,确保你在层次窗口中选择了主模式游戏对象。
-
将AR 会话原点对象从层次窗口拖动到检查器窗口中,将其放置在图像管理器槽中。
-
在检查器窗口中,点击行星主模式 | 行星预制体列表右下角的+按钮。
-
在Id槽中输入单词
Earth。 -
展开项目,并从层次结构窗口中,将地球预制件对象拖放到检查器窗口中的值槽。
-
使用文件 | 保存保存你的工作。
当它启用时,PlanetsMainMode。现在,代码已准备好检测不同的行星卡片图像并实例化相应的不同行星预制件。我们将从添加火星开始。
使用多个行星扩展项目
要将另一个行星添加到项目中,我们需要将其行星卡片图像添加到参考图像库,创建其行星预制件,包括用于渲染行星皮肤的材质,并将引用添加到PlanetsMainMode中的planetPrefabs列表。然后,我们将更新脚本以处理跟踪多个行星。让我们一步步地添加火星。
将行星卡片图像添加到参考图像库
执行以下步骤将火星添加到我们的参考图像库:
-
定位并选择你的
Image Library/文件夹)。 -
在其检查器窗口中,点击添加图像。
-
定位并拖动
Mars-MarkerCard图像从项目窗口,并将其拖放到检查器窗口中空白的纹理槽。 -
检查
0.08米(8 厘米)。 -
此外,勾选在运行时保持纹理复选框。
参考图像库现在应该看起来如下:

图 8.17 – 添加了火星-MarkerCard 图像的参考图像库
接下来,我们将创建火星预制件和材质。
创建行星预制件
要创建行星预制件,我们将复制并修改地球预制件资产。执行以下步骤:
-
在
Prefabs/文件夹)。 -
选择
Mars Prefab。 -
打开火星预制件进行编辑。选择子行星游戏对象。
-
在
Materials/文件夹中,选择Mars Material。 -
将火星材质拖放到行星对象上。
-
在
Planet Textures/文件夹)并将它拖放到火星材质 | 基图纹理槽。火星预制件的行星现在应该看起来如下:
![图 8.18 – 火星预制件及其行星设置为火星材质]()
图 8.18 – 火星预制件及其行星设置为火星材质
-
接下来,我们将设置火星行星元数据。在层次结构窗口中,选择根火星预制件游戏对象。
-
在
Mars;25.2;24.7。因为火星是太阳系中距离太阳第四颗行星,也是太阳系中第二小的行星。 -
保存预制件并返回场景层次结构(使用层次结构窗口左上角的<按钮)。
现在,我们可以将预制件添加到主模式的行星预制件字典中,如下所示:
-
在场景层次结构中,选择主模式游戏对象(在交互控制器下)。
-
在检查器窗口中,点击行星主模式 | 行星预制件列表右下角的+按钮。
-
在Id槽中输入单词
Mars。 -
展开项目,从层次结构窗口中,将火星预制体对象拖放到检查器窗口中的值槽位。
行星主模式组件现在应该如下所示:

图 8.19 – 添加火星后的行星主模式组件的行星预制体字典
如果您现在构建并运行,在扫描模式下,将相机指向您的火星行星卡片。火星 3D 对象将被添加到场景中,旋转展示其全部荣耀!
不幸的是,执行此操作后,如果您将相机移动到扫描地球行星卡片,将不会发生任何操作。让我们修复这个问题。
对检测到的图像做出响应
您的脚本可以订阅事件,以便在图像被跟踪、更新或删除时得到通知。具体来说,我们可以实现一个OnTrackedImageChanged函数来处理这些事件。我们可以在PlanetsMainMode脚本中使用它,如下所示:
-
再次打开
PlanetsMainMode脚本进行编辑,并添加以下代码:void OnTrackedImageChanged (ARTrackedImagesChangedEventArgs eventArgs) { foreach (ARTrackedImage newImage in eventArgs.added) { InstantiatePlanet(newImage); } } -
将以下行添加到您的
OnEnable函数中,为imageManager添加监听器:imageManager.trackedImagesChanged += OnTrackedImageChanged; -
同样,在
OnDisable中移除监听器:void OnDisable() { imageManager.trackedImagesChanged -= OnTrackedImageChanged; }当
ARTrackedImageManager检测到新图像时,主模式脚本将启动。它包含事件监听器,并将为任何新跟踪的图像调用InstantiatePlanet。 -
如果应用程序完全丢失图像跟踪,我们应该回到扫描模式并显示其教学图形,提示用户找到参考图像。将此检查添加到
Update中,如下所示:void Update() { if (imageManager.trackables.count == 0) { InteractionController.EnableMode("Scan"); } }小贴士——跟踪单个可追踪对象的状态
AR Foundation 还为您提供了每个可追踪图像的当前跟踪状态。给定一个可追踪图像(
ARTRackedImage),您可以检查其trackingState以确定是Tracking——图像正在积极跟踪,Limited——图像正在被跟踪但不可靠,或None——图像没有被跟踪。请参阅docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.1/manual/tracked-image-manager.html``#tracking-state。在本项目中,我们只有在没有图像被跟踪时才会回到扫描模式,因此我们不一定需要这一额外的状态监控级别。
好的——这已经相当稳健了。构建并运行项目,这次扫描地球和火星的行星卡片(或两者都扫描)。我们得到了行星!以下屏幕截图显示了应用程序正在运行,底部增加了信息 UI,我们将在下一节中添加:

图 8.20 – 运行时渲染的地球和火星
按照这些相同的步骤继续添加其余的行星到你的项目中。正如我们在本章前面提到的,参考nssdc.gsfc.nasa.gov/planetary/factsheet/index.html提供的 NASA 数据,他们的日长行对于我们的Planet脚本已经包含了一个动画速度标量animationHoursPerSecond,你可以用它来修改在应用程序中可视化的旋转速率。
现在我们应用程序支持多个行星,你可能想告诉用户更多关于他们正在查看的特定行星的信息。让我们将此功能添加到主模式中,以便它能够响应式地更新 UI。
制作响应式 UI
在本节中,我们将向屏幕底部添加一个信息面板(如图 8.20 所示的屏幕截图)。当你将相机对准一个或另一个行星时,我们将显示行星的名称,以及一个信息按钮,点击该按钮将显示包含该行星更多信息的文本框。
创建主模式 UI
当应用程序处于主模式时,主 UI 面板将显示。在此面板上,我们将显示当前行星的名称,并为用户提供一个信息按钮,以便在需要更多关于该行星的详细信息时按下。执行以下步骤:
-
在层次结构窗口中,展开UI 画布对象及其子主 UI对象。
-
面板中的默认子文本是一个临时占位符,因此我们可以将其删除。右键单击子文本对象并选择删除。
-
通过右键单击
信息面板创建一个子面板。 -
使用
175。 -
我将背景设置为
255。 -
为行星名称创建一个文本元素。右键单击
行星名称文本。 -
在行星名称文本
[行星名称]。 -
设置文本属性;例如,
50;72。 -
创建一个
信息按钮。 -
设置按钮属性;例如,
150, 150;-20。 -
展开并设置
-50及其文本内容为信息。 -
右键单击
?,72,-15。 -
我们将使用此按钮来切换详细信息的面板开/关。因此,让我们用切换组件替换其按钮组件。在层次结构窗口中选择信息按钮对象,在检查器窗口中,使用3 点上下文菜单选择移除组件来移除按钮组件。
-
选择
切换,并添加一个切换组件。
我的主要信息面板现在看起来如下:
![图 8.21 – 主 UI 的信息面板,包含行星名称文本和信息按钮
![图 8.21 – 主 UI 的信息面板,包含行星名称文本和信息按钮
图 8.21 – 主 UI 的信息面板,包含行星名称文本和信息按钮
行星名称文本的内容将在运行时填充。让我们现在添加这段代码。
指向相机以显示信息
计划是在场景中实例化一个或多个虚拟行星后,用户可以将相机指向一个行星,使其在信息面板中显示行星的名称。这可以通过使用Physics Raycast来实现。(射线投射在前面章节中已介绍并解释。见docs.unity3d.com/ScriptReference/Physics.Raycast.html)。回想一下,在本章开头,我们将行星预制件放置在一个名为PlacedObjects的层上。我们在这里将利用这一点。
对PlanetsMainMode脚本进行以下更改:
-
确保脚本文件包含以下程序集引用:
using TMPro; using UnityEngine.UI; -
在类的顶部,声明并初始化对 AR
camera和layerMask变量的引用,如下所示:Camera; int layerMask; void Start() { camera = Camera.main; layerMask = 1 << LayerMask.NameToLayer("PlacedObjects"); } -
还在信息面板中添加对
planetName和infoButtonUI 元素的引用:[SerializeField] TMP_Text planetName; [SerializeField] Toggle infoButton; -
当模式启用时,我们可以初始化 UI 设置。请将以下行添加到
OnEnable函数中:planetName.text = ""; infoButton.interactable = false; -
然后,将以下高亮代码添加到
Update函数中:void Update() { if (imageManager.trackables.count == 0) { InteractionController.EnableMode("Scan"); } else { Ray = new Ray(camera.transform.position, camera.transform.forward); RaycastHit hit; if (Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask)) { Planet = hit.collider. GetComponentInParent<Planet>(); planetName.text = planet.planetName; infoButton.interactable = true; } else { planetName.text = ""; infoButton.interactable = false; } } } -
保存脚本。回到 Unity 中,在层次结构窗口中选择主模式对象。
-
将行星名称文本游戏对象从层次结构窗口(位于UI Canvas / 主 UI / 信息面板下)拖动到行星主模式 | 行星名称槽中。
-
将信息按钮对象拖动到信息按钮槽中。
继续构建并运行项目。在查看一个或多个行星时,当你将设备的相机指向其中一个时,行星的名称将在屏幕底部的信息面板中显示。
最后,让我们设置信息按钮和描述显示。
显示信息详情
当用户将相机指向场景中的虚拟 3D 行星时,我们在信息面板中显示行星的名称。当用户点击信息按钮时,我们希望显示有关行星的更多信息,例如其描述文本。现在让我们通过以下步骤添加一个文本面板:
-
在详细信息面板中。
-
它已经设置为
30、30、150和200。 -
右键点击详细信息文本。
-
格式化文本区域;例如,设置其
48、30, 30, 30, 30和其对齐方式:为居中,中间。
现在,将此面板的控制权添加到PlanetsMainMode脚本中,如下所示:
-
在类的顶部添加对
detailsPanel和detailsText的引用:[SerializeField] GameObject detailsPanel; [SerializeField] TMP_Text detailsText; -
确保在模式启用时隐藏面板。将以下行添加到
OnEnable函数中:detailsPanel.SetActive(false); -
当选择行星时初始化面板的内容。也就是说,在
Update中,我们必须同时设置detailsText和planetName:if (Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask)) { Planet = hit.collider. GetComponentInParent<Planet>(); planetName.text = planet.planetName; detailsText.text = planet.description; infoButton.interactable = true; } else { planetName.text = ""; detailsText.text = ""; infoButton.interactable = false; }保存脚本。回到 Unity 中,我们将连接信息按钮切换。
-
在层次结构窗口中选择信息按钮,在检查器窗口中,点击Toggle | On Value Changed动作列表右下角的+按钮。
-
从层次结构窗口中,将详细信息面板游戏对象拖放到On Value Changed | Object槽中。
-
从功能选择列表中,选择GameObject | Dynamic Bool | SetActive。
-
保存场景。
现在,当你构建并运行项目并查看一个行星时,然后按下信息按钮,详细信息面板将显示在行星描述文本旁边,如下面的手机屏幕截图所示:
![图 8.22 – 在切换的详细信息面板中显示关于火星的描述文本
![图 8.22 – 在切换的详细信息面板中显示关于火星的描述文本
图 8.22 – 在切换的详细信息面板中显示关于火星的描述文本
在本节中,我们向场景添加了一个响应式 UI。当用户将他们的设备相机指向场景中实例化的虚拟行星时,行星的名称会显示在屏幕底部的信息面板中。如果用户点击信息按钮,一个文本面板会被切换,显示有关该特定行星的更多详细信息。
你能想到其他改进这个项目的办法吗?
摘要
在本章中,你构建了一个 AR 项目,让你能够可视化并学习我们太阳系中的行星。场景使用了 AR 图像检测,并跟踪了你从本书提供的 PDF 文件中打印出的行星卡片。每张行星卡片图像都包含一个独特的标记,具有独特的细节、高对比度的边缘和不对称的形状,这使得它们易于被 AR 系统检测和跟踪。你设置了 AR 会话,使用 AR 可追踪图像管理器组件来跟踪图像,并使用行星卡片图像构建了一个参考图像库资产。
然后,你创建了一个通用的行星预制件,其中包含控制行星旋转行为和元数据的行星脚本。然后,你为每个行星创建了单独的预制件变体。你编写了一个PlanetsMainMode脚本,当检测到特定的行星卡片图像时,它会实例化正确的行星预制件。这允许场景中存在多个跟踪图像和行星。然后,你添加了一个响应式 UI,用户可以将他们的设备相机指向一个实例化的行星,并获取有关该虚拟对象的更多信息。
在下一章中,我们将探索另一种类型的 AR 应用:翻转设备相机,使其面向用户,制作自拍面部滤镜。
第九章:第九章:自拍:制作搞笑的面孔
在本章中,您将学习如何使用 Unity AR Foundation 进行面部追踪,以便制作有趣且娱乐性的面部滤镜。我提前为在这章中展示我英俊的面孔道歉——当与自拍一起工作时,这是必要的恶行!
我们将从面部追踪的工作原理的简要说明开始,然后我们将创建一个启用面部追踪的新 AR 场景。我们将使用几个 3D 头部模型,这些模型可以追踪您的头部姿态,并且您可以添加额外的配件,如帽子和太阳镜。我们将构建一个主菜单,以便用户可以在运行时选择和更改模型。然后我们将处理动态面部网格,并创建几个材料,以便轻松地在它们之间切换。在最后一部分,我们将探讨更高级的功能,如眼动追踪、面部区域(ARCore)和混合形状(ARKit)。
我们将涵盖以下主题:
-
理解面部追踪
-
配置新的 AR 场景进行面部追踪
-
使用 3D 模型和配件追踪面部姿态
-
控制应用程序的主模式并构建主菜单
-
使用各种材料制作动态面部网格
-
使用眼动追踪(ARKit)
-
将贴纸附加到面部区域(ARCore)
-
追踪表情丰富的面部混合形状(ARKit)
到本章结束时,您将熟悉 AR Foundation、ARCore 和 ARKit 中许多面部追踪功能。您还将拥有一个可以展示给朋友的面部制作器项目!
技术要求
要实现本章中的项目,您需要在您的开发计算机上安装 Unity,并将其连接到支持增强现实应用程序的移动设备(有关说明,请参阅第一章,为 AR 开发设置)。我们还假设您已安装ARFramework模板及其先决条件(请参阅第五章,使用 AR 用户框架)。完成的项目可以在本书的 GitHub 存储库中找到,网址如下:github.com/PacktPublishing/Augmented-Reality-with-Unity-AR-Foundation。
理解面部追踪
让我们从面部追踪及其工作原理的背景知识开始。面部追踪是一种增强现实技术(通常)使用您的移动设备的正面摄像头。Snapchat、Instagram 和 Animoji 等应用程序普及了面部滤镜技术,现在它已成为移动设备上的主流。它为高度娱乐和创造性的体验提供了可能。该技术检测面部特征和表情,Unity AR Foundation 使您能够编写将 3D 对象附加到追踪到的特定面部特征的应用程序。
人脸追踪从您设备摄像头的视频帧开始。它分析像素,寻找代表人脸的图案——例如,鼻梁的像素比周围的像素亮,眼睛比额头暗。关键点和区域被识别并用于构建一个类似于面具的 3D 网格,代表人脸。网格的节点“锁定”在图像的关键点上,使得网格不仅能跟随人脸的姿态,还能跟随与人类面部表情相对应的详细变化,如微笑或眨眼。
要了解更多关于人脸追踪如何工作,我鼓励您观看由 Vox 制作的开创性视频(超过 300 万次观看)Snapchat 的滤镜是如何工作的,可在以下 URL 查看视频:www.youtube.com/watch?v=Pc2aJxnmzh0。
了解人脸追踪与人脸识别之间的区别,以及如何使用 AR Foundation 追踪人脸是有帮助的。
人脸追踪与人脸识别的区别
应该区分人脸追踪和人脸识别。通常,人脸追踪限于检测人脸和跟踪其姿态(位置和旋转)、面部特征,如额头和鼻子,以及代表表情的变化,如张嘴或眨眼。另一方面,人脸识别增加了识别使您的面孔独特且与其他面孔不同的特征。面部识别被用作指纹。面部识别技术的一个例子是用于解锁设备。更高级(且令人不安)的人脸识别越来越多地被威权政府和执法机构用于在人群中识别陌生人,使用的是大量的人脸数据库。
使用 Unity AR Foundation,您可以访问设备的人脸追踪功能。我们将接下来探讨这一点。
使用 AR Foundation 追踪人脸
如您现在所知,使用 AR Foundation 和 XR 插件的 Unity 项目将包含一个场景,该场景包括一个 ARSession 和一个 ARSessionOrigin 对象。将 AR Face Manager 组件添加到 AR Session Origin 中以启用人脸追踪。像大多数 AR Foundation 功能一样,此组件封装了 Unity AR 子系统,即 XR 面部子系统(见 docs.unity3d.com/Packages/com.unity.xr.arsubsystems@4.2/api/UnityEngine.XR.ARSubsystems.XRFaceSubsystem.html)。这反过来又与底层的 XR 插件接口,例如 ARCore 或 ARKit。
AR 人脸管理器组件引用您提供的脸预制件。此预制件将被实例化并跟踪检测到的人脸。如果需要应用程序支持同一摄像头视图中多个人(取决于底层设备的功能),组件还提供了一个最大人脸数量参数。组件在以下屏幕截图中显示:
![图 9.1 – AR 会话源对象上的 AR 人脸管理器组件]
![img/Figure_9.01_B15145.jpg]
图 9.1 – AR 会话源对象上的 AR 人脸管理器组件
人脸预制件应该有一个表示由 AR 设备检测到的人脸的AR 人脸组件。它具有包括人脸网格顶点、面法线和左右眼变换等属性。与其他 AR 可追踪对象一样,你的脚本可以订阅变化,以便知道何时添加、更新和删除人脸。具体可用的属性将取决于底层设备的功能。请参阅以下 URL 提供的文档:docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.2/api/UnityEngine.XR.ARFoundation.ARFace.html。另外,请参阅以下 URL:docs.unity3d.com/Packages/com.unity.xr.arsubsystems@4.2/api/UnityEngine.XR.ARSubsystems.XRFace.html。
AR Foundation 提供了一个用于 AR 人脸跟踪(非识别)的接口,通过将 AR 人脸管理器组件添加到您的 AR 会话源对象。现在我们可以开始构建自拍人脸过滤器项目了。
入门
首先,我们将使用ARFramework场景模板创建一个名为FaceMaker的新场景。如果您针对 iOS ARKit,可能需要额外的设置,包括安装单独的 ARKit 人脸跟踪包。然后,我们在添加人脸跟踪到场景之前,将项目标题添加到 UI 中。
使用 ARFramework 模板创建新场景
使用以下步骤在你的 Unity AR 准备好的项目中创建一个新的场景:
-
选择文件 | 新建场景。
-
在新建场景对话框中,选择ARFramework模板。
-
点击创建。
-
在您的项目
Assets文件夹中的Scenes/文件夹中,将其命名为FaceMaker,然后点击保存。
新的 AR 场景已经从模板中包含了以下设置:
-
AR 会话游戏对象,其中包含 AR 会话组件。
-
AR 会话源装置,其中包含 AR 会话源组件和其他组件,以及一个子主摄像头。我们将用 AR 人脸管理器组件替换其 AR 平面管理器组件。
-
UI Canvas是一个屏幕空间画布,包含我们为ARFramework构建的子面板Startup UI、Scan UI、Main UI和NonAR UI,它包含我们编写的 UI 控制器组件脚本。我们将使用项目特定的 UI 更新它。
-
Interaction Controller是我们为ARFramework构建的游戏对象,包含我们编写的交互控制器组件脚本,帮助应用在启动、扫描、主和非 AR 模式之间切换交互模式。它还包含配置了之前创建的AR Input Actions资产的Player Input组件。我们将为我们的面部追踪应用定制主模式。
-
OnboardingUX是来自 AR Foundation Demos 项目的预制件,提供 AR 会话状态消息和动画引导图形提示。
让我们从设置应用标题开始,如下所示:
-
在层次结构中,展开UI Canvas对象,然后展开其子对象App Title Panel。
-
选择Title Text对象。
-
在其
Face Maker中。
如果您针对 iOS 上的 ARKit,可能需要额外的项目设置。
设置 iOS ARKit 以进行面部追踪
要使用 ARKit 在 iOS 设备上开发和使用面部追踪的项目,您还需要通过包管理器安装 ARKit Face Tracking 包。执行以下步骤:
-
使用Window | Package Manager打开包管理器。
-
在左上角的包过滤器选择中,选择Unity Registry。
-
使用右上角的搜索字段搜索
ar,并从包列表中选择ARKit Face Tracking包。 -
在窗口的右下角点击Install。
然后,按照以下步骤配置 ARKit XR Plugin 以进行面部追踪:
-
使用Edit | Project Settings打开Project Settings窗口。
-
在左侧的选项卡菜单中,选择XR Plug-in Management | ARKit。
-
选择Face Tracking复选框。
接下来,我们将收集一些在本章中将要使用的资产。其中一些也包含在本书的 GitHub 仓库中。其他是一些第三方资产,您必须单独下载并导入。
导入项目中使用的资产
首先,您应该已经在项目中拥有AR Foundation Samples资产(我们在第一章,为 AR 开发设置)中导入的资产。如果您跟随着步骤操作,这些资产位于Assets/ARF-samples/文件夹中。它包含一些有用的示例资产,我们将在本章中使用并参考,这些资产可以为您提供关于 AR Foundation 面部追踪功能的额外见解,以及如何使用这些功能。
我们还将使用来自 Unity 的 AR Face Assets 包中的资产(可在资产商店中找到)。这些资产也用于 Unity Learn 教程,使用 AR Foundation 进行 AR 面部追踪 (learn.unity.com/project/ar-face-tracking-with-ar-foundations)。要导入包,请按照以下步骤操作:
-
使用你的网络浏览器,访问以下网址:
assetstore.unity.com/packages/essentials/asset-packs/ar-face-assets-184187。 -
点击 添加到我的资产(如有必要),然后点击 在 Unity 中打开。
-
在 Unity 中,这应该会打开 包管理器 窗口(或选择 窗口 | 包管理器)。
-
从左上角的 包 过滤器中选择 我的资产。
-
找到 AR Face Assets 包,点击 下载 和/或 导入(右下角)。在 导入 Unity 包 窗口中,点击 导入 按钮。
-
通过选择 编辑 | 渲染管线 | 通用渲染管线 | 升级项目材质到 URP 材质,将导入的材质转换为通用渲染管线。
面部配件 3D 模型:我在这个项目中找到了一些免费的 3D 模型。你也可以使用它们或替换成你自己的。如果你想使用它们,它们包含在以下 GitHub 仓库中:
-
太阳镜:
free3d.com/3d-model/sunglasses-v1--803862.html。OBJ 格式(由 printable_models 提交,free3d.com/user/printable_models) -
高顶帽:
free3d.com/3d-model/cartola-278168.html.FBX 格式(由 zotgames 提交,free3d.com/user/zotgames)
如果你自行下载,请解压并将文件拖入项目文件夹的 Assets/ 中。我们将在本章后面讨论导入设置和步骤。
面部贴纸 2D 精灵图像:对于基于 ARCore 的面部区域贴纸,我在 Creative Commons 找到了一些免费的剪贴画。你也可以使用它们或替换成你自己的。如果你想使用它们,它们包含在以下 GitHub 仓库中:
我使用 Photoshop 将这些图像的背景调整为透明,画布形状为方形,并缩放到 512x512 像素。这些图像被导入为 纹理类型:精灵(2D 和 UI)。
对于上述所有资产,我还创建了我们将用于 UI 的按钮图标。这些图标也位于 GitHub 仓库的icons/文件夹中,并作为纹理类型:精灵(2D 和 UI)导入。
现在我们已经创建了基本场景,并将必需的资产导入到项目中。我们创建新场景时使用了为本书创建的ARFramework场景模板,并更新了此项目的 UI 标题文本。如果您在 iOS 上工作,我们还向项目中安装了额外的必需包。然后,我们导入了我们将要使用的其他图形资产,包括 Unity 提供的 demo AR Face Assets 包。现在,让我们为人脸跟踪配置场景。
为人脸跟踪配置新的 AR 场景
配置基于 AR Foundation 的场景进行人脸跟踪需要几个简单的步骤。由于我们将进行自拍,我们将设置 AR 相机使用前置摄像头的输入。然后,我们将向 AR Session Origin 添加一个 AR Face Manager 组件。如果您想使用 Unity Onboarding UX 动画图形来提示用户,您可以为此修改ScanMode脚本。
让我们开始吧!
设置 AR 相机用于自拍
使用以下步骤设置 AR 相机用于自拍:
-
在层次结构中展开AR Session Origin游戏对象,并选择其子对象Main Camera。
-
在检查器中,将AR Camera Manager | 面向方向设置为用户。
-
我们还需要将 AR Session 跟踪模式设置为仅旋转。在层次结构中选择AR Session游戏对象。
-
在检查器中,将AR Session | 跟踪模式设置为仅旋转。
接下来,我们将把 AR Face Manager 组件添加到 AR Session Origin 中。
添加 AR Face Manager 组件
使用ARFramework模板提供的场景,我们将用AR Face Manager组件替换给定的 AR 可跟踪组件。对于Face Prefab,我们将从 AR Samples 项目中的TriAxes预制件开始。如果您检查这个预制件,您会发现它有一个AR Face组件,因此它可以作为可跟踪对象使用。
要配置AR Session以跟踪人脸,请按照以下步骤操作:
-
在层次结构窗口中,选择AR Session Origin游戏对象。
-
在检查器窗口中,在AR Plane Manager组件上使用三点上下文菜单(或右键点击),然后选择移除组件。
-
使用
AR,并添加一个AR Face Manager组件。 -
在您的
Assets/ARF-samples/Prefabs/文件夹中),将其拖动到检查器中,并将其放置在AR Face Manager | Face Prefab槽中。 -
使用文件 | 保存保存场景。
场景现在基本上已经为人脸跟踪设置好了。ARFamework包括一个提示用户使用相机寻找可跟踪对象的扫描模式。我们现在可以为此配置人脸跟踪。
提示用户寻找人脸,或是不寻找
可选地,你可以让应用提示用户扫描人脸。当使用自拍相机(面向方向设置为用户)时,这不太必要,因为当你拿着手机看屏幕时,相机正对着你!但如果你的应用使用面向世界的相机,可能需要使用指导提示告诉用户找到人脸。
要跳过扫描模式和其指导提示,告诉启动模式直接进入主模式,使用以下步骤:
-
在层次结构中,在Interaction Controller游戏对象下,选择Startup Mode对象。
-
在
Main中,将Next Mode属性设置为Next Mode。
否则,如果你想使用扫描模式,你需要编写一个如下的FaceScanMode脚本:
-
在你的
Scripts/文件夹中,右键点击并选择FaceScanMode。 -
打开脚本进行编辑,并替换其内容如下:
using UnityEngine; using UnityEngine.XR.ARFoundation; public class FaceScanMode : MonoBehaviour { [SerializeField] ARFaceManager faceManager; private void OnEnable() { UIController.ShowUI("Scan"); } void Update() { if (faceManager.trackables.count > 0) { InteractionController.EnableMode("Main"); } } }脚本显示
Update,等待检测到人脸后再将应用切换到主模式。 -
在 Unity 中,在层次结构窗口中,选择Scan Mode对象(在Interaction Controller下)。
-
使用三点上下文菜单删除旧的Scan Mode组件,然后选择移除组件。
-
将新的
FaceScanMode脚本拖到Scan Mode游戏对象上,添加为组件。 -
将AR Session Origin游戏对象从层次结构拖到Face Scan Mode | Face Manager槽中。
-
在层次结构中,导航并选择UI Canvas | Scan UI | Animated Prompt。
-
在检查器中,将Instruction属性设置为Find A Face。
使用这个后置设置,应用以启动模式开始。在 AR 会话运行后,它进入扫描模式,提示用户找到人脸。一旦检测到人脸,应用进入主模式(目前,这什么也不做)。你也可以选择通过告诉启动模式直接进入主模式来完全跳过扫描模式提示。
让我们确保到目前为止一切正常。你现在可以尝试运行场景。
构建并运行
让我们在你的设备上进行构建和运行,以确保项目设置正确。使用以下步骤:
-
使用文件 | 保存保存你的工作。
-
选择文件 | 构建设置以打开构建设置窗口。
-
点击
FaceMaker场景到构建中的场景,并确保它是列表中唯一带有勾选标记的场景。 -
确保你的目标设备已连接到 USB 端口并准备就绪。
-
点击构建和运行来构建项目。
在下面的屏幕截图上,你可以看到人脸姿态是通过TriAxes预制件可视化的。我已经稍微倾斜了头部,以便更明显地看到三个轴。

图 9.2 – 使用 TriAxes 预制件可视化跟踪人脸姿态
注意每个轴的方向。轴的颜色为红色、绿色和蓝色,分别对应 X、Y 和 Z。正 Z 方向是设备相机面对的方向,因此,指向我的背部。
现在我们已经运行了面部追踪,让我们用更有趣的东西替换这个TriAxes预制件——一个完整的 3D 头模型。
使用 3D 头部追踪面部姿态
在本章顶部导入的 Unity AR Face Assets 包中包含了一些 3D 头模型,我们可以在我们的项目中使用。我们将为每个模型创建预制件,并在 AR Face Manager 的Face Prefab属性中分别尝试它们。在下一节中,我们将创建一个菜单,以便用户可以在运行时选择查看哪个头。
制作 Mr. Plastic Head 预制件
第一个头部预制件将使用 Unity AR Face Assets 包中提供的 Plasticscene Head 资产,位于Assets/AR face Assets/3D Head/Plasticene Head/文件夹中。此文件夹包含一个名为Plasto_Head的 FBX 模型和一个名为PlasiceneHead的材质(错误是他们的)。在用作面部预制件之前,该模型需要一些变换调整。要为该模型创建预制件,请按照以下步骤操作:
-
在
Prefabs/文件夹中(如果需要,请先创建一个)并选择MrPlasticHead。 -
点击打开预制件开始编辑。
-
在选择根对象后,在检查器中点击添加组件。然后,搜索并选择AR Face来添加一个AR Face组件。
-
从
Plastic_Head模型到层次结构,并将其作为MrPlasticHead的子对象拖放。 -
选择
180,使其面向相机。 -
设置
0.6, 0.6, 0.6。然后设置-0.2。我通过试错和使用测量立方体(见插图提示)选择了这些变换设置。 -
如果默认材质(转换为 URP)看起来太暗,请选择子对象Plaso_Head/Plasto_Head,然后在检查器中,在Plasticene Head材质下,将基础贴图颜色设置为白色(从中间灰色)。
-
保存预制件并退出回到场景
0, 0, 0),0, 0, 0),和0.125, 0.125, 0.125)。这可以帮助你决定你使用的其他导入模型的变换参数。
让我们看看这看起来怎么样。将预制件添加到AR Face Manager,并按照以下步骤构建项目:
-
在层次结构窗口中,选择AR Session Origin游戏对象。
-
从项目窗口,将MrPlasticHead预制件拖放到检查器中,将其拖放到AR Face Manager | Face Prefab槽中。
-
使用文件 | 保存来保存场景。
-
使用
PlasticeneHead材质构建项目,该材质为基础(反照率)、法线和遮挡图使用了三种纹理。基础纹理提供了反照率着色,就像网格的表面被这些像素涂上一样。法线图(也称为凹凸图或高度图)允许着色器以比网格几何本身给出的更详细的方式改变数学表面法线向量,模拟出在光照下特别明显的表面纹理。最后,遮挡图通过加深表面纹理中的凹槽,提供额外的真实感,创造出类似于现实材料中的更高对比度。对于更详细的解释,从法线图开始,请参阅以下网址:docs.unity3d.com/Manual/StandardShaderMaterialParameterNormalMap.html。
下面显示了我使用 Mr. Plastic Head 头部的屏幕截图,以及我们将要使用的 Mr. Facet Head 模型:
![Figure 9.3 – 屏幕截图:我使用 MrPlasticHead(右)和 MrFacetHead(左)]
Figure 9.3 – 屏幕截图:我使用 MrPlasticHead(右)和 MrFacetHead(左)
让我们制作MrFacetHead预制体。
制作 Mr. Facet Head 预制体
AR Face Assets 包中提供了一个第二个模型,Faceted Head,位于Assets/AR face Assets/3D Head/Faceted Head/文件夹中。此文件夹包含一个名为FacetedHead的 FBX 模型和一个也命名为FacetedHead的材质。与之前一样,该模型需要一些变换调整才能用作面部预制体。要为该模型创建预制体,请按照以下步骤操作:
-
在
Prefabs/文件夹中,选择MrFacetHead。 -
点击打开预制体开始编辑。
-
在选择根对象后,在检查器中,点击添加组件。然后,搜索并选择AR Face以添加AR Face组件。
-
从
FacetedHead模型到层次,并将其作为MrFacetHead的子对象放下。 -
将
-90调整到面向摄像机的位置。设置1.1, 1.1, 1.1)。 -
如果默认材质(转换为 URP)看起来太暗,选择FacetedHead对象,并在其检查器下的FacetedHead材质中,将基础图颜色设置为白色。
-
保存预制体,并使用窗口左上角的<按钮返回到场景层次窗口。
-
在层次窗口中,选择AR 会话原点游戏对象。
-
从项目窗口,将MrFacetHead预制体拖到检查器中,将其放置在AR Face Manager | Face Prefab槽中。
-
使用文件 | 保存保存场景。
-
使用文件 | 构建并运行构建项目。
当它运行时,你现在有一个 Mr. Faceted Head 头,如图所示(是的,那些是我的真实眼睛透过面具看出去的)。
在本节中,我们创建了两个 prefab,MrPlasticHead 和 MrFacetHead,使用我们之前导入的 Unity AR Face Assets 包中的资产。每个这些 prefab 都在其根 GameObject 上有一个 AR Foundation AR Face 组件,并为两个头部导入不同的模型。我们尝试在我们的应用中使用其中一个,通过将其添加到 AR Face Manager 组件并运行场景来测试。
如果用户可以在运行时选择头部,而不是手动设置 AR Face Manager 并重新构建项目,那岂不是很好?接下来,让我们创建一个主菜单和一个可从菜单按钮控制的可变脸 prefab。
构建 Main 模式和菜单
在本节中,我们将设置主模式应用以处理用户交互,包括从主菜单中选择面部滤镜。为此,我们首先需要创建一个可变脸 prefab,可以指定要显示哪些面部特征。我们将编写一个 FaceMainMode 脚本,显示主 UI 面板并将用户的更改请求传递给面部对象。然后,我们将制作一个带有水平滚动按钮的主菜单,用户可以点击这些按钮来更改面部滤镜。
创建可变脸 prefab
要创建一个在运行时可以动态更改滤镜的 face prefab,我们将从一个带有 AR Face 组件的空游戏对象开始,并添加一个用于设置包含的 prefab 对象的脚本。按照以下步骤操作:
-
在
Prefabs/文件夹中并选择Changeable Face Prefab。 -
点击 打开 Prefab 以开始编辑。
-
选择根对象后,在 Inspector 中点击 添加组件。搜索并选择 AR Face 以添加 AR Face 组件。
-
在你的
Scripts/文件夹中,右键点击 并选择ChangeableFace。 -
打开脚本进行编辑,并按照以下内容替换其内容:
using System.Collections.Generic; using UnityEngine; using UnityEngine.XR.ARFoundation; public class ChangeableFace : MonoBehaviour { GameObject currentPosePrefab; GameObject poseObj; public void SetPosePrefab(GameObject prefab) { if (prefab == currentPosePrefab) return; if (poseObj != null) Destroy(poseObj); currentPosePrefab = prefab; if (prefab != null) poseObj = Instantiate(prefab, transform, false); } }该脚本公开了一个
SetPosePrefab公共函数,该函数将prefab参数实例化为当前对象的子对象。如果请求的 prefab 已经实例化,则忽略请求。如果有之前实例化的对象,它首先被销毁。该函数可以用prefab参数的 null 值调用,这将仅清除现有的实例化对象。 -
保存脚本,然后在 Unity 中,将
ChangeableFace脚本拖放到 Changeable Face Prefab 根对象上。 -
保存 prefab 并使用窗口左上角的 < 按钮退出回到场景层次结构。
-
在 Hierarchy 中选择 AR Session Origin 对象。从 Project 窗口,将 Changeable Face Prefab 拖入 Inspector,并将其放置在 AR Face Manager | Face Prefab 槽中。
现在我们有了 ChangeableFace 脚本。我们计划在主模式中响应用户按钮点击时调用其 SetPosePrefab 函数。我们现在应该设置主模式。
编写主模式控制器脚本
在我们的 ARFramework 模板中,交互模式通过交互控制器下的游戏对象表示,并在启用特定模式时激活。模板中的默认MainMode脚本只是一个占位符。我们现在应该用这个项目的自定义脚本替换它。为此,请按照以下步骤操作:
-
在
FaceMainMode。 -
在层次结构中,选择主模式游戏对象(位于交互控制器下)。
-
在检查器中,使用三点菜单移除默认的主模式组件,然后点击移除组件。
-
将新的
FaceMainMode脚本拖放到主模式对象上,添加为组件。 -
打开
FaceMainMode脚本进行编辑,并按照以下步骤启动:using UnityEngine; using UnityEngine.XR.ARFoundation; public class FaceMainMode : MonoBehaviour { [SerializeField] ARFaceManager faceManager; void OnEnable() { UIController.ShowUI("Main"); } public void ChangePosePrefab(GameObject prefab) { foreach (ARFace face in faceManager.trackables) { ChangeableFace changeable = face.GetComponent<ChangeableFace>(); if (changeable != null) { changeable.SetPosePrefab(prefab); } } } }
当主模式启用时,它显示主 UI 面板。这将包含主菜单按钮。当点击菜单按钮并调用ChangePosePrefab时,它将依次调用场景中任何可跟踪人脸的SetPosePrefab。
让我们创建菜单 UI。
创建可滚动的主菜单按钮
在我们的用户框架中,一个模式的 UI 面板将通过相应的交互模式启用。现在,我们将添加一个水平滚动的菜单到主 UI 面板,其中包含可以更改跟踪人脸的按钮。请按照以下步骤操作:
-
在
MainMenu Panel。 -
在检查器中,使用锚点预设选项(位于矩形变换的右上角)设置底部拉伸,然后使用Shift + Alt + 左键点击底部拉伸。
-
设置
150。 -
使用三点菜单移除其图像组件,然后移除组件(我们不会在这个菜单上使用背景)。
-
在层次结构中,右键点击MainMenu Panel,然后选择UI | 滚动视图。
-
使用锚点预设点击拉伸-拉伸选项,然后使用Shift + Alt + 左键点击拉伸-拉伸。
-
移除图像组件。
-
在滚动矩形组件中,取消勾选垂直。
-
删除水平滚动条和垂直滚动条字段的内容,并在层次结构中禁用(或删除)滚动条水平和滚动条垂直游戏对象。
-
在层次结构中,展开子视口游戏对象,并选择子内容游戏对象。
-
点击添加组件,然后搜索并选择水平布局组。
-
取消勾选所有复选框,包括子强制扩展 | 宽度和高度。
-
设置
5。 -
点击添加组件,然后搜索并选择内容大小适配器。
-
将水平适配设置为首选大小。
现在,我们在主 UI下有一个MainMenu Panel。它包含一个水平滚动的内容区域,如下面的 UI 层次结构截图所示,其中选择了内容对象:
![图 9.4 – 显示内容检查器的主 UI 层次结构]
![img/Figure_9.04_B15145.jpg]
图 9.4 – 显示内容检查器的主 UI 层次结构
现在我们可以向内容容器添加按钮。目前,我们将创建两个按钮,用于两个头部。稍后,我们将通过更多选项来扩展它。每个按钮将显示一个图像图标(如果您没有自己内容的图标,可以使用文本标签):
-
在
PlasticHead Button。 -
设置其
150, 150)。 -
删除其子文本对象(除非您没有为该按钮的图标图像)。
-
从
plastichead icon图像资产(可能位于您的/icons文件夹中)到图像 | 源图像槽位。 -
在检查器中,点击按钮组件On Click区域右下角的+按钮。
-
从层次结构中,将主模式对象(在交互控制器下),拖到检查器中,并将其放到On Click Object槽位。
-
在功能选择列表中,选择FaceMainMode | ChangePosePrefab。
-
从
Prefabs/文件夹)到下面的空参数槽位,如图下所示:

图 9.5 – PlasticHead 按钮的 On Click 动作会将 MrPlasticHead 预制体传递给 FaceMainMode.ChangePosePrefab 函数
小贴士:创建按钮图标
为了创建本章中使用的许多按钮图标,我有时会先制作实际游戏对象的屏幕截图。然后,在 Photoshop 中,通过选择其边缘(使用魔棒工具)来隔离形状,并制作一个带有透明背景的剪影。然后,我在一个方形画布上裁剪图像,并将其调整大小为 256x256,然后将其导出为 PNG 文件。然后,在 Unity 中导入图像,并在导入设置中,将纹理类型设置为精灵(2D 或 UI),然后点击应用。现在,该资产可以作为 UI 精灵在图像组件中使用,如按钮对象上的那些。
现在我们在主菜单中有一个按钮。这是用于选择 MrPlasticHead 模型的。让我们再创建一个按钮,用于 MrFacetHead 预制体。为此,我们可以复制并修改第一个按钮,如下所示:
-
在层次结构中,选择PlasticHead 按钮游戏对象。
-
从主菜单,选择
FacetHead Button。 -
从
facethead icon资产到图像 | 源图像槽位。 -
从
Prefabs/文件夹)到参数槽位(替换已存在的MrPlasticHead预制体)。
主菜单现在有两个按钮。当应用运行时,点击其中一个将在我脸上显示MrPlasticHead。点击另一个将显示MrFacetHead。提供重置按钮以清除所有面部过滤器也会很好。
添加重置面部按钮
我们还可以添加一个重置按钮,将当前姿势对象设置为 null。让我们在FaceMainMode脚本中作为一个单独的函数来做这件事。使用以下步骤:
-
打开
FaceMainMode脚本进行编辑,并添加一个ResetFace函数:public void ResetFace() { foreach (ARFace face in faceManager.trackables) { ChangeableFace changeable = face.GetComponent<ChangeableFace>(); if (changeable != null) { changeable.SetPosePrefab(null); } } } -
在 Unity 中,在
Reset Button下。 -
设置其
150, 150)。移除其图像组件。 -
在其子
Reset上,勾选自动大小复选框,如果你想的话,更改文本顶点颜色。 -
点击点击列表中的+按钮,将主模式对象拖放到对象槽中,并从函数列表中选择FaceMainMode | ResetFace。
我的菜单,在屏幕底部,现在看起来是这样的,有三个按钮:

图 9.6 – 带有三个按钮的主菜单
你现在可以构建并运行项目了。保存你的工作(文件 | 保存)并构建它(文件 | 构建和运行)。你现在有一个小小的 Face Maker 应用,它允许你选择 3D 头像或重置场景!
在本节中,我们创建了一个可更换的面部预制件,你可以在运行时设置其子预制件,以便用户可以为他们的自拍选择不同的头像模型。然后我们创建了一个主菜单面板,其中包含水平可滚动的按钮,并添加了允许用户选择MrPlasticHead、MrFacetHead或重置当前模型的按钮。
接下来,让我们给你的脸添加一些 3D 配饰——太阳镜和帽子。
添加 3D 配饰
假设你现在想给你的脸和头添加配饰。设置与我们所使用的姿势预制件非常相似。为此,我们将介绍一些从网络下载的第三方模型(并在本章顶部导入到你的项目中)。我们还将向可更换的面部预制件添加一个AddAccessory函数,允许用户一次查看多个配饰。
戴着帽子
我在网上找到了一个 3D 帽子(free3d.com/3d-model/cartola-278168.html),我们之前在本章中下载并安装了它。请随意使用此模型,或找到自己的模型添加到项目中。我将其安装在我的Assets/Models/TopHat/文件夹中。该模型是一个名为CapCartola的 FBX 文件。我们还需要配置其材质。
如果你选择项目窗口中的CapCartola模型并将其展开,你会注意到它有子相机和灯光对象。这对于从某些 3D 建模程序(例如 Blender)导出的模型来说并不罕见。显然,我们不需要这些对象在我们的场景中,所以我们将从导入的模型中移除它们。然后我们将提取并设置材质,然后将它们作为一个预制件组合在一起。按照以下步骤操作:
-
在
CapCartola模型(位于Assets/Models/TopHat/文件夹中)。 -
在检查器中,你会看到导入设置。确保窗口顶部的模型选项卡被选中。
-
取消选择导入相机和导入灯光复选框。然后点击应用。
-
在检查器窗口的顶部选择材质选项卡。
-
点击
Material.001(用于帽子本身)和Material.002(用于其丝带带)。这些已经与模型关联。 -
在
Prefabs/文件夹中,选择TopHat。然后打开预制体进行编辑。 -
从项目窗口,将CapCartola模型拖入层次结构中,在根TopHat对象下创建一个子实例。
-
使用
(0, 0.18, -0.02),(-20, 0, 0),和0.077, 0.077, 0.077))。 -
在层次结构中展开CapCartola,并选择其子Cylinder对象。
-
在
#331D1D下)。 -
同样,在
#FF1919下)。 -
如果你将AR Face组件添加到根对象中,你可以立即通过将其用作AR Face Manager | 面部预制体来测试它。
-
保存预制体并退出场景层次结构。
现在,你有一个可以用来装饰你脸部的TopHat预制体。让我们也添加一副太阳镜。
摩登太阳镜
我在网上找到了一个 3D 太阳镜模型(free3d.com/3d-model/sunglasses-v1--803862.html),我们之前在本章中已下载并安装。我将其安装在我的Assets/Models/Sunglasses/文件夹中。原始模型是一个名为12983_Sunglasses_v2_l3的 OBJ 文件。我们还需要配置其材质。
按以下步骤提取并设置材质,然后将模型组装成预制体:
-
在
12983_Sunglasses_v2_l3模型中选择它。 -
在
sunglasses_body和sunglasses_lens中。 -
选择sunglasses_body材质并根据需要调整它。我将其设置为黑色。镜片材质可能已经是好的(深色且有透明度)。
-
在
Prefabs/文件夹中,选择Sunglasses。 -
打开太阳镜预制体进行编辑。
-
从项目窗口,将12983_Sunglasses_v2_l3模型拖入层次结构中,在根Sunglasses对象下创建一个子实例。
-
使用
(-0.08, -0.025, -0.058),(-90, 90, 09),和0.0235, 0.0235, 0.0235))。 -
如果你也在根对象中添加了AR Face组件,你可以立即通过将其用作AR Face Manager | 面部预制体来测试它。
-
保存预制体并退出场景层次结构。
现在我们有两个可以作为面部配饰使用的模型。你可以通过手动将其中一个添加到AR 会话起源 | AR 面部管理器 | 面部预制体槽位,构建并运行项目来测试它们。完成后,别忘了将可更换面部预制体放回槽位。
接下来,我们将在脚本中添加对这些配饰的支持。
更新配饰脚本
我们需要更新ChangeableFace脚本以管理配饰对象。它将维护当前配饰对象的列表,确保我们只创建任何预制体的一个实例。
当从场景中移除一个配饰对象时,我们不会销毁它,而是将其禁用,如果用户再次添加相同的对象,我们再重新启用它。
我们还需要更新FaceMainMode脚本,添加一个菜单按钮可以调用的函数。这反过来会将请求的预制体传递给ChangeableFace。
按以下步骤更新你的脚本:
-
首先,打开
ChangeableFace脚本进行编辑,并在类的顶部添加以下声明:Dictionary<GameObject, GameObject> accessories = new Dictionary<GameObject, GameObject>();我们使用字典来维护已实例化配件对象的列表,键为预制件。
-
然后,添加以下
AddAccessory函数:public void AddAccessory(GameObject prefab) { GameObject obj; if (accessories.TryGetValue(prefab, out obj) && obj.activeInHierarchy) { obj.SetActive(false); return; } else if (obj != null) { obj.SetActive(true); } else { obj = Instantiate(prefab, transform, false); accessories.Add(prefab, obj); } }AddAccessory将预制件实例化为脸部的子对象,并将其添加到accessories列表中。然而,如果预制件已经被实例化,我们将通过将其设置为非活动状态来从场景中移除它。同样,如果你再次尝试添加它,它将被重新激活。 -
接下来,我们将添加一个
ResetAccessories函数,用于移除所有配件,如下所示:public void ResetAccessories() { foreach (GameObject prefab in accessories.Keys) { accessories[prefab].SetActive(false); } }小贴士:通过使用对象缓存来避免垃圾回收
在这个
AddAccessory函数中,我本可以调用Destroy来移除现有的实例,然后在对象第二次添加时再次调用Instantiate。相反,我通过在不需要时简单地禁用现有对象并在需要时重用相同的实例来管理内存。在运行时重复实例化和销毁对象会导致内存碎片化,并需要 Unity 执行内存Destroy。 -
接下来,我们可以打开
FaceMainMenu脚本进行编辑,并添加一个将被菜单按钮调用的AddAccessory函数,如下所示:public void AddAccessory(GameObject prefab) { foreach (ARFace face in faceManager.trackables) { ChangeableFace changeable = face.GetComponent<ChangeableFace>(); if (changeable != null) { changeable.AddAccessory(prefab); } } } -
接下来,将以下突出显示的代码添加到
ResetFace:public void ResetFace() { foreach (ARFace face in faceManager.trackables) { ChangeableFace changeable = face.GetComponent<ChangeableFace>(); if (changeable != null) { changeable.SetPosePrefab(null); changeable.ResetAccessories(); } } }
我们现在准备好为高顶礼帽和太阳镜配件添加菜单按钮。
在主菜单中添加配件
要向主菜单添加新按钮,我们可以复制一个现有的按钮,并按照以下步骤修改它:
-
在
HatAccessory Button上。 -
从
tophat icon资产拖放到图像 | 源图像槽位。 -
在
FaceMainMode.AddAccessory上。 -
从
Prefabs/文件夹拖放到参数槽位。 -
同样,重复步骤 1-4为
SunglassesAcessory Button,使用sunglasses icon图像和太阳镜预制件资产。
保存场景并构建项目。当你点击帽子按钮时,你将戴上高顶礼帽。再次点击以取下它。在下面的屏幕截图中,我戴着面罩、高顶礼帽和太阳镜。我从未看起来这么酷!


图 9.7 – 我戴着高顶礼帽、太阳镜和面罩的自拍
在本节中,我们通过添加其他要同时跟踪的模型来扩展了基本的面部姿态跟踪功能。我们使用从网络上下载的模型创建了高顶礼帽和太阳镜的预制件。然后,我们更新了ChangeableFace脚本以处理多个配件对象。通过避免相同预制件的重复实例并缓存生成的实例在字典列表中,我们实现了良好的内存管理实践。在将公共AddAccessory函数更新到FaceMainMode脚本后,我们在主菜单中添加了新的按钮,以便用户可以用帽子和/或太阳镜装饰他们的头部。
到目前为止,我们所有的面部都是固定表情的静态模型。AR Foundation 也支持面部的动态可视化。让我们尝试一下。
使用各种材料制作动态面部网格
要显示与你的真实表情匹配的增强现实面部,Unity AR Foundation 允许你在运行时动态生成面部网格。在这个网格上,你可以应用不同的材料,产生你戴着任意面部面具的效果。为了将此功能添加到我们的项目中,我们首先查看 AR Foundation 提供的默认面部游戏对象。然后我们将创建几个不同的材料来使用。为了将此功能集成到我们的项目中,我们将扩展 ChangeableFace 脚本来切换材料,向 FaceMainMode 脚本添加一个类似的功能来更新面部跟踪器,然后添加菜单按钮来切换材料。
探索 AR 默认面部
你可以从 Unity 菜单的 GameObject | XR | AR Default Face 创建一个用于 AR Foundation 的动态面部游戏对象。该对象包含一个 AR Face Mesh Visualizer 组件,该组件在运行时生成与你的面部表情匹配的面部网格,包括移动你的嘴巴和扬起你的眉毛。在我们将此功能添加到我们的 Changeable Face Prefab 之前,让我们快速尝试一下。使用以下步骤:
-
从编辑器菜单栏中选择 GameObject | XR | AR Default Face。这将在场景层次结构中创建一个名为 AR Default Face 的对象。
注意,你不会在场景窗口中看到这个对象,因为网格是在运行时动态生成的,所以还没有东西可以渲染。
-
替换默认材料(包含的默认材料不适合 URP):在
Materials/文件夹中(如有必要,先创建一个),并将其命名为DefaultFace Material。将 基础贴图颜色设置为你的喜欢的颜色。将材质拖动到 AR Default Face 对象上。 -
使其成为一个预制件。将
Prefabs/文件夹拖动。 -
然后从 Hierarchy 中删除它。
-
现在,将预制件拖动到你的 AR 会话原点 | AR 面部管理器 | 面部预制件槽位。
这是一张我戴着默认面具、面带灿烂笑容的屏幕截图,在左边。右边是我的脸部网格在运行时生成的场景视图:
![图 9.8 – 我戴着 AR 默认面具(左)和我脸部网格的线框图(右)]
![img/Figure_9.08_B15145.jpg]
图 9.8 – 我戴着 AR 默认面具(左)和我脸部网格的线框图(右)
用其他材料替换这个默认材料很容易,以制作你自己的面具。
创建面部材料
为了好玩(以及教学目的),让我们尝试使用任意照片作为面部纹理。我将使用名为 WinterBarn.jpg 的图片(这也在 第六章,图库:构建 AR 应用)中使用,按照以下步骤创建一个新的材质:
-
在你的
Materials/文件夹中 右键点击 并选择PhotoFace Material。 -
将照片从
WinterBar.jpg拖动到基础地图纹理芯片上。确保基础地图颜色为白色。 -
复制
PhotoFace Prefab。 -
打开新的预制件进行编辑,并将PhotoFace Material拖动到上面。保存预制件并返回场景层次结构。
-
要尝试它,将PhotoFace Prefab拖动到AR Face Manager | Face Prefab并运行场景。
这应该会给你一种感觉,即二维纹理图像是如何映射到面部网格上的。这被称为PopFace_Albedo面部网格:

图 9.9 – 普通二维图像作为面部纹理(左),以及 UV 映射的面部纹理(右)
这样,你可以使用任何你想要的二维照片或图像。尝试一些其他的,比如你的国旗,你最喜欢的运动队的标志等等。
前面图中显示的PopFace_Albedo纹理包含在 Unity 导入到我们项目中的 AR Face Assets 包中。现在通过重复步骤 1-5,命名为PopFace Material,并使用PopFace_Albedo作为基础地图纹理来制作材料。
同样,AR Face Assets 包还包括机器人面部的纹理。再次,为新的RobotFace Material重复步骤 1-5,使用Robot_Albedo作为Robot_Normal,使用Robot_Occlusion分别作为法线图和遮挡图。
当添加法线图纹理时,你可能会收到提示此纹理未标记为法线图。点击立即修复按钮以应用所需的导入设置。
以下图显示了戴着RobotFace和PopFace面具的我。在这些屏幕截图不太明显的是,面部网格会实时跟随我的面部表情:

图 9.10 – 使用机器人 PBR 材质(左)和 Pop 反照率纹理(右)的自拍
信息:使用 Procreate 绘制自己的纹理
如果你感兴趣自己绘制 UV 映射的面部纹理(并且拥有 iPad),Procreate 应用(procreate.art/)具有此功能(查看Dilmer Valecillos关于此功能的视频)。
制作好材料后,我们可以将面部网格可视化器添加到可变面部预制件中,这样它将在运行时生成面部网格。
将面部网格可视化器添加到可变面部预制件
要将动态面部网格集成到我们的应用程序中,我们应该将其添加到我们的多功能可变面部预制件中。我们需要与之前生成的AR 默认面部游戏对象相同的组件,并且它们需要位于预制件的根对象上。使用以下步骤手动添加它们:
-
打开可变面部预制件进行编辑。
-
在选择预制体根对象后,在检查器中点击添加组件。
-
搜索并选择AR Face Mesh Visualizer组件。
-
搜索并选择一个网格过滤器组件。
-
搜索并选择一个网格渲染器组件。
-
将默认面材质从项目窗口拖动到可变面预制体根对象上。
-
保存预制体。
-
在场景层次结构中返回,将可变面预制体资产拖动到AR 会话起源 | AR 面管理器 | 面预制体槽位。
如果你现在构建并运行,你会看到默认的面网格。所有菜单按钮仍然工作,让你添加 3D 头部模型和配件。
我们希望有按钮让用户在面材质之间进行选择。为此,我们需要更新我们的脚本。
控制面材质
我们可以通过切换AR Face Mesh Visualizer和Mesh Render组件来隐藏或显示面网格。使用以下步骤:
-
打开
ChangeableFace脚本进行编辑,并在脚本顶部添加以下内容:using UnityEngine.XR.ARFoundation; -
添加以下代码以声明和初始化对
ARFaceMeshVisualizer和MeshRenderer组件的引用:ARFaceMeshVisualizer meshVisualizer; MeshRenderer renderer; private void Start() { meshVisualizer = GetComponent<ARFaceMeshVisualizer>(); meshVisualizer.enabled = false; renderer = GetComponent<MeshRenderer>(); renderer.enabled = false; }我们将启动应用程序,使面网格不可见,因此两个组件都是禁用的。
-
然后,添加一个
SetMeshMaterial函数,如下所示:public void SetMeshMaterial(Material mat) { if (mat == null) { meshVisualizer.enabled = false; renderer.enabled = false; return; } renderer.material = mat; meshVisualizer.enabled = true; renderer.enabled = true; }当给定材质
mat时,该函数将其设置在渲染器中,并确保可视化器和渲染器组件被启用。如果你为mat传递一个null值,那么组件将被禁用。 -
接下来,打开
FaceMainMode脚本并添加一个ChangeMaterial函数,如下所示:public void ChangeMaterial(Material mat) { foreach (ARFace face in faceManager.trackables) { ChangeableFace changeable = face.GetComponent<ChangeableFace>(); if (changeable != null) { changeable.SetMeshMaterial(mat); } } }就像脚本中的其他函数一样,它遍历任何可追踪项并调用可变组件。
-
接下来,使用以下突出显示的行更新
ResetFace函数:changeable.SetPosePrefab(null); changeable.ResetAccessories(); changeable.SetMeshMaterial(null);
代码现在已经编写完成。我们在ChangeableFace脚本中添加了一个SetMaterial函数,它启用了网格可视化器并将材质设置为渲染。在FaceMainMode脚本中,我们添加了一个ChangeMaterial函数,它对每个可追踪的 AR 面调用SetMaterial。
我们现在准备好添加各种网格材质的菜单按钮。
添加到主菜单的面材质
要添加新的按钮到主菜单,我们可以复制一个现有的按钮并修改它,就像我们之前做的那样。使用以下步骤:
-
在
DefaultFace Button。 -
从
default face icon资产拖动到图像 | 源图像槽位。 -
在按钮的点击动作中,将函数更改为FaceMainMode.ChangeMaterial。
-
从
Materials/文件夹拖动到参数槽位。 -
同样,重复步骤 1-4三次,对于
PhotoFace Button(使用photo face icon图像,以及PopFace Button,和RobotFace Button。
保存场景并构建项目。当你点击其中一个面材质按钮时,它会渲染面网格。以下截图中显示了带有新按钮的水平滚动菜单:

图 9.11 – 主菜单上的面部网格纹理按钮
在本节中,我们添加了一个 FaceMainMode。然后它将这个模式转发到可追踪的面部。
虽然面部可视化器可以跟随一些你的表情,包括扬起眉毛和张开嘴巴,但它对你的眼睛没有任何作用。让我们接下来考虑眼动追踪。
使用眼动追踪(ARKit)
对于眼动追踪,正如你所预期的,你将获得每个眼睛的姿势变换,你可以使用这些变换来更新你自己的“眼球”游戏对象。对于这个功能,我会向你展示如何实现,但将集成到项目中的细节留给你。目前,这个功能需要一个带有 TrueDepth 相机的 iOS 设备。
要了解更多关于 AR Foundation 的眼动追踪信息,请查看 AR Foundation 示例资产中提供的 EyeLasers 场景(我们已将其安装到 Assets/ARF-samples/ 文件夹中)。
场景中的 AR Face Manager 中的 Face Prefab 是 AR Eye Laser Visualizer 预制体。这有一个 AR Face 组件(正如你所期望的),还有一个 Eye Pose Visualizer。这个可视化脚本反过来又给了一个眼球预制体。在这个特定的场景中,它被赋予了 Eye Laser Prefab。这只是一个包含一个细长圆柱体的简单预制体,它将被渲染成激光束。总的来说,这些依赖关系可以表示如下:
EyeLasers 场景 -> AR Eye Laser Visualizer 面部预制体 -> Eye Pose Visualizer 脚本 -> Eye Laser Prefab
EyePoseVisualizer 脚本是一个示例脚本(本身不是 AR Foundation 包的一部分)。简而言之,你给它一个眼球预制体,该预制体被实例化两次,并由 ARFace、leftEye 和 rightEye 姿势变换作为父级。例如,你会在脚本的 CreateEyeGameObjectsIfNecessary 函数(第 45 行)中找到以下代码行:
m_LeftEyeGameObject = Instantiate(m_EyePrefab, m_Face.leftEye);
作为追踪眼变换的子对象,生成的对象似乎会自动跟踪你的检测到的眼动。
脚本还订阅了 ARFace 和 update 事件,根据可追踪对象的追踪状态切换眼睛的可见性,如下面的代码所示:
void OnUpdated(ARFaceUpdatedEventArgs eventArgs)
{
CreateEyeGameObjectsIfNecessary();
SetVisible((m_Face.trackingState == TrackingState.Tracking) && (ARSession.state > ARSessionState.Ready));
}
小贴士:使用带有面部追踪的更新事件
这个脚本展示了使用 AR Foundation 进行面部追踪的另一个最佳实践。通过订阅可追踪对象的 updated 事件,它根据可追踪对象的 trackingState 以及整体的 ARSession.state 来切换实例化预制体的可见性。你可能考虑重构我们 FaceMainMode 类中的函数,以便以这种方式处理 updated 事件。
眼动追踪并非在所有平台上都可用。当脚本启用时,它首先检查 Unity 眼动追踪子系统。如果该功能不受支持,组件会自行禁用,如下面的 OnEnable 函数(第 65-78 行)所示:
void OnEnable()
{
var faceManager = FindObjectOfType<ARFaceManager>();
if (faceManager != null && faceManager.subsystem != null && faceManager.descriptor.supportsEyeTracking)
{
m_FaceSubsystem = (XRFaceSubsystem)faceManager.subsystem;
SetVisible((m_Face.trackingState == TrackingState.Tracking) && (ARSession.state > ARSessionState.Ready));
m_Face.updated += OnUpdated;
}
else
{
enabled = false;
}
}
如果您想尝试使用眼球而不是激光束,以下 URL 包含一个您可以使用免费眼球 3D 模型:free3d.com/3d-model/eyeball--33237.html.将其制作成预制件,并用它替换 AR 眼激光可视化预制件上的Eye Pose Visualizer | Eye Prefab槽位。
这太棒了!然而,你可以做更多的事情。例如,使用 ARCore,你可以将图形附加到面部特定区域。现在让我们来看看这一点。
将贴纸附加到面部区域(ARCore)
如果您的项目正在使用 ARCore XR 插件和 Android,您将能够访问 ARCore 特定的功能,包括三个重要面部区域的变换:鼻尖、左额和右额。例如,如果您抬起左眉毛,该变换将独立于面部其余部分移动,为您的应用程序中的面部表情提供更多细节。
除了我们在这里做的事情之外,您还可能想查看项目中的ARF-samples/文件夹,以及它使用的ARCoreFaceRegionManager脚本。我们在这个部分开发的代码相当简单且易于理解。
为了展示 ARCore 面部区域,我们将实现几个 2D 贴纸并将它们附加到 3D 面部区域。我们将让您使用我们在本章顶部识别的剪贴画(以及我在 Photoshop 中编辑的)添加眉毛、胡须和舔嘴唇。它们已被导入为Sprite(2D 和 UI)。这些可以在本书的 GitHub 仓库中找到。
我们可以从创建贴纸预制件开始。
创建贴纸预制件
要制作这些剪贴画图像的预制件,请按照以下步骤操作:
-
在
Mustache Prefab上右键单击。然后打开它进行编辑。 -
从
mustache图像到根Mustache Prefab。这会创建一个名为mustache的子对象,并带有Sprite Renderer组件。 -
设置
(0, -0.02, 0)和(0,019, 0,019, 0,019)。 -
保存预制件。
-
重复步骤 1-4,使用
licking-lips贴图图像创建Lips Prefab。使用(0, -0.05, 0)和(0,019, 0,019, 0,019)。 -
再次,重复步骤 1-4,使用
eyebrow-left贴图图像创建Eyebrow Left Prefab。使用(0, -0.01, 0)和(0,019, 0,019, 0,019)。 -
同样,再次,使用
eyebrow-right贴图图像创建一个Eyebrow Right Prefab。使用(0, -0.01, 0)和(0,019, 0,019, 0,019)。
现在我们有了胡须、嘴唇和眉毛的预制件。让我们编写脚本,使用 ARCore 面部区域支持将它们附加起来。
管理附件的位置
我们将在ChangeableFace脚本上创建一个单独的脚本,名为FaceRegionAttachments,因为代码是 ARCore 特定的,并且相对较长。
依赖于 ARCore 的代码行被包含在#if UNITY_ANDROID &&!UNITY_EDITOR编译器符号中,因此它们将在非 Android 环境中(包括桌面播放模式)不会运行。请按照以下步骤操作:
-
创建一个新的 C#脚本命名为
FaceRegionAttachments并打开它进行编辑。 -
通过替换以下代码来开始编写脚本:
using System.Collections.Generic; using UnityEngine; using Unity.Collections; using UnityEngine.XR.ARFoundation; #if UNITY_ANDROID using UnityEngine.XR.ARCore; #endif public class FaceRegionAttachments : MonoBehaviour { ARFaceManager faceManager; ARFace face; Dictionary<ARCoreFaceRegion, GameObject> prefabs = new Dictionary<ARCoreFaceRegion, GameObject>(); Dictionary<ARCoreFaceRegion, GameObject> objs = new Dictionary<ARCoreFaceRegion, GameObject>(); #if UNITY_ANDROID && !UNITY_EDITOR NativeArray<ARCoreFaceRegionData> faceRegions; #endif private void Start() { faceManager = FindObjectOfType<ARFaceManager>(); face = GetComponent<ARFace>(); }脚本首先声明我们正在使用 ARFoundation API 以及 ARCore。然后,在类顶部声明
ARFaceManager和对象的ARFace变量,并在Start中初始化这些变量。我们还声明了两个字典prefabs和objs,它们将按 ARCore 的region标识符(枚举)索引。然后,我们声明了一个名为faceRegions的NativeArray,其中包含ARCoreFaceRegionData,我们将在Update中使用它。 -
添加一个
SetRegionAttachment函数(它将由FaceMainMode调用),如下所示:public void SetRegionAttachment(ARCoreFaceRegion region, GameObject prefab) { GameObject obj; if (objs.TryGetValue(region, out obj)) { GameObject currentPrefab = prefabs[region]; Destroy(obj); prefabs.Remove(region); objs.Remove(region); if (prefab == currentPrefab) return; } obj = Instantiate(prefab); prefabs.Add(region, prefab); objs.Add(region, obj); }函数获取一个
regionID 和一个prefab,实例化prefab,并将prefab和生成的对象记录在字典中。如果已经有一个生成的对象,它首先被销毁并从列表中移除。我们检查新的prefab是否与当前的一个相同,因此它不会再次重生,从而允许菜单按钮通过点击两次来切换附件的开和关。 -
在每次
Update中,我们需要向 ARCore 请求当前的面部区域列表,并相应地更新生成的对象变换,如下所示:private void Update() { #if UNITY_ANDROID && !UNITY_EDITOR var subsystem = (ARCoreFaceSubsystem)faceManager.subsystem; if (subsystem == null) return; subsystem.GetRegionPoses(face.trackableId, Allocator.Persistent, ref faceRegions); for (int i = 0; i < faceRegions.Length; ++i) { GameObject obj; if (objs.TryGetValue(faceRegions[i].region, out obj)) { obj.transform.localPosition = faceRegions[i].pose.position; } } #endif } -
我们还可以提供一个公共的
Reset函数,它销毁所有实例化的对象并清除字典:public void Reset() { foreach (ARCoreFaceRegion region in objs.Keys) { Destroy(objs[region]); } objs.Clear(); prefabs.Clear(); } -
最后,当这个游戏对象被销毁时,良好的做法是按照以下方式处置
faceRegions本地数组:void OnDestroy() { #if UNITY_ANDROID && !UNITY_EDITOR if (faceRegions.IsCreated) faceRegions.Dispose(); #endif } } -
保存脚本后,在 Unity 中打开可更改的面部预制体资产进行编辑。
-
将
FaceRegionAttachments脚本拖放到相同预制体的根Destroy和Instantiate上。
现在我们将更新FaceMainMode脚本以使用它,并提供公共函数,菜单按钮可以调用,如下所示:
-
打开
FaceMainMode脚本进行编辑,并在文件顶部添加以下行(用于enumARCoreFaceRegion定义):#if UNITY_ANDROID using UnityEngine.XR.ARCore; #endif -
添加一个私有的
SetRegionAttachment函数,它遍历可追踪对象并在它们上调用SetRegionAttachment:private void SetRegionAttachment(ARCoreFaceRegion region, GameObject prefab) { foreach (ARFace face in faceManager.trackables) { FaceRegionAttachments regionAttachments = face.GetComponent<FaceRegionAttachments>(); if (regionAttachments != null) { regionAttachments. SetRegionAttachment(region, prefab); } } } -
接下来,通过我们可以从菜单按钮 Unity 动作中调用的单独公共函数公开此功能,如下所示:
public void SetNoseAttachment(GameObject prefab) { SetRegionAttachment(ARCoreFaceRegion.NoseTip, prefab); } public void SetForeheadLeftAttachment(GameObject prefab) { SetRegionAttachment( ARCoreFaceRegion.ForeheadLeft, prefab); } public void SetForeheadRightAttachment(GameObject prefab) { SetRegionAttachment( ARCoreFaceRegion.ForeheadRight, prefab); } -
保存脚本。
在这里,我们创建了一个新的FaceRegionAttachments脚本,它维护字典列表prefabs和生成的objs,用于特定面部区域附加的游戏对象。在每一帧Update中,objs变换基于面部区域的姿态变换进行更新,因此它与区域一起跟踪。这种实现允许在面部上添加多个附件,但每个区域只有一个。然后,我们更新了FaceMainMode脚本,其中包含可以由菜单按钮调用的公共函数来添加附件。
我们现在可以创建菜单按钮。
向主菜单添加区域附件
如我们之前所做的那样,要向主菜单添加新按钮,我们可以复制一个现有的按钮并修改它。使用以下步骤:
-
在
Mustache Button中。 -
从
mustache icon资产拖动到图像 | 源图像槽位。 -
在按钮的点击动作中,将函数更改为FaceMainMode.SetNoseAttachment。
-
从项目窗口,将Mustache Prefab资产拖动到参数槽位。
-
使用
licking-lips icon图像和Lips Prefab资产,重复步骤 1-4为Lips Button。使用与胡须相同的函数,FaceMainMode.SetNoseAttachment。 -
再次重复步骤 1-4为
Eyebrows Button,使用eyebrows icon图像。这次,我们将有两个点击动作,一个用于每个眼睛。第一个调用FaceMainMode.SetForeheadLeftAttachment与EyebrowLeft Prefab。第二个调用FaceMainMode.SetForeheadRightAttachment与EyebrowRight Prefab,如下所示:

图 9.12 – 眉毛按钮有两个点击动作,用于左侧和右侧的区域和预制件
保存场景并构建项目。当你点击其中一个区域附加按钮时,它会将其贴纸预制件添加到场景中。胡须和嘴唇都设置了鼻子附加,所以你一次只能查看一个。以下屏幕截图显示了我全部装备的样子,包括将其与其他我们之前创建的面部增强功能结合(右侧):

图 9.13 – 带有多个贴纸的自拍截图,以及(在右侧)与其他增强功能结合
因为这个功能是针对 ARCore 的,如果你尝试为 iOS 构建项目,你可能想隐藏贴纸按钮。我们可以稍后添加这些。
ARCore 专用 UI 按钮
这个面部区域贴纸功能仅在 ARCore 和 Android 上运行。如果你计划在 iOS(以及 Android)上构建相同的项目,我们已经通过条件编译符号解决了代码编译问题。然而,菜单按钮仍然可见。你可以在构建之前在编辑器中手动禁用它们,或者你可以让脚本处理它。
使用以下ARCoreOnly脚本来隐藏 UI 中的按钮(除非你的目标是 Android)。如果你针对 Android 但使用编辑器中的播放模式(使用 AR Foundation 远程工具),此脚本将禁用按钮,使其可见但不能交互:
using UnityEngine;
using UnityEngine.UI;
public class ARCoreOnly : MonoBehaviour
{
private void Awake()
{
#if !UNITY_ANDROID
gameObject.SetActive(false);
#endif
#if UNITY_EDITOR
Button button = GetComponent<Button>();
button.interactable = false;
#endif
}
}
将此脚本的副本拖动到胡须按钮、嘴唇按钮和眉毛按钮游戏对象上,以便它们只能与 ARCore 一起使用。
总结来说,在本节中,我们创建了包含 FaceRegionAttachments 的几个贴纸预制件,它使用本地的 ARCoreFaceRegionData(通过 ARCoreFaceSubsystem)来找到每个面部区域(鼻尖、左额头和右额头)的姿态变换,并跟踪每个生成的游戏对象与给定的面部区域。我们为每个贴纸添加了菜单按钮,通过传递贴纸预制件来调用 FaceMainMenu 中的公共函数。这反过来又把预制件传递给了可追踪的面部。你可以自由地添加更多的贴纸预制件和按钮,使用与本章中找到的类似步骤。
这很酷,但只有三个面部区域似乎有点局限。使用 ARKit,你可以获取更多关于面部几何的精细细节。这是通过使用混合形状实现的。
跟踪表达式的面部混合形状(ARKit)
ARKit 引入了仅在 iOS 设备上可用的额外高级面部跟踪功能,包括混合形状。混合形状指的是用于在视频游戏和 VR 应用程序中动画 NPC(非玩家角色)面部网格几何形状的变形。目前,它们是 ARKit 特有的功能。ARKit 混合形状提供了面部表情的详细细节,作为单独的特征,例如左眼或右眼眨眼、向下看、眼睛睁大、脸颊膨胀、脸颊皱眉、下巴左倾、嘴部酒窝等。每个特征都有一个在 0.0 到 1.0 范围内的系数。这些形状数据可以转发到 Unity 骨骼蒙皮渲染器(docs.unity3d.com/Manual/class-SkinnedMeshRenderer.html),用于角色动画。有关更详细的解释和讨论,请参阅以下网址:www.quora.com/What-is-blendshape-exactly。
构建一个动画骨架(带有骨骼和蒙皮网格)超出了本书的范围。相反,为了解释,我将通过 AR Foundation 示例项目中的 ARKitFaceBlendShapes 场景的示例资产进行说明,该场景位于 Assets/ARF-samples/scenes/FaceTracking/ 文件夹中。首先,你可以尝试自己构建 ARKitFaceBlendShapes 场景(如果你已经为 iOS 开发做好了准备)。现在,让我们更仔细地看看。
在 Unity 编辑器中打开场景,你会找到用于面部预制件的 SlothHead 预制件。
打开 ARKitBlenShapeVisualizer。这是一个与 AR Foundation 示例一起提供的示例脚本(它本身不是 AR Foundation 包的一部分)。此组件有一个用于骨骼蒙皮渲染器的参数。它位于Sloth_Head2子对象上,如下面的截图所示:

图 9.14 – SlothHead 预制件具有示例 ARKitBlendShapeVisualizer 脚本,该脚本引用了子 Sloth_Head2 上的 skinned mesh render
在你的代码编辑器中打开ARKitBlendShapeVisualizer脚本。你会找到一个名为CreateFeatureBlendMapping的函数,它被Awake调用。这个函数将 ARKit 混合形状名称(类型ARKitBlendShapeLocation)与skinnedMeshRenderer上的相应索引进行映射。有关位置和描述的列表,请参阅以下 URL:docs.unity3d.com/Packages/com.unity.xr.arkit-face-tracking@4.2/api/UnityEngine.XR.ARKit.ARKitBlendShapeLocation.html。
以下截图显示了Sloth_Head2对象的Skinned Mesh Renderer,其中一些BlendShapes在 Unity Inspector中可见:
![Figure 9.15 – Skinned Mesh renderer component with some of the blend shapes listed
![img/Figure_9.15_B15145.jpg]
图 9.15 – 包含一些列出混合形状的 Skinned Mesh renderer 组件
ARKit 混合形状的位置映射到Skinned Mesh Renderer的位置。
然后是ARKitBlendShapeVisualizer脚本,它使用OnUpdated函数订阅ARFace的updated事件,反过来,它调用其UpdateFaceFeatures函数。UpdateFaceFeatures从 ARKit 获取当前的混合形状系数(m_ARKitFaceSubsystem.GetBlendShapeCoefficients),并为每个系数,将该系数值(按全局标度缩放)设置到skinnedMeshRender。从那里,Unity 执行其魔法,变形和动画网格几何形状以在屏幕上渲染。这并不简单,但如果正确理解,是有意义的。
这基本上就是混合形状的工作原理。开发自己的模型和代码可能需要熟悉 Unity 的相关部分,但所有你需要的信息都是可访问的。如果你知道如何使用它,你就会成功。
摘要
在本章中,你构建了一个面部制作应用程序,该应用程序使用移动设备上的面向前方的(面向用户的)摄像头处理面部跟踪。你了解到我们可以从FaceMainMode脚本更新ChangeableFace脚本。
您使用这种架构来探索了几种渲染跟踪面部的方法。首先,您使用面部姿态来渲染一个实例化的 3D 头部模型(MrPlasticHead和MrFacetHead)。接下来,您使用这项技术为面部添加配件,包括高顶帽和太阳镜。然后,您添加了一个AR 面部网格可视化器,在运行时动态生成面部网格,并制作了多种可以应用于网格的材料,以制作各种面部面具。如果您使用的是 ARCore,您还实现了使用附加到 ARCore 面部区域的小精灵图像的面部区域贴纸。最后,您了解了 ARKit 特定的面部跟踪功能,包括眼动跟踪和混合形状。在这个过程中,您实现了一个水平滚动的主菜单按钮,允许用户选择各种面部滤镜的组合。这一切都很有趣!
现在,您已经掌握了如何在 Unity 中使用 AR Foundation 构建 AR 应用程序的知识。如果您跟随本书的每一章,您将学习如何使用 Unity 配置您的系统以在目标平台和移动设备上构建 AR 开发。您创建了一个简单的 AR 场景,学习了 AR 所需的主要游戏对象,包括 AR 会话和 AR 会话原点。您还探索了 Unity 提供的示例 AR 项目。接下来,您学习了如何改进开发人员的工作流程和解决您的应用程序的问题,考虑到 AR 开发的独特情况。
您创建了一个用于开发 AR 应用程序的用户框架,包括入门图形、交互模式和 UI 面板。这个框架被保存为场景模板以供重复使用。您学习了如何使用这个框架,首先构建了一个简单的放置对象场景,其中包含一个简单的主菜单。
在本书的第三部分,您构建了几个 AR 应用程序,包括一个相册,您可以在墙上放置带框的照片,具有菜单和用户交互功能。您改进了应用程序,添加了编辑工具来移动、调整大小、删除和更改场景中虚拟图片中显示的图像。在下一个项目中,您使用了图像跟踪来展示 3D 图形和关于行星的信息,使用现实生活中的印刷闪卡。最后,在本章中,您构建了一个面部跟踪应用程序,其中包含一个滚动菜单,包含各种面部头部、面具和可附加的配件,以制作有趣的快照。
这只是开始。AR Foundation 和 Unity 为增强现实应用程序提供了更多的支持,包括使用 GPS 进行对象跟踪和地理标记,以及 Unity 平台的全丰富性,用于开发交互式 3D 游戏和应用。走出户外,增强世界吧!

订阅我们的在线数字图书馆,全面访问超过 7,000 本书籍和视频,以及行业领先的工具,帮助你规划个人发展并推进你的职业生涯。更多信息,请访问我们的网站。
第十章:为什么订阅?
-
使用来自超过 4,000 位行业专业人士的实用电子书和视频,节省学习时间,多花时间编码
-
通过为你量身定制的 Skill Plans 提高你的学习效果
-
每月免费获得一本电子书或视频
-
完全可搜索,便于快速访问关键信息
-
复制粘贴、打印和收藏内容
你知道 Packt 为每本书都提供电子书版本,包括 PDF 和 ePub 文件吗?你可以在packt.com升级到电子书版本,并且作为印刷书客户,你有权获得电子书副本的折扣。如需更多详情,请联系我们 customercare@packtpub.com。
在www.packt.com,你还可以阅读一系列免费的技术文章,注册各种免费通讯,并享受 Packt 书籍和电子书的独家折扣和优惠。
你可能也会喜欢的其他书籍
如果你喜欢这本书,你可能对 Packt 的其他书籍也感兴趣:
《Unity 2021 游戏开发模式 - 第二版》
David Baron
ISBN: 978-1-80020-081-4
-
使用行业标准开发模式构建专业的 Unity 代码
-
确定实现特定游戏机制或功能的正确模式
-
开发可配置的核心游戏机制和成分,无需编写任何代码即可进行修改
-
审查实用的面向对象编程(OOP)技术,并学习它们如何在 Unity 项目中使用
-
构建独特的游戏开发系统,例如关卡编辑器
-
探索将传统设计模式用于 Unity API 的方法
[(https://www.packtpub.com/product/unity-2020-virtual-reality-projects-third-edition/9781839217333)]
《Unity 2020 虚拟现实项目 - 第三版》
Jonathan Linowes
ISBN: 978-1-83921-733-3
-
了解虚拟现实和 VR 消费产品的当前状态
-
通过使用 Unity 编辑器和导入的资产构建一个简单的场景来开始使用 Unity
-
配置你的 Unity VR 项目以在 Oculus、SteamVR 和 Windows 沉浸式 MR 等 VR 平台上运行
-
设计并构建一个带有音轨和时序的 VR 叙事动画
-
使用游戏物理和粒子系统实现一个音频火球游戏
-
使用各种软件模式设计 Unity 事件和交互式组件
-
发现照明、渲染和后期处理的最佳实践
Packt 正在寻找像你这样的作者
如果你有兴趣成为 Packt 的作者,请访问authors.packtpub.com并今天申请。我们曾与成千上万的开发者和技术专业人士合作,就像你一样,帮助他们将见解分享给全球技术社区。你可以提交一般申请,申请我们正在招募作者的特定热门话题,或者提交你自己的想法。
分享你的想法
嗨!
我是 Jon,Augmented Reality with Unity AR Foundation 和 Unity 2020 Virtual Reality Projects 书籍的作者。我真心希望你喜欢阅读我的书籍,并觉得它对开始构建 AR 和/或 VR 应用程序很有用。我想你现在准备好走出去了,增强你的世界了!
如果你能在 Amazon 上留下关于《使用 Unity AR Foundation 进行增强现实》的评论,分享你的想法,这将真正帮助我(以及其他潜在读者!)。

你的评论将帮助我了解这本书中哪些地方做得很好,以及未来版本中哪些地方可以改进,所以这真的非常感谢。
祝好运,
Jon

目录
-
使用 Unity AR Foundation 进行增强现实
-
贡献者
-
关于作者
-
关于审稿人
-
前言
-
本书面向的对象
-
本书涵盖的内容
-
充分利用本书
-
下载示例代码文件
-
下载彩色图片
-
使用的约定
-
取得联系
-
-
第一部分 – 开始使用增强现实
-
第一章:为 AR 开发做准备
-
技术要求
-
定义增强现实
-
开始使用 Unity
-
安装 Unity Hub
-
安装 Unity 编辑器
-
创建和管理 Unity 项目
-
介绍 Unity 编辑器界面
-
Unity 编辑器使用基础
-
组织你的项目资源
-
-
为 AR 开发准备你的项目
-
为 AR 设备安装 XR 插件
-
安装 AR Foundation 包
-
选择输入处理器
-
添加对通用渲染管道的支持
-
-
为移动开发设置
-
为 Android/ARCore 开发设置
-
为 iOS/ARKit 开发设置
-
为可穿戴 AR 眼镜开发
-
-
构建和运行测试场景
-
总结
-
-
第二章:你的第一个 AR 场景
-
技术要求
-
探索 Unity 的 AR Foundation 示例项目
-
构建和运行样本项目
-
导出样本资产以供重用
-
-
在自己的项目中构建 SimpleAR 场景
-
创建新项目
-
将样本资产导入到自己的项目中
-
-
开始一个新的基本 AR 场景
-
使用 AR 会话
-
使用 AR Session Origin
-
使用 AR 摄像头
-
添加平面和点云管理器
-
添加 AR Raycast 管理器
-
添加光估计
-
构建和运行场景
-
-
在平面上放置对象
-
设置 PlaceObject 输入动作
-
介绍 Unity C# 编程和 MonoBehaviour 类
-
编写 PlaceObjectOnPlane 脚本
-
构建和运行场景
-
重构你的脚本
-
-
创建放置预制件
-
理解 GameObjects 和 Transforms
-
寻找 3D 模型
-
完成场景
-
-
总结
-
-
第三章:改进开发者工作流程
-
技术要求
-
使用日志消息进行故障排除
-
使用 Debug.Log
-
使用移动设备上的控制台
-
在您的应用程序中模拟控制台窗口
-
-
使用调试器进行调试
- 在远程设备上进行调试
-
使用编辑器远程工具进行测试
-
使用 Unity MARS 进行模拟
- 使用 MARS 伴侣应用进行捕获
-
总结
-
-
第二部分 – 可重用 AR 用户框架
-
第四章:创建 AR 用户框架
-
技术要求
-
理解 AR 交互流程
-
安装必备资产
-
TextMeshPro
-
DOTween
-
Serialized Dictionary Lite
-
其他必备资产
-
-
从新场景开始
-
创建 UI 画布和面板
-
创建屏幕空间画布
-
添加应用程序标题
-
创建 UI 面板
-
-
创建 UI 控制器
-
创建 Singleton 类脚本
-
编写 UIController 脚本
-
淡出 UI 面板
-
-
创建交互控制器模式
-
创建交互模式层次结构
-
编写交互控制器
-
-
创建交互模式行为
-
StartupMode 脚本
-
ScanMode 脚本
-
MainMode 脚本
-
NonARMode 脚本
-
-
进行测试
-
使用 Unity 入门 UX 资产
-
介绍入门资产
-
准备 Unity AR 入门资产
-
安装依赖包
-
导入 OnboardingUX 包
-
编写 AnimatedPrompt 脚本
-
集成入门图形
-
-
为新场景创建场景模板
-
总结
-
-
第五章:使用 AR 用户框架
-
技术要求
-
规划项目
-
从 ARFramework 场景模板开始
-
添加主菜单
-
添加带有教学 UI 的 PlaceObject 模式
-
创建 PlaceObject UI 面板
-
创建 PlaceObject 模式
-
-
连接菜单按钮
-
执行构建和运行
-
在不需要时隐藏跟踪对象
-
高级入门问题
-
创建 AR 可选项目
-
确定设备是否支持特定的 AR 功能
-
添加本地化
-
-
总结
-
-
第三部分 – 构建更多 AR 项目
-
第六章:画廊:构建 AR 应用
-
技术要求
-
指定艺术画廊项目的 UX
-
项目目标
-
用例
-
UX 设计
-
用户故事
-
-
开始使用
-
收集图像数据
-
导入照片以使用
-
将图像数据添加到场景中
-
获取图像的像素尺寸
-
在运行时加载图片列表
-
-
创建带框照片预制体
-
创建预制体层次结构
-
编写 FramedPhoto 脚本
-
调整图片的形状
-
-
在墙上挂一张虚拟照片
-
检测垂直平面
-
创建添加图片的 UI 面板
-
编写初始添加图片模式脚本
-
创建添加图片模式对象
-
创建主菜单添加按钮
-
构建和运行
-
完成添加图片模式脚本
-
在添加图片模式中显示跟踪平面
-
-
选择要使用的图像
-
创建选择图片模式
-
创建选择图片的 UI 面板
-
创建图像按钮预制体
-
编写 ImageButtons 脚本
-
重定向添加按钮
-
-
调整图像的宽高比
-
摘要
-
-
第七章:相册:编辑虚拟对象
-
技术要求
-
创建编辑模式
-
创建编辑菜单 UI
-
创建编辑图片模式
-
-
选择要编辑的图片
-
定义选择对象输入动作
-
替换 MainMode 脚本
-
从主模式中选择对象
-
连接完成编辑按钮
-
-
突出显示选定的图片
-
从编辑模式中选择对象
-
避免相交的对象
-
删除图片
-
替换图片的图像
- 替换框架
-
交互编辑图片
-
确保 FramedPhoto 对象接收输入动作消息
-
添加交互组件
-
使用手指移动图片
-
捏合以调整图片大小
-
-
总结
-
-
第八章:行星:跟踪图像
-
技术要求
-
理解 AR 图像跟踪
-
指定 Planets 项目
-
用户体验流程
-
准备行星卡片
-
收集行星纹理和数据
-
-
入门
-
跟踪参考图像
-
添加 AR 跟踪图像管理器
-
创建参考图像库
-
-
配置用户交互模式和 UI
-
扫描参考图像
-
构建和运行
-
-
创建和实例化虚拟地球预制件
-
创建通用行星预制件
-
理解等角图像
-
创建地球预制件
-
添加行星元数据
-
动画行星旋转
-
-
构建应用的主模式
- 编写 PlanetsMainMode 脚本
-
使用多个行星扩展项目
-
将行星卡片图像添加到参考图像库
-
创建行星预制件
-
对检测到的图像做出响应
-
-
创建响应式 UI
-
创建主模式 UI
-
指向摄像头以显示信息
-
显示信息详情
-
-
总结
-
-
第九章:自拍:制作有趣的面孔
-
技术要求
-
理解人脸追踪
-
人脸追踪与人脸识别
-
使用 AR Foundation 追踪人脸
-
-
入门
-
使用 ARFramework 模板创建新场景
-
设置 iOS ARKit 进行人脸追踪
-
导入项目中使用的资产
-
-
为人脸追踪配置新的 AR 场景
-
设置自拍 AR 相机
-
添加 AR 人脸管理器组件
-
提示用户寻找或忽略人脸
-
构建和运行
-
-
使用 3D 头部追踪人脸姿态
-
制作 Mr. Plastic Head 预制件
-
制作 Mr. Facet Head 预制件
-
-
构建主模式和菜单
-
创建可更换的人脸预制件
-
编写主模式控制器脚本
-
创建可滚动的主菜单按钮
-
添加重置人脸按钮
-
-
附加 3D 配件
-
戴帽子
-
佩戴酷炫太阳镜
-
更新配件的脚本
-
将配件添加到主菜单
-
-
使用多种材料制作动态人脸网格
-
探索 AR 默认人脸
-
创建人脸材料
-
将人脸网格可视化器添加到可更换的人脸预制件
-
控制人脸材料
-
将人脸材料添加到主菜单
-
-
使用眼动追踪(ARKit)
-
在人脸区域贴上贴纸(ARCore)
-
创建贴纸预制件
-
管理附件位置
-
将区域附件添加到主菜单
-
仅 ARCore 的 UI 按钮
-
-
跟踪表情混合形状(ARKit)
-
摘要
-
为什么要订阅?
-
-
你可能还会喜欢的其他书籍
- Packt 正在寻找像你这样的作者
地标
-
封面
-
目录















[(
浙公网安备 33010602011771号