基于ManoMotion+ARFoundation的Unity移动端裸眼交互方案(安卓版)

本文介绍了如何在Unity 2019.4版本中结合ARFoundation 3.1.3和ARCore 3.1.3搭建AR环境,并使用ManoMotion 1.3实现手势交互。通过导入和配置相关组件,创建AR识别图集,编写脚本来动态加载和管理模型,以及实现模型的点击和拖动交互。同时,文章还展示了使用ScreenTapFX插件添加点击特效的方法。
摘要由CSDN通过智能技术生成

 最终效果:

话不多说,直接开干:

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<string, GameObject> mPrefabs = new Dictionary<string, GameObject>();
    private Dictionary<string, GameObject> mCurSaveObjList = new Dictionary<string, GameObject>();

    private void Awake()
    {
        ImageTrackedManager = GetComponent<ARTrackedImageManager>();
        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;
    }
    /// <summary>
    /// 图片识别处理
    /// </summary>
    /// <param name="eventArgs"></param>
    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;
        
    }
    /// <summary>
    /// 屏幕特效原始资源
    /// </summary>
    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<Transform>();
        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)大功告成,打包测试。

  • 24
    点赞
  • 105
    收藏
    觉得还不错? 一键收藏
  • 32
    评论
好的,下面我会详细介绍一下如何在AR Foundation中实现手势交互。 首先,我们需要在Unity中创建一个AR SessionAR Session Origin对象,并将它们放入场景中。然后,我们需要将一个AR Camera作为AR Session Origin的子对象,并将其设置为主相机。接着,我们需要将要进行手势交互的物体作为AR Session Origin的子对象放入场景中。 然后,我们需要添加AR Foundation提供的手势识别组件。例如,如果我们要添加捏合手势识别,可以在AR Session Origin对象上添加PinchGesture组件。在组件中,我们可以设置手势的灵敏度、最小和最大缩放值等参数。我们还需要将要进行手势交互的物体作为PinchGesture的Target对象。 接下来,我们需要编写脚本来处理手势事件。例如,如果我们要实现捏合手势缩放物体的功能,可以编写一个脚本,将其挂载到物体上,并实现IPinchGestureHandler接口。在脚本中,我们需要实现OnPinch方法,该方法会在捏合手势发生时被调用。在该方法中,我们可以根据手势的缩放值来缩放物体。 除了捏合手势,AR Foundation还提供了其他的手势识别组件,例如旋转手势、平移手势、点击手势等。使用方法类似,只需要将对应的组件添加到AR Session Origin对象上,并编写相应的脚本来处理手势事件即可。 注意,使用AR Foundation进行手势交互时,需要在Unity中配置好AR环境,例如使用ARCore或ARKit等。同时,不同的AR平台可能会有些许差异,请根据具体的平台进行相应的调整。
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值