最终效果:

话不多说,直接开干:

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)大功告成,打包测试。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐