最终效果:
话不多说,直接开干:
Unity版本:2019.4
ARFoundation版本:3.1.3(4.0版本发布后会黑屏,目前未找到解决方案)
ARCore版本:3.1.3
MannoMotion版本:1.3
1.安装ARFoundation和ARCore:
在unity中打开 window→package manager,搜索 ar :
打开PlayerSetting中的OtherSetting, 删掉GraphicsAPI中的Vulkan(移动端不支持,打包会报错)
取消Multithreaded Pendering.
2.导入ManoMotion:
直接从我的网盘下:百度网盘 请输入提取码 提取码:2vdf
或者官网下载:Manomotion - ManoMotion(可能需要科学一下)
导入unity:
ManoMotion需要配置license key,需要去官网申请(需要花钱买权限)。当然,你也可以向我这个穷逼一样,直接使用插件包里自带的免费key。
官网申请license key方式:
3.接下来就带大家做一个小demo:
(1)首先在场景中添加ARCore的必要组件AR Session Origin和AR Session(记得删除场景自带的相机,并把AR Session Origin下的AR Camera 的tag设为主相机):
(2)添加ARManomotionManager,并为其绑定AR Session Origin下的AR Camera:
添加AManoVisualization,并为其绑定AR Session Origin下的AR Camera:
添加 ManomotionCanvas,并将其子物体statusAnimator绑定到ARManomotionManager的ManoEvents组件上。
(3)创建ARFoundation识别图集,并添加识别图片:
(4)为AR Session Origin添加图片识别组件ARTrackedImageManager,并为其绑定识别的图集和默认显示的模型预制体,修改其同时追踪的图片最大数量为1(越大越消耗性能)
(5)(接下来开始撸代码)因为在实际情况中,我们不可能只有一张图片,也不可能只显示一个模型,所以接下来要对模型进行统一加载和显示(我在Resources文件夹中创建ArObj文件,并把所有要加载的预制体放在里面,用来进行动态加载)。创建脚本ImageTrackingController,并添加到AR Session Origin物体上。代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.ARFoundation;
public class ImageTrackingController : MonoBehaviour
{
ARTrackedImageManager ImageTrackedManager;
private Dictionary mPrefabs = new Dictionary();
private Dictionary mCurSaveObjList = new Dictionary();
private void Awake()
{
ImageTrackedManager = GetComponent();
ChangeImageTrackingState();
}
void Start()
{
for (int i = 0; i < ImageTrackedManager.referenceLibrary.count; i++)
{
string bojName = ImageTrackedManager.referenceLibrary[i].name;
GameObject obj = Resources.Load("ArObj/" + bojName) as GameObject;
mPrefabs.Add(bojName, obj);
mCurSaveObjList.Add(bojName, null);
}
ChangeImageTrackingState();
}
private void OnEnable()
{
ImageTrackedManager.trackedImagesChanged += OnTrackedImagesChanged;
}
void OnDisable()
{
ImageTrackedManager.trackedImagesChanged -= OnTrackedImagesChanged;
}
///
/// 图片识别处理
///
///
void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
{
foreach (var trackedImage in eventArgs.added)
{
//当图片是第一次识别时,实例化对应的模型
OnImagesChanged(trackedImage);
}
for (int i = 0; i < eventArgs.updated.Count; i++)
{
//在ARCore中,图片丢失时,模型不会自动隐藏,所以这里对其进行手动隐藏处理
//当前识别的图片丢失时,隐藏对应的物体
if(eventArgs.updated[i].trackingState== UnityEngine.XR.ARSubsystems.TrackingState.Limited)
{
GameObject obj = mCurSaveObjList[eventArgs.updated[i].referenceImage.name];
if (obj != null)
obj.SetActive(false);
}
//当图片再次识别时,显示刚刚隐藏的对应的模型
else if(eventArgs.updated[i].trackingState == UnityEngine.XR.ARSubsystems.TrackingState.Tracking)
{
GameObject obj = mCurSaveObjList[eventArgs.updated[i].referenceImage.name];
if (obj != null)
{
obj.SetActive(true);
obj.transform.SetParent(eventArgs.updated[i].transform);
obj.transform.localPosition = Vector3.zero;
obj.transform.localRotation = Quaternion.Euler(0, 0, 0);
}
}
}
}
private void OnImagesChanged(ARTrackedImage referenceImage)
{
GameObject obj = Instantiate(mPrefabs[referenceImage.referenceImage.name], referenceImage.transform);
obj.transform.localPosition = Vector3.zero;
obj.transform.localRotation = Quaternion.Euler(0, 0, 0);
mCurSaveObjList[referenceImage.referenceImage.name]=obj;
}
#region 启用与禁用图像跟踪
public void ChangeImageTrackingState()
{
ImageTrackedManager.enabled = !ImageTrackedManager.enabled;
if (ImageTrackedManager.enabled)
//禁用图像跟踪;
SetAllImagesActive(true);
else
//启用图像跟踪;
SetAllImagesActive(false);
}
void SetAllImagesActive(bool value)
{
foreach (var img in ImageTrackedManager.trackables)
img.gameObject.SetActive(value);
}
#endregion
}
(6)OK,模型显示出来后怎么进行交互呢,接下来就是我们的重头戏了。首先,我们先创建一个简单的UI控制脚本,用来显示我们要显示的模型信息,代码和UI层级如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIController : MonoBehaviour
{
public static UIController instance;
private void Awake()
{
instance = this;
}
public Image Image_Des;
public Text Text_Des;
public void ShowDes(string desStr)
{
Image_Des.gameObject.SetActive(true);
Text_Des.text = desStr;
}
public void HideDes()
{
Text_Des.text = null;
Image_Des.gameObject.SetActive(false);
}
}
(7)然后,创建交互物体脚本 ManoObjInteraction,并添加到每个需要交互的预制体上,代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ManoObjInteraction : MonoBehaviour
{
public string des;
public GameObject mControObj;
Vector3 lastHandPos = Vector3.zero;
ManoGestureTrigger lastState;
private void Update()
{
//当触发点击手势时,触发点击事件
ManoGestureTrigger curState = ManomotionManager.Instance.Hand_infos[0].hand_info.gesture_info.mano_gesture_trigger;
if (curState != lastState)
{
if (curState == ManoGestureTrigger.CLICK)
{
OnClick();
}
lastState = curState;
}
//当前手势为捏住状态时,持续触发拖拽事件,否则结束拖拽
if (ManomotionManager.Instance.Hand_infos[0].hand_info.gesture_info.mano_gesture_continuous == ManoGestureContinuous.HOLD_GESTURE)
{
OnDraging();
}
else
{
EndDrag();
}
}
private void OnEnable()
{
UIController.instance.ShowDes(des);
}
private void OnDisable()
{
lastHandPos = Vector3.zero;
}
#region CallBack
void OnClick()
{
TrackingInfo tracking = ManomotionManager.Instance.Hand_infos[0].hand_info.tracking_info;
Vector3 worPos = Camera.main.ViewportToWorldPoint(new Vector3(tracking.poi.x, tracking.poi.y, tracking.depth_estimation));
ScreenTapFX.instance.PlayFX(worPos);
if (UIController.instance.Image_Des.gameObject.activeInHierarchy)
UIController.instance.HideDes();
else
UIController.instance.ShowDes(des);
}
public void OnDraging()
{
Vector3 vPos = ManomotionManager.Instance.Hand_infos[0].hand_info.tracking_info.poi;
Vector3 sPos = Camera.main.ViewportToScreenPoint(vPos);
if (lastHandPos == Vector3.zero)
{
lastHandPos = sPos;
return;
}
float offsetX = sPos.x - lastHandPos.x;
mControObj.transform.Rotate(-Vector3.up * offsetX * 0.5f, Space.Self);//绕Y轴进行旋转
lastHandPos = sPos;
}
public void EndDrag()
{
lastHandPos = Vector3.zero;
}
#endregion
}
(8)为了更好的显示我们是否触发了手指点击的事件,我们可以在手指点击的时候添加一些点击特效。从商店下载免费插件Cartoon FX Free,并导入项目:
然后创建特效生成脚本ScreenTapFX,代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScreenTapFX : MonoBehaviour
{
public static ScreenTapFX instance;
private void Awake()
{
instance = this;
}
///
/// 屏幕特效原始资源
///
public GameObject fxSample;
private void Start()
{
if (fxSample == null)
{
Debug.LogErrorFormat("没有找到屏幕特效");
this.enabled = false;
}
else
{
fxSample.SetActive(false);
}
}
public void PlayFX(Vector3 tapPos)
{
if (fxSample == null) return;
GameObject fx = CreateFX();
fx.name = Time.time.ToString(); ;
Transform fxRectTrans = fx.GetComponent();
fxRectTrans.localScale = new Vector3(0.05f, 0.05f, 0.05f);
fxRectTrans.position = tapPos;
fxRectTrans.LookAt(Camera.main.transform.position);
fx.SetActive(true);
}
private GameObject CreateFX()
{
GameObject newFX = null;
newFX = Instantiate(fxSample);
return newFX;
}
}
在场景中创建空物体,添加该特效生成脚本,然后为该组件绑定一个自己喜欢的点击特效:
(9)大功告成,打包测试。