公司动态

Unity与Vuforia开发AR射箭游戏:从物理交互到解决穿模问题

📅 2026/8/3 4:23:36
Unity与Vuforia开发AR射箭游戏:从物理交互到解决穿模问题
1. 项目概述为什么选择Unity 2019.3.9f1与Vuforia构建AR射箭游戏如果你正在寻找一个能融合物理交互、3D视觉和移动端部署的AR实战项目那么用Unity和Vuforia开发一款射箭游戏绝对是个绝佳的选择。这个组合之所以经典是因为它完美平衡了开发效率、功能成熟度和跨平台能力。Unity 2019.3.9f1是一个长期支持LTS版本在AR Foundation和图形渲染管线方面非常稳定避免了使用最新版可能遇到的未知兼容性问题。而Vuforia作为老牌AR SDK其图像识别Image Target和模型追踪Model Target的稳定性和精度对于需要精确锚定虚拟弓箭与真实世界的射箭游戏来说是至关重要的基础。这个项目的核心挑战与乐趣远不止于让玩家在手机屏幕上拉弓射箭。它涉及到从环境搭建、3D资源处理、物理逻辑编写到最终解决那个让所有AR开发者头疼的“穿模”问题——即虚拟的箭矢在飞行过程中如何正确地与识别图背后的真实空间比如桌面、墙壁发生碰撞和遮挡而不是尴尬地“穿透”过去。这不仅是视觉真实性的关键更是沉浸感的核心。接下来我将带你从零开始完整走一遍这个流程分享我在实际开发中踩过的坑和总结出的高效解决方案。2. 环境配置与项目初始化搭建稳固的开发地基在开始任何创意编码之前一个正确且干净的环境是项目成功的基石。这一步的失误可能会导致后续无数诡异且难以排查的Bug。2.1 Unity版本与模块安装要点首先从Unity Hub安装Unity 2019.3.9f1。这里有一个关键细节在安装组件时务必勾选“Android Build Support”及其下的“Android SDK NDK Tools”和“OpenJDK”。虽然Unity允许你后续关联自己的JDK但使用其自带的OpenJDK能最大程度避免因环境变量或版本不一致导致的构建失败。很多开发者遇到的“unity关联jdk总是提示无法找到”的问题根源往往就在这里。安装完成后创建一个新的3D项目。我建议项目命名清晰如“AR_Archery_Project”并选择合适的存储路径避免中文和特殊字符。2.2 Vuforia引擎的集成与授权Vuforia的集成是项目的核心。在Unity 2019.3中推荐通过Package Manager来安装这比直接导入.unitypackage文件更便于版本管理。打开Window Package Manager。点击左上角的“”号选择“Add package from git URL...”。输入Vuforia的官方Git地址https://gitlab.com/unity-packages/Vuforia/unityextension.git。你也可以在Package Manager的“Advanced”下拉菜单中勾选“Show preview packages”来搜索Vuforia Engine AR。安装完成后Unity会提示重启编辑器。重启后最关键的一步是配置Vuforia授权。你需要前往 Vuforia开发者门户 注册并创建一个License Key。创建时应用类型选择“Development”这为你提供了免费的开发额度。创建成功后复制License Key。回到Unity菜单栏会出现Vuforia Configuration选项或在Project窗口搜索VuforiaConfiguration资源。点击打开将复制的License Key粘贴到“App License Key”字段中。这一步是AR功能能够启动的“钥匙”没有它你的AR相机将无法初始化。2.3 初始场景与AR相机设置环境配置好后我们开始搭建第一个AR场景。删除场景中自带的Main Camera因为Vuforia会提供专用的AR相机。在Hierarchy面板右键选择Vuforia Engine AR Camera。这会在场景中创建一个名为“ARCamera”的游戏对象。接着再次右键选择Vuforia Engine Image。这会在ARCamera下创建一个Image Target图像目标子物体。Image Target是AR内容的“锚点”我们的弓箭和靶子都将以它为参考系进行放置。配置Image Target在Inspector面板中你需要为其指定一张“目标数据库”中的图片。首先你需要创建一个数据库。在Vuforia开发者门户的“Target Manager”中创建一个新数据库例如“ArcheryTargets”然后上传你准备用作识别图的图片比如一张画有特殊图案的靶子图。上传后下载该数据库的Unity包.unitypackage并导入到项目中。回到Unity在Image Target组件的“Database”下拉框中选择你刚导入的数据库然后在“Image Target”下拉框中选择你上传的图片。将“Width”设置为一个合理的物理尺寸比如0.2代表20厘米这决定了虚拟世界与真实世界的比例尺。至此一个最基本的AR场景框架就搭建好了。当你在手机上运行此应用并用摄像头对准你打印出来的那张识别图时ARCamera应该能成功识别并稳定追踪它。3. 核心游戏逻辑实现从拉弓到射箭的物理模拟有了稳定的AR环境接下来我们构建游戏的核心——射箭机制。这里的关键是模拟真实的物理感觉拉弓蓄力、释放、箭矢飞行与命中。3.1 弓箭与箭矢的3D模型与层级设置首先你需要弓箭和箭矢的3D模型。可以从Asset Store获取免费资源或使用简单的圆柱体和立方体组合来自建。将弓箭模型Bow设置为Image Target的子物体并调整其位置和旋转让它“站在”识别图上。箭矢Arrow需要单独处理。我建议创建一个空的GameObject作为“箭袋”Quiver也作为Image Target的子物体用于存储未发射的箭矢。箭矢模型本身需要添加两个关键组件Rigidbody用于物理模拟。初始时应勾选“Is Kinematic”这样它不会受物理引擎影响而掉落而是由我们的代码控制。Capsule Collider用于碰撞检测。将其设置为Trigger触发器这样在飞行中我们可以检测它是否射中了靶子而不会因为物理碰撞被弹开。3.2 拉弓蓄力与手势交互编码射箭的交互通常通过触摸屏实现。我们在弓上挂载一个C#脚本例如BowController.cs。using UnityEngine; public class BowController : MonoBehaviour { public GameObject arrowPrefab; // 箭矢预制体 public Transform arrowSpawnPoint; // 箭矢生成点在弓弦位置 public float maxPullDistance 0.5f; // 最大拉弓距离 public float maxForce 50f; // 最大发射力 private GameObject currentArrow; private bool isPulling false; private Vector3 pullStartPosition; void Start() { CreateArrow(); } void CreateArrow() { if (arrowPrefab arrowSpawnPoint) { currentArrow Instantiate(arrowPrefab, arrowSpawnPoint.position, arrowSpawnPoint.rotation); currentArrow.transform.parent arrowSpawnPoint; // 让箭附着在弓上 } } void Update() { // 触摸开始记录拉弓起点 if (Input.touchCount 0 Input.GetTouch(0).phase TouchPhase.Began) { // 这里可以添加射线检测确保触摸点在弓的特定区域 isPulling true; pullStartPosition Input.GetTouch(0).position; } // 触摸移动计算拉弓力度 if (isPulling Input.touchCount 0) { Vector3 currentPosition Input.GetTouch(0).position; float pullDistance Mathf.Clamp(Vector3.Distance(currentPosition, pullStartPosition) / Screen.dpi, 0, maxPullDistance); // 视觉反馈可以将弓弦模型或箭矢向后移动 pullDistance if (currentArrow) { currentArrow.transform.localPosition new Vector3(0, 0, -pullDistance); } } // 触摸结束发射箭矢 if (isPulling Input.touchCount 0 Input.GetTouch(0).phase TouchPhase.Ended) { isPulling false; Vector3 endPosition Input.GetTouch(0).position; float finalPullDistance Mathf.Clamp(Vector3.Distance(endPosition, pullStartPosition) / Screen.dpi, 0, maxPullDistance); ShootArrow(finalPullDistance); Invoke(CreateArrow, 0.5f); // 0.5秒后生成新箭 } } void ShootArrow(float pullStrength) { if (!currentArrow) return; currentArrow.transform.parent null; // 解除父子关系让箭独立飞行 Rigidbody rb currentArrow.GetComponentRigidbody(); if (rb) { rb.isKinematic false; // 启用物理模拟 float forceMagnitude pullStrength / maxPullDistance * maxForce; rb.AddForce(arrowSpawnPoint.forward * forceMagnitude, ForceMode.Impulse); // 沿弓弦方向施加冲力 } currentArrow null; } }这段代码的核心逻辑是触摸开始记录起点触摸移动时根据手指拖拽距离计算拉弓力度并给出视觉反馈箭矢后移触摸释放时根据最终力度计算发射力并赋予箭矢刚体一个向前的冲量。注意这里的拉弓距离计算除以了Screen.dpi每英寸像素数这是一个将屏幕像素距离转换为近似物理距离英寸的小技巧能让不同屏幕尺寸的设备上拉弓手感更一致。但更精确的做法是结合AR的空间感知这会在后面解决穿模问题时进一步探讨。3.3 箭矢飞行物理与碰撞检测优化箭矢发射后其飞行轨迹完全由物理引擎PhysX控制。为了模拟箭矢的旋转箭羽稳定飞行我们可以在箭矢上添加另一个脚本ArrowFlight.cs。using UnityEngine; public class ArrowFlight : MonoBehaviour { private Rigidbody rb; private bool hasHit false; void Start() { rb GetComponentRigidbody(); } void Update() { if (!hasHit rb.velocity ! Vector3.zero) { // 让箭矢的指向始终与其速度方向一致模拟空气动力学 transform.forward Vector3.Slerp(transform.forward, rb.velocity.normalized, Time.deltaTime * 10f); } } void OnTriggerEnter(Collider other) { if (hasHit) return; // 避免多次触发 if (other.CompareTag(Target)) // 为目标物体设置“Target”标签 { hasHit true; rb.isKinematic true; // 命中后停止物理模拟箭“钉”在靶子上 rb.velocity Vector3.zero; transform.parent other.transform; // 让箭成为靶子的子物体跟随靶子移动 // 这里可以触发得分、音效、粒子特效等 Debug.Log(Hit the target!); } // 注意这里暂时不处理与环境如桌面的碰撞那是“穿模”问题要解决的。 } }这个脚本实现了两个关键点一是通过Update中的代码让箭矢在飞行中始终指向速度方向看起来更真实二是在OnTriggerEnter中处理命中靶子的逻辑通过将箭矢的刚体设为Kinematic并父级化到靶子来实现“钉在靶上”的效果。4. AR核心挑战深度感知与“穿模”问题的本质分析现在游戏的基本功能已经实现但你会发现一个破坏沉浸感的致命问题箭矢在飞向靶子的过程中如果靶子识别图后面有一堵真实的墙或一个桌面箭矢会毫无阻碍地“穿”过去仿佛它们不存在。这就是所谓的“穿模”Clipping。在传统3D游戏中这通过精心设计的环境碰撞体来解决但在AR中我们的环境是未知的、动态的真实世界。“穿模”的根本原因在于默认的Vuforia AR相机只提供了空间中的一个平面Image Target作为锚点却没有提供真实环境的几何深度信息。虚拟的箭矢带有碰撞体和虚拟的靶子都存在于这个锚定的3D空间中但它们与手机摄像头捕捉到的真实世界图像之间没有建立几何遮挡关系。从渲染层面讲虚拟物体总是被绘制在摄像头画面的最上层。因此解决穿模问题核心思路有两个方向物理碰撞层面让虚拟箭矢能与“代表”真实环境的虚拟几何体发生碰撞。视觉渲染层面让真实世界的画面能够正确地遮挡住虚拟的箭矢。对于移动端AR尤其是基于标记Marker-based的AR我们通常从第一个方向入手因为获取精确的环境3D网格用于渲染遮挡对设备和要求更高。我们的策略是在识别图所在的真实平面如桌面上动态生成一个隐形的“碰撞地面”让箭矢能与这个地面碰撞并停止。5. 实战解决方案利用平面检测动态生成环境碰撞体Vuforia Engine自带了“Ground Plane”功能即水平面检测这为我们解决穿模问题提供了完美的工具。它允许设备检测并追踪现实世界中的水平表面如地板、桌面。5.1 启用与配置Ground Plane功能在Hierarchy中选中ARCamera对象。在Inspector中找到Vuforia Behaviour (Script)组件。确保“World Center Mode”设置为SPECIFIC_TARGET或FIRST_TARGET。这里我们选择SPECIFIC_TARGET并拖入我们的Image Target这样世界中心就固定在识别图上更稳定。在同一面板找到“Advanced”部分勾选“Enable Ground Plane”和“Enable Predictive Tracking”预测性追踪能提升稳定性。接下来我们需要在场景中添加一个“锚点”用于放置检测到的地面。在Hierarchy中右键选择Vuforia Engine Plane Finder。这会创建一个Plane Finder对象。选中Plane Finder在Inspector中你可以看到一个交互提示的UI预制体选项。Vuforia提供了一个默认的“Ground Plane Stage”预制体它包含了指导用户移动手机以检测地面的UI。你可以直接使用它或者自定义。关键步骤在Plane Finder组件的“Anchor Stage”字段中将一个空的GameObject比如新建一个名为“EnvironmentAnchor”的空对象拖拽进去。这个空对象将成为检测到的地面的父节点。5.2 动态生成地面碰撞体与交互逻辑当用户通过移动手机成功检测到一个水平面如桌面后Plane Finder会实例化Anchor Stage我们的空对象到检测到的位置。我们需要写一个脚本在这个空对象下动态生成一个代表真实地面的碰撞体。创建一个脚本DynamicGroundCollider.cs并将其挂载到我们作为Anchor Stage的那个空对象“EnvironmentAnchor”上。using UnityEngine; public class DynamicGroundCollider : MonoBehaviour { public float groundColliderHeight 0.01f; // 碰撞体的厚度 public float groundColliderSize 2.0f; // 碰撞体的边长假设是方形区域 private BoxCollider groundCollider; private MeshRenderer groundRenderer; // 可选用于调试可视化 void Start() { // 程序启动时先禁用自身等待Plane Finder激活 gameObject.SetActive(false); } // 这个方法可以由Plane Finder的事件触发或简单地在OnEnable中执行 void OnEnable() { CreateGroundCollider(); } void CreateGroundCollider() { // 添加一个BoxCollider来代表地面 groundCollider gameObject.AddComponentBoxCollider(); groundCollider.size new Vector3(groundColliderSize, groundColliderHeight, groundColliderSize); groundCollider.center Vector3.zero; // 中心位于此空对象位置 // 地面碰撞体通常不是Trigger因为我们需要箭矢与它发生物理碰撞后停止 groundCollider.isTrigger false; // 可选添加一个简单的Quad或Plane网格并赋予一个半透明材质用于视觉调试 GameObject visualGround GameObject.CreatePrimitive(PrimitiveType.Cube); visualGround.transform.parent this.transform; visualGround.transform.localPosition Vector3.zero; visualGround.transform.localScale new Vector3(groundColliderSize, groundColliderHeight, groundColliderSize); groundRenderer visualGround.GetComponentMeshRenderer(); if (groundRenderer) { Material debugMat new Material(Shader.Find(Standard)); debugMat.color new Color(0, 1, 0, 0.3f); // 半透明绿色 groundRenderer.material debugMat; } Destroy(visualGround.GetComponentBoxCollider()); // 移除自带的碰撞体避免重复 } // 提供一个方法供其他脚本查询地面位置例如判断箭矢是否低于地面 public bool IsPointBelowGround(Vector3 worldPoint) { if (!groundCollider) return false; float groundTopY transform.position.y (groundCollider.center.y groundCollider.size.y / 2); return worldPoint.y groundTopY; } }现在我们需要修改箭矢的飞行逻辑让它能与这个动态生成的地面碰撞。修改之前的ArrowFlight.cs脚本在Update或FixedUpdate中持续检测与地面的关系并在OnCollisionEnter中处理碰撞因为地面碰撞体不是Trigger了。// 在ArrowFlight.cs中新增和修改 public class ArrowFlight : MonoBehaviour { // ... 保持原有变量 ... public LayerMask groundLayer; // 在Inspector中设置一个“Ground”层并分配给动态地面 void Start() { // ... 原有代码 ... // 确保箭矢的碰撞体不是Trigger用于与地面发生物理碰撞 Collider col GetComponentCollider(); if (col) col.isTrigger false; } void FixedUpdate() { if (!hasHit rb ! null rb.velocity ! Vector3.zero) { transform.forward Vector3.Slerp(transform.forward, rb.velocity.normalized, Time.deltaTime * 10f); // 简单射线检测下方是否有地面备用方案更精确的是用碰撞事件 RaycastHit hit; float checkDistance 0.2f; // 检测距离 if (Physics.Raycast(transform.position, Vector3.down, out hit, checkDistance, groundLayer)) { OnHitGround(hit.point); } } } void OnCollisionEnter(Collision collision) { if (hasHit) return; // 检查是否碰撞到地面层 if (((1 collision.gameObject.layer) groundLayer) ! 0) { ContactPoint contact collision.contacts[0]; OnHitGround(contact.point); return; } if (collision.gameObject.CompareTag(Target)) { // ... 原有命中靶子逻辑 ... } } void OnHitGround(Vector3 hitPoint) { hasHit true; if (rb) { rb.isKinematic true; rb.velocity Vector3.zero; } // 可选将箭矢轻微插入地面点的位置 transform.position hitPoint transform.forward * -0.05f; // 假设箭尖向前 // 播放击中地面的音效或粒子 Debug.Log(Arrow hit the ground!); } }5.3 流程整合与用户体验优化将上述所有部分整合用户启动App摄像头对准识别图看到虚拟的弓箭和靶子。系统提示用户缓慢移动手机以检测地面通过Plane Finder的默认UI。当桌面被检测到时EnvironmentAnchor被激活并放置在桌面位置其上的脚本动态生成一个绿色的半透明碰撞方块调试用。用户拉弓射箭。如果箭矢飞向靶子但路径上有桌面箭矢会与动态生成的“地面碰撞体”发生碰撞并OnHitGround函数被调用箭矢会停止并“插在”桌面位置。如果箭矢成功越过桌面或地面未被检测到/箭矢发射角度较高则会飞向靶子命中后钉在靶上。实操心得地面碰撞体的大小groundColliderSize需要根据你的游戏场景合理设置。设得太小箭矢容易从边缘“溜走”设得太大可能会在识别图附近产生不合理的碰撞。通常设置为识别图区域的2-3倍是安全的起点。此外在正式发布时记得移除或禁用用于调试的可视化地面网格groundRenderer.enabled false或者使用更优雅的“击中地面”特效来替代。6. 进阶优化与平台适配让体验更上一层楼解决了核心的穿模问题我们的AR射箭游戏已经具备了可玩性。但要打磨出一个令人印象深刻的体验还需要在一些细节上深耕。6.1 性能优化与渲染技巧移动端AR应用对性能极其敏感需要保持稳定的高帧率60fps才能保证追踪的流畅和沉浸感。Draw Call优化合并Batch弓箭、箭矢、靶子的静态材质。对于低多边形模型静态合批效果显著。光照与阴影避免使用实时光照。使用烘焙光照贴图Lightmap或轻量级的混合光照模式。对于AR内容简单的环境光Ambient和一个方向光Directional Light模式设为Baked通常就够了。关闭不必要的实时阴影。后处理谨慎使用屏幕后处理如Bloom, Color Grading。如果必须使用选择移动端优化过的版本并控制强度。LOD多层次细节如果模型比较复杂考虑设置LOD Group在距离较远时使用低模。Vuforia特定设置在Edit Project Settings Player Other Settings中确保“Multithreaded Rendering”和“Graphics Jobs”2019.3中为Experimental根据目标设备尝试开启可能提升性能。在Vuforia Configuration中可以适当降低相机分辨率以换取更高帧率。6.2 Android/iOS构建与真机调试要点Android构建Player Settings在File Build Settings切换平台到Android后点击Player Settings。Other SettingsBundle Identifier改为唯一的反向域名格式如com.YourCompany.Archery。Minimum API Level设置为至少Android 7.0 ‘Nougat’ (API level 24)这是ARCore/Vuforia的常见最低要求。Target API Level设置为你测试设备的API级别或最新的稳定版。Write Permission如果游戏需要保存截图或数据勾选External (SDCard)。XR Settings确保“Virtual Reality Supported”被勾选且其下的“Vuforia Augmented Reality Supported”也被勾选。这是AR功能生效的关键。构建连接安卓手机并开启USB调试。在Build Settings中点击Build And Run。第一次构建可能较慢因为需要下载SDK和构建模块。iOS构建iOS构建需要在macOS上进行并安装Xcode。Player Settings切换平台到iOS。Other SettingsCamera Usage Description必须填写请求相机权限的描述如“需要摄像头来体验增强现实功能”。否则App会被拒绝。Target minimum iOS Version设置为11.0或更高这是ARKit的支持起点。XR Settings同样勾选VR支持及Vuforia AR支持。构建点击Build生成一个Xcode工程。在Xcode中你需要配置有效的开发者账号和证书Provisioning Profile才能将应用部署到真机。常见问题排查如果在Android真机上运行黑屏或无法初始化AR请按以下步骤检查确认手机支持ARCore可安装Google的“ARCore服务”App检查。部分华为设备可能使用自己的AR引擎Vuforia对此有专门支持但配置更复杂。检查Vuforia License Key是否正确无误地填入了VuforiaConfiguration。在Player Settings的XR Settings中确认Vuforia已正确启用。查看Unity Console中的错误信息。常见错误如“Failed to initialize Vuforia Engine”通常与License Key或设备不支持有关。6.3 交互、音效与UI的打磨触觉反馈在射箭释放的瞬间调用Handheld.Vibrate()可以给玩家一个短暂的震动反馈极大增强手感。注意iOS上此API无效需用原生插件。音效设计至少需要三种音效拉弓弦的紧绷声循环播放随力度变化音调、箭矢离弦的“嗖”声、命中靶心的“咚”声。使用AudioSource.PlayClipAtPoint在3D空间中播放可以增强空间感。UI设计UI应简洁且不影响AR视图。使用Unity的Canvas将渲染模式设置为Screen Space - Overlay。分数、剩余箭数等信息可以放在屏幕角落。引导用户检测地面的提示UI至关重要要清晰易懂。手势优化上述示例代码使用了简单的全屏触摸检测。在实际中最好通过Graphic Raycaster限制触摸初始区域在弓的附近防止误触UI或其他区域。也可以引入更复杂的手势如双指捏合调整弓的角度。7. 问题排查与调试技巧实录在开发过程中你一定会遇到各种问题。以下是我总结的一些常见问题及其解决方法希望能帮你节省大量时间。问题现象可能原因排查步骤与解决方案AR相机启动后黑屏无图像1. 相机权限未获取。2. Vuforia License Key错误或未配置。3. 设备不支持ARCore/ARKit。1. 检查AndroidManifest/iOS Info.plist中的权限声明。2. 双击检查VuforiaConfiguration中的License Key确保与应用门户中的一致且未过期。3. 在支持AR的设备上测试或检查Vuforia日志。图像目标Image Target无法识别1. 识别图特征点不足。2. 数据库未加载或目标未激活。3. 光照条件太差或反光。1. 使用Vuforia Target Manager检查图片的星级最好4-5星更换特征更丰富的图片。2. 在Image Target组件确认Database和Target Name已正确选择。3. 确保识别图光照均匀无强烈反光。虚拟物体抖动或漂移1. 摄像头对焦不稳。2. 识别图特征点临时丢失。3. 设备移动过快。1. 在代码中尝试锁定相机对焦模式需平台特定API。2. 使用质量更高的识别图或启用Vuforia的“Extended Tracking”。3. 在Vuforia Configuration中调整追踪参数或优化性能保证帧率稳定。地面检测Plane Finder不工作1. 环境缺乏纹理或过于光滑。2. Ground Plane功能未启用。3. Anchor Stage未设置。1. 在纹理丰富、光线充足的平面上尝试。2. 确认ARCamera上Vuforia Behaviour组件中的“Enable Ground Plane”已勾选。3. 检查Plane Finder组件中的“Anchor Stage”字段是否关联了一个有效的GameObject。构建到手机后崩溃或立即退出1. 脚本编译错误。2. 依赖的SDK如ARCore未正确打包。3. 内存或权限问题。1. 在Unity Editor中确保无任何编译错误。2. 对于Android检查Plugins/Android文件夹下的libs和manifest是否正确合并。有时需要手动处理ARCore的依赖。3. 查看手机LogcatAndroid或Xcode设备日志iOS获取具体崩溃信息。箭矢物理表现怪异旋转失控、穿透1. 刚体碰撞体设置不当。2. 物理时间步长Fixed Timestep不合适。3. 碰撞层Layer未正确设置。1. 检查箭矢的Collider形状和大小是否贴合模型Rigidbody的Mass、Drag值是否合理。2. 在Project Settings Time中尝试调整Fixed Timestep如0.02和Maximum Allowed Timestep。3. 确保箭矢和地面/靶子处于能相互碰撞的Layer中。调试技巧使用Debug.DrawRay和Debug.DrawLine在代码中可视化射线检测非常有助于理解箭矢的飞行路径和碰撞检测。Vuforia的DeviceTracker日志在VuforiaConfiguration中开启高级日志可以在Console中看到更详细的追踪状态信息。在Editor中模拟AR虽然不如真机但Vuforia提供了Play Mode下的模拟器可以模拟摄像头输入和图像识别用于快速调试游戏逻辑。在Game视图的下拉菜单中可以选择“模拟设备”。