项目构思:

人脸识别是AR应用开发中比较重要的功能。本项目打算将原有的手机、PC端的应用改造成AR应用,并展示人脸识别的功能

项目设计:

把经典游戏FlappyBird如图所示,转变为由通过人脸识别用鼻子控制Bird的飞行躲避障碍物的游戏

项目实施:

新建项目,命名为AR_FlappyBird,分别导入ARFoundation和ARCore XR Plugin,进行Android平台的基本设置(在ARFoundation的基本环境配置已经介绍过了)

在文末素材链接中,将素材包FlappyBird.unitypackage导入项目,在Sence目录下找到Bird场景文件,按下Ctrl+D组合键创建该文件的副本,并命名为AR_FlappyBird,双击打开。

在Hierarachy窗口的空白处单击鼠标右键,在弹出的菜单中选择XR>AR Session Origin和XR>AR Session,如图所示。

首先是对摄像头的调整,FlappyBird用的是MainCamera,我们的AR应用要用的是ARCamera,所以要把ARCamera的属性调整成和MainCamera基本一致.

我们选择AR Session Origin下的ARCamera,如图所示,调整transform的position为(0,0,-20),调整Projection为Orthographic,设置Clipping Planes Near为0.3,Far为1000,如图所示,然后再为ARCamera添加GameState的脚本组件,如图所示,用于判断游戏是否结束。这时点击运行就能得到跟原项目一样的效果。

随后把整个原项目的游戏对象都挂载到ARCamera的层级之下,这样才能实现我们整体的游戏画面不会因AR摄像机的运动而发生变化。

摄像头是要检测人脸上的鼻子来进行Bird的控制,因此我们需要添加用于人脸识别的组件ARFaceManager,选择Hierarchy窗口的ARSessionOrigin对象,为其挂载ARFaceManager组件,如图所示

补充一下,ARFaceManager组件在检测到人脸时会使用人脸的姿态实例一个预制体,即图中的Face Prefab属性,这个属性可以不用设置。同时当使用该组件时,默认使用的就是前置摄像头了。

5不能让背景遮住人脸,我们把背景隐藏,如图所示

6注意当使用了前置摄像头,我们X轴的朝向是相反的,X轴是向左的,这是为了在人脸上挂载虚拟物体时,可以保持虚拟物体正确的三轴朝向,但我们的UI方向就相反了,如图所示,这是未调整的前置摄像头会看到的画面。

因此我们要把UI都旋转180度,选择GameOver、StartGame、Score,设置Transform组件的Rotation为(0,180,0),调整后的画面如图所示

接下来调整管道的生成,首先找到Pipe脚本,修改udate中的内容,代码如下,使管道向X轴移动,这样当开启前置摄像头时,管道就会从右边出来。

    void Update ()

    {

        //若游戏处于运行中

        if (!GameState.Instance.GameOver)

        {

            //物体向X方向移动

            transform.Translate(new Vector3(Speed * Time.deltaTime, 0, 0));

        }

    }

}

再找到PipeLines脚本,由于现在用的是ARcamera,摄像机的位置不是固定的,因此要修改CreatePipe()函数中设置管道坐标的代码,让管道生成的位置再加上摄像机的偏移量,代码如下。

            go.transform.position = new Vector3(InitPosX, Random.Range(-2.4f, 1.5f))+this.transform.position;

新建一个C#脚本,命名为NoseTipManager,代码如下,用于检测鼻子位置,并控制小鸟飞行。

using System.Collections;

using System.Collections.Generic;

using Unity.Collections;

using UnityEngine;

using UnityEngine.XR.ARCore;

using UnityEngine.XR.ARFoundation;


[RequireComponent(typeof(ARFaceManager))]

[RequireComponent(typeof(ARSessionOrigin))]

public class NoseTipManager : MonoBehaviour

{

   

    private ARFaceManager mARFaceManager;

    public GameObject mInstantiatedPrefab;

    private ARSessionOrigin mSessionOrigin;

    private NativeArray<ARCoreFaceRegionData> mFaceRegions;

      private void Awake()  {

        mARFaceManager = GetComponent<ARFaceManager>();

         mSessionOrigin = GetComponent<ARSessionOrigin>();

       

    }

private void OnEnable()

    {

        mARFaceManager.facesChanged += OnFacesChanged;

    }

 void OnDisable() { mARFaceManager.facesChanged -= OnFacesChanged;

    }

    void OnFacesChanged(ARFacesChangedEventArgs eventArgs)

    {

        foreach (var trackedFace in eventArgs.added)

        {

            OnFaceChanged(trackedFace);

        }


        foreach (var trackedFace in eventArgs.updated)

        {

            OnFaceChanged(trackedFace);

        }

    }

    private void OnFaceChanged(ARFace refFace)

    {

        var subsystem = (ARCoreFaceSubsystem)mARFaceManager.subsystem;

        subsystem.GetRegionPoses(refFace.trackableId, Allocator.Persistent, ref mFaceRegions);

        for (int i = 0; i<mFaceRegions.Length; ++i)

        {

            var regionType = mFaceRegions[i].region;

            if (regionType == ARCoreFaceRegion.NoseTip)

            {

                mInstantiatedPrefab.transform.localPosition = mFaceRegions[i].pose.position;

                mInstantiatedPrefab.transform.localRotation = mFaceRegions[i].pose.rotation;

            }

    }

    }

    void OnDestroy()

    {

        if (mFaceRegions.IsCreated)

            mFaceRegions.Dispose();

    }

}

regionType == ARCoreFaceRegion.NoseTip用于判断鼻尖的位置,通过OnFaceChanged进行事件监听,在added、update中实时更新bird的姿态。

随后把脚本挂载到ARSessionOrigin上,把原场景中的bird游戏对象赋值给NoseTipManager,如图所示

\

在Scripts文件夹中找到Bird脚本,Ctrl+D复制脚本,改名为AR_Bird,注释掉update中用鼠标控制小鸟运动的代码,如图所示,再挂载到游戏对象bird上。

设置bird游戏对象的Rigidbody2D组件的Gravity Scale设置为0,让他不受重力的影响,同时设置Scale为(0.1,0.1,0.1),如图所示,SpriteRender中勾选X的Filp,即把贴图水平翻转,保证其在前置摄像头的正确朝向,如图所示。

.还存在一个问题,Scripts文件夹中的Restart脚本中OnMouseDown()函数是通过射线碰撞到物体,就调用这个函数,如图所示。

但在ARFaceManager组件在检测到人脸后,射线被遮挡,无法接触到用于重新加载的UI对象,场景无法重新加载,无法所以我们要换种方式进行场景的重新加载,这是通过Invoke函数进行延迟调用,修改Restart脚本,代码如下。

using UnityEngine;

using UnityEngine.SceneManagement;

public class Restart : MonoBehaviour

{

    void Start()

    {

        //设置挂载本脚本物体的父物体 初始状态为隐藏

        //这里即是 初始时隐藏 “GameOver”物体

        this.transform.parent.gameObject.SetActive(false);

    }

    private void Update()

    {

        if (GameState.Instance.GameOver) {

            //当游戏结束后,3秒后重新开始游戏

            Invoke("SceneManagerRestart", 3f);

        }

    }


    private void SceneManagerRestart()

    {

        //重新加载本游戏场景

        SceneManager.LoadScene("AR_FlappyBird", LoadSceneMode.Single);

    }

}

项目测试:

打包成apk发布到手机运行,效果如下:

注:这里用了《VR AR MR项目开发实战》案例的随书资源

素材链接:

链接:https://pan.baidu.com/s/12BjOCMKFrCjXzB9bgRWmew?pwd=tudp
提取码:tudp

源码链接:

链接:https://pan.baidu.com/s/1p5oFq2cNpGPH6T-aSPj5uQ?pwd=83mc
提取码:83mc

安装包链接:

链接:https://pan.baidu.com/s/1eu_BKvCAB389NcH-oxrosw?pwd=fbfk
提取码:fbfk

注意去官网看手机是否支持arcore,不然就是闪退黑屏

ARFoundation项目学习记录(一)--配置环境-CSDN博客

官网链接:

支持 ARCore 的设备  |  Google Developers

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐