1. 项目概述与核心价值最近在捣鼓一个AR小项目想实现不依赖任何额外硬件只用手机摄像头就能玩转手势交互。市面上方案不少但要么收费不菲要么配置复杂得让人头大。直到我发现了Manomotion SDK一个宣称免费、支持Unity、能直接在Android和iOS上跑的手势识别方案。这听起来简直是为独立开发者和中小团队量身定做的。但说实话第一次接触时看着官方文档和零散的社区帖子心里还是有点打鼓尤其是在Unity 2020.3.33这个LTS版本和Android平台上的配置总怕哪里一步没对就掉坑里。所以我花了几天时间把从零开始到成功在真机上跑通手势识别的完整流程彻底走了一遍过程中踩的坑、总结的技巧都在这篇保姆级教程里了。无论你是想快速做个AR手势demo验证想法还是打算在成熟项目里集成低成本的手势功能这篇基于Unity 2020.3.33和Android平台的实战记录应该都能让你少走不少弯路。Manomotion SDK的核心价值在于它把复杂的手势识别算法打包成了一个即插即用的Unity插件。你不需要从头研究计算机视觉和机器学习只需要在Unity里导入插件、配置几个场景、写点简单的逻辑就能实时获取到手掌、手指的3D位置、姿态以及预定义的手势比如握拳、比耶、点赞。这对于开发教育类AR应用、虚拟试戴、手势控制的游戏或者无障碍交互应用来说是一个快速启动的利器。而且它支持单双手跟踪精度和响应速度在主流手机上实测下来对于很多非极致要求的场景已经足够用了。2. 环境准备与SDK获取2.1 基础环境清单在开始导入SDK之前确保你的开发环境是干净且匹配的这能避免至少50%的莫名错误。以下是经过验证的稳定组合Unity编辑器Unity 2020.3.33f1c2 (LTS)。这是关键不同的小版本号如2020.3.30和2020.3.33在Android构建管线或包管理上可能有细微差别强烈建议使用这个确切版本。你可以通过Unity Hub进行安装。Android开发环境JDK安装Oracle JDK 8 或 OpenJDK 8。Unity 2020.3版本对JDK 11及以上版本的支持有时会有问题。我使用的是OpenJDK 8 (AdoptOpenJDK)。Android SDK NDK最简单的方式是在Unity编辑器的Preferences - External Tools中勾选Android部分下的JDK, SDK, NDK的“Install with Unity”选项让Unity自动安装匹配的版本。这能保证最大兼容性。手动配置路径容易出错。GradleUnity通常会内置一个Gradle版本用于构建。保持默认即可除非你有特殊需求。测试设备准备一部Android手机建议Android 9.0及以上并开启开发者选项和USB调试。这是后续真机测试的必需品。注意千万不要在环境没配好时就急着导入SDK。我曾经试过先导SDK再配Android环境结果Unity项目设置里一堆红色错误清理起来非常麻烦。正确的顺序是装好Unity - 配置好External Tools里的Android路径 - 再处理SDK。2.2 获取Manomotion SDKManomotion SDK目前主要通过其官方网站提供下载。你需要注册一个免费账户登录后才能在开发者后台下载SDK包。通常下载下来的是一个.unitypackage文件这是Unity的标准插件格式。这里有个小技巧留意你下载的SDK版本号最好去官方文档或下载页面看一眼它声明的兼容Unity版本。虽然Manomotion对2020.3.x系列支持较好但确认一下能更安心。2.3 创建与设置Unity项目打开Unity Hub创建一个新的3D项目。项目名称和位置按你喜欢来定。创建完成后第一件事不是导入SDK而是先设置项目的基本构架目标平台切换在File - Build Settings中选择Android平台然后点击Switch Platform。这个过程可能会花点时间Unity需要重新导入一些资源以适应Android。Player Settings关键配置切换到Android平台后点击Player Settings以下几个设置必须检查Other Settings 部分Minimum API Level设置为Android 7.0 ‘Nougat’ (API level 24)或更高。Manomotion需要一些较新的系统API。Target API Level设置为与你测试手机系统相匹配的API级别或直接选择Automatic。Scripting Backend选择IL2CPP。这是发布到移动平台尤其是64位的推荐且更稳定的后端。Target Architectures勾选ARM64。现在的手机基本都是64位系统勾选这个能确保性能和应用商店的兼容性。XR Settings 部分如果存在由于我们做的是基于摄像头的AR并非必须依赖Unity的AR Foundation等XR插件所以这部分通常保持默认即可。但如果你后续要结合ARCore使用则需要在这里启用。3. SDK导入与基础配置3.1 导入Unity Package环境准备就绪后就可以导入SDK了。在Unity编辑器中选择Assets - Import Package - Custom Package...然后找到你下载的.unitypackage文件。导入时会弹出一个窗口显示包内所有文件。通常全部勾选并导入即可。导入过程可能会提示一些关于API兼容性或命名空间的警告只要不是错误Error可以先忽略。导入完成后你的Project窗口的Assets文件夹下应该会出现一个名为ManoMotion或类似的文件夹。这就是SDK的核心所在。里面通常包含Scripts核心脚本、Prefabs预制体、Materials材质、Shaders着色器以及Documentation文档等子文件夹。3.2 配置第一个手势识别场景Manomotion SDK的设计思路是提供即用的预制件Prefab和管理器Manager。配置场景最快的方法如下清空示例场景创建一个新的场景或使用默认场景删除场景中自带的Main Camera和Directional Light我们稍后会用SDK的预制体替代。拖入核心预制体在ManoMotion/Prefabs文件夹下找到名为ManoMotion或ManoManager的预制体将其拖入场景Hierarchy中。这个预制体是手势识别系统的中枢。检查自动生成的摄像机拖入预制体后你可能会发现场景里自动出现了一个新的摄像机比如叫ManoCamera或者原预制体下包含一个摄像机子物体。这个摄像机会被脚本用来渲染手机摄像头画面。配置ManoManager脚本选中场景中的ManoMotion预制体在Inspector面板中你会看到挂载的ManoManager脚本。这里有一些关键参数Camera通常会自动关联上场景中那个专用的摄像机。如果没有手动拖拽赋值。Target Framerate建议设置为60以获得更流畅的手部跟踪体验。其他高级设置如手势平滑度、检测置信度阈值首次运行时可以先保持默认后续根据效果微调。3.3 添加可视化反馈可选但推荐为了直观地看到手势识别结果SDK通常提供一些可视化预制体手部网格/骨架在ManoMotion/Prefabs/Visualization路径下寻找类似Hand Mesh、Hand Skeleton或Wrist Information的预制体。将其拖入场景。关联到管理器通常你需要将这个可视化预制体拖拽到ManoManager脚本的某个公开字段上例如Hand Visualization Prefab。这样当SDK检测到手部时就会在对应的3D位置实例化这个可视化模型。手势状态UISDK可能还包含一个用于在屏幕上显示当前识别到的手势如“FIST”、“PINCH”的UI预制体。同样找到并拖入场景的Canvas下并按照脚本要求进行关联。完成以上步骤后点击Unity的播放按钮在Game视图里你应该就能看到模拟的手机摄像头视图可能是测试图像或颜色背景。但目前还看不到手势识别效果因为我们需要构建到真机。4. Android平台构建与部署4.1 项目构建设置复查在打包前再次确认Build Settings和Player SettingsBuild Settings确保当前场景已被添加到Scenes In Build列表中拖入或点击Add Open Scenes。Player Settings - Other SettingsPackage Name修改为一个唯一的反向域名格式如com.yourcompany.handdemo。Version设置应用版本。Write Permission确保External (SDCard)写入权限被勾选。虽然手势识别本身不一定需要但SDK或你的应用可能需要临时存储数据。Internet Access如果需要可以勾选Required。4.2 解决可能的编译错误与警告首次为Android构建时可能会遇到一些错误或警告常见的有.NET版本/API兼容性警告如果SDK使用了较新的C#特性而项目设置是较旧的.NET Standard 2.0可能会报警告。在Player Settings - Other Settings - Configuration中将Api Compatibility Level暂时改为.NET 4.x通常可以解决。但要注意这可能会增加包体大小。AndroidManifest冲突如果导入的SDK自带AndroidManifest.xml文件可能会与Unity自动生成的冲突。Manomotion SDK通常以插件形式提供其Manifest配置会通过Plugins/Android文件夹自动合并。如果构建失败提示Manifest错误可以尝试删除项目中的Assets/Plugins/Android/AndroidManifest.xml如果有让Unity重新生成但更安全的方法是检查SDK文档看是否需要手动合并某些权限如摄像头权限。缺失的Gradle依赖某些SDK版本可能需要额外的Gradle仓库或依赖。这通常会在Assets/Plugins/Android下的mainTemplate.gradle或baseProjectTemplate.gradle文件中配置。Manomotion一般不需要但如果构建失败提示找不到类可以检查这些文件。4.3 构建APK与真机安装用USB数据线连接你的Android手机并确保手机已开启“USB调试”在开发者选项中。在Unity的Build Settings窗口中点击Build And Run。选择一个位置保存APK文件。Unity会开始编译、打包并自动将APK安装到已连接的手机上。第一次构建可能会花费几分钟时间。安装完成后手机会自动启动应用。请立即在手机上授予应用“相机”权限这是手势识别能工作的前提。5. 核心功能实现与脚本交互5.1 理解SDK的数据流当应用在手机上运行你把手放在摄像头前时Manomotion SDK内部的工作流程大致如下图像采集ManoManager控制摄像头获取实时视频帧。手部检测与跟踪SDK的核心算法在后台运行从视频帧中检测手部区域并持续跟踪其运动。数据解析算法会计算出丰富的骨骼信息包括手掌中心Wrist、21个手部关键点类似MediaPipe的手部模型的3D位置、手的整体边界框、手势分类结果等。事件派发这些数据被封装成结构体如HandInfo并通过C#事件或回调函数的方式提供给开发者使用。你的代码主要就是订阅这些事件然后根据获取到的HandInfo数据来驱动游戏逻辑。5.2 编写第一个手势交互脚本让我们创建一个简单的脚本当用户做出“握拳”手势时让场景中的一个物体变色。创建脚本在Project窗口中右键选择Create - C# Script命名为GestureController。编写核心逻辑双击打开脚本进行编辑。using UnityEngine; using Manomotion; // 引入Manomotion的命名空间 public class GestureController : MonoBehaviour { // 一个可拖拽赋值的目标物体比如一个Cube public GameObject targetObject; // 用于存储物体原始颜色的材质 private Material originalMaterial; // 定义握拳手势对应的枚举值需参考Manomotion文档 private const ManoGestureContinuous FIST_GESTURE ManoGestureContinuous.CLOSED_HAND_GESTURE; void Start() { if (targetObject ! null) { // 获取物体材质并保存原始颜色 originalMaterial targetObject.GetComponentRenderer().material; } // 订阅Manomotion的手势信息更新事件 // 这是关键步骤当SDK处理完一帧数据后会触发此事件 ManomotionManager.OnManoMotionFrameProcessed HandleManoMotionFrameProcessed; } void OnDestroy() { // 非常重要在脚本销毁时取消订阅防止内存泄漏 ManomotionManager.OnManoMotionFrameProcessed - HandleManoMotionFrameProcessed; } // 事件处理方法 private void HandleManoMotionFrameProcessed() { // 从ManomotionManager的单例中获取当前帧的手部信息 HandInfo handInfo ManomotionManager.Instance.Hand_infos[0]; // 通常索引0代表第一只手 // 检查是否检测到了手 if (handInfo.gestureInfo.manoClass ! ManoClass.NO_HAND) { // 检查持续手势如握拳、张开手是否为握拳 if (handInfo.gestureInfo.manoGestureContinuous FIST_GESTURE) { ChangeObjectColor(Color.red); } else { // 如果不是握拳恢复原色 ResetObjectColor(); } // 你也可以获取离散手势如捏合、滑动 // ManoGestureTrigger triggerGesture handInfo.gestureInfo.manoGestureTrigger; // 或者获取手部3D位置手掌中心 // Vector3 wristPosition handInfo.trackingInfo.skeleton.joints[0]; // 0号关节通常是手腕 } else { // 没有检测到手时也恢复原色 ResetObjectColor(); } } void ChangeObjectColor(Color newColor) { if (targetObject ! null) { targetObject.GetComponentRenderer().material.color newColor; } } void ResetObjectColor() { if (targetObject ! null originalMaterial ! null) { targetObject.GetComponentRenderer().material.color originalMaterial.color; } } }应用脚本将GestureController脚本拖拽到场景中的任意GameObject上比如一个空的GameObject命名为GameManager。关联物体在Inspector面板中将场景中你想要变色的那个物体比如一个Cube拖拽到脚本的Target Object字段上。构建并测试再次构建APK到手机。运行后当你对着摄像头握拳时指定的物体应该会变成红色松开手则恢复原色。5.3 实现更复杂的交互手势控制物体移动理解了基础的数据获取我们可以更进一步用手势控制物体的3D位置。例如用手掌的屏幕坐标来控制一个UI元素的移动。using UnityEngine; using UnityEngine.UI; // 如果需要控制UI using Manomotion; public class HandUIFollower : MonoBehaviour { public RectTransform uiElementToMove; // 要移动的UI元素 public Camera mainCamera; // 用于坐标转换的摄像机应该是ManoCamera void Start() { ManomotionManager.OnManoMotionFrameProcessed UpdateUIFromHand; if (mainCamera null) { // 尝试自动查找ManoCamera mainCamera Camera.main; } } void OnDestroy() { ManomotionManager.OnManoMotionFrameProcessed - UpdateUIFromHand; } private void UpdateUIFromHand() { HandInfo handInfo ManomotionManager.Instance.Hand_infos[0]; if (handInfo.gestureInfo.manoClass ! ManoClass.NO_HAND uiElementToMove ! null) { // 获取手掌中心的屏幕坐标归一化0到1 Vector3 normalizedWristPosition handInfo.trackingInfo.palmCenter; // 将归一化坐标转换为屏幕像素坐标 float screenX normalizedWristPosition.x * Screen.width; float screenY normalizedWristPosition.y * Screen.height; // 将屏幕坐标转换为UI的局部坐标假设Canvas是Screen Space - Overlay Vector2 localPos; RectTransformUtility.ScreenPointToLocalPointInRectangle( uiElementToMove.parent as RectTransform, // UI元素的父级RectTransform new Vector2(screenX, screenY), null, // 对于Overlay Canvas相机参数为null out localPos ); uiElementToMove.localPosition localPos; } } }这个脚本展示了如何将SDK输出的归一化手部位置转换到UI坐标系中实现一个简单的“手指点哪UI跟到哪”的效果。6. 性能优化与调试技巧6.1 性能调优参数在真机上运行时你可能会关心帧率和耗电。ManoManager脚本上的一些参数可以帮助你平衡效果和性能Target Framerate前面提到设为60。如果觉得发热或耗电快可以尝试降到30。Processing Resolution这是最重要的性能参数之一。它决定了送给手势识别算法的图像分辨率。HIGH精度最好但最耗性能LOW最省电但可能丢失细节。对于大多数手机MEDIUM是一个不错的起点。你可以在脚本运行时动态调整它观察效果和帧率的变化。Smoothing Filter对手部位置和手势进行平滑滤波。值越大越平滑但延迟感也会增加。对于需要快速响应的游戏可以调低对于需要稳定性的UI控制可以调高。多手跟踪默认可能只跟踪一只手。如果你需要同时跟踪两只手确保在设置中启用了相关选项但这会显著增加计算量。6.2 真机调试与日志查看在真机上调试AR应用查看日志是定位问题的关键。使用Android LogcatUnity与Android设备连接时可以通过Window - Analysis - Android Logcat打开Logcat窗口。确保设备已连接并在Logcat窗口中选择你的设备。运行应用所有Unity的Debug.Log以及系统日志都会在这里打印。这是查找崩溃原因、查看SDK内部警告的必备工具。在脚本中添加调试信息在你的手势脚本中可以添加Debug.Log来输出关键数据比如检测到的手势类型、手部坐标等。这能帮你确认数据流是否正常。Debug.Log($Detected Gesture: {handInfo.gestureInfo.manoGestureContinuous}, Hand Position: {normalizedWristPosition});处理权限拒绝如果你的应用启动后一片黑屏首先检查手机是否弹出了相机权限请求并被你不小心拒绝了。可以在脚本的Start方法中检查应用是否有相机权限如果没有可以提示用户或引导用户去设置中开启。Unity的Application.HasUserAuthorization可以用来检查但在Android上更可靠的方式是尝试访问摄像头如果失败则给出提示。6.3 常见问题与解决方案速查表以下是我在配置和开发过程中遇到的一些典型问题及解决方法问题现象可能原因解决方案构建失败提示Gradle错误JDK、SDK、NDK路径错误或版本不兼容Gradle版本冲突。1. 检查UnityPreferences - External Tools中Android路径是否正确且已安装。2. 尝试在Player Settings - Publishing Settings中勾选Custom Base Gradle Template并编辑生成的baseProjectTemplate.gradle文件调整Gradle版本或仓库源此操作需谨慎建议先备份。3. 最彻底的方法关闭Unity删除项目根目录下的Library、Temp、Obj文件夹以及[项目名].csproj文件然后重新打开Unity让它重新生成。应用安装后打开立即闪退缺少必要的Android权限SDK与目标设备ABI不兼容脚本存在运行时错误。1. 确认AndroidManifest.xml中已声明相机权限 (uses-permission android:nameandroid.permission.CAMERA /)。2. 检查Player Settings - Other Settings - Target Architectures确保至少勾选了ARMv7和ARM64。3. 通过Android Logcat查看崩溃瞬间的日志寻找NullReferenceException或MissingMethodException等错误信息。摄像头画面黑屏但应用未崩溃相机权限未授予ManoManager的Camera字段未正确赋值手机摄像头被其他应用占用。1. 确保手机已为应用开启相机权限。2. 检查Hierarchy中ManoMotion预制体上ManoManager脚本的Camera字段是否绑定了场景中的摄像机。3. 杀掉后台可能占用摄像头的应用如其他相机App、微信视频等。手势检测不稳定或延迟高光线环境太暗手距离摄像头太近或太远手机性能不足Processing Resolution设置过高。1. 确保在光线充足的环境下使用。2. 让手与摄像头保持约30-80厘米的距离并尽量在画面中央。3. 在ManoManager上将Processing Resolution从HIGH调至MEDIUM或LOW。4. 关闭手机上的省电模式。可视化手部模型位置漂移或抖动缺乏平滑滤波摄像头画面存在运动模糊。1. 适当增加ManoManager上的Smoothing Filter值。2. 尝试在更稳定的环境下操作避免手部或手机快速移动。脚本中获取不到HandInfo数据事件订阅代码写在了Update中导致重复订阅/取消订阅ManoMotionManager.Instance为空。1. 确保事件订阅 (OnManoMotionFrameProcessed ) 只在Start或Awake中执行一次。2. 检查Manomotion SDK的核心管理器是否已正确初始化。通常拖入预制体后会自动完成。可以在Start中加Debug.Log(ManomotionManager.Instance)来检查。7. 项目打包与发布注意事项当你完成了开发和测试准备发布应用时还有最后几关要过图标与闪屏在Player Settings - Icon和Splash Image中设置好你的应用图标和启动画面。版本号与包名再次确认Player Settings中的Version和Package Name是最终发布版本。减少APK大小在Build Settings中点击Export Project而不是直接构建APK然后用Android Studio打开进行更精细的优化和打包如启用ProGuard代码混淆和压缩。在Unity的Project Settings - Editor中可以为Asset Serialization选择Force Text这有时能略微减小包体但主要影响的是项目文件本身。检查Player Settings - Publishing Settings如果不需要可以取消勾选Split APKs by target architecture但注意兼容性。Keystore签名发布到应用商店如Google Play需要你自己的密钥库Keystore文件对APK进行签名。务必妥善保管这个文件和密码丢失后将无法更新同一个应用。隐私政策因为你的应用使用了摄像头权限在大多数应用商店上架时都需要提供隐私政策链接说明你如何收集和使用摄像头数据。Manomotion SDK本身可能也要求你在应用中声明其使用。走完这套流程从环境搭建、SDK配置、功能开发、调试优化到最终打包一个基于Manomotion SDK的AR手势交互应用就真正从想法变成了手机上的可运行产品。整个过程最磨人的往往不是代码本身而是环境配置和真机调试时那些意想不到的兼容性问题。希望这篇超详细的记录能帮你把路上的坑填平一大半。