Unity平台 + Vuforia SDK实现的AR程序开发

  • 模型的三大操作(平移 旋转 缩放)代码
  • 一.平移
  • 二.旋转缩放
  • 三.操作代码解析
  • 模型的选定(射线法)
  • 食用方法
  • 自发光组件
  • 食用方法
  • VirtualButton虚拟按钮
  • 关于UI的Canvas使用方法请自行搜索
  • 关于按钮OnPress变色(红色)OnRelease蓝色
  • VB按钮的控制脚本构建
  • 代码终结篇总结


作者写在前面的话:
接触了1个多月的Vuforia以及AR知识,感觉可能了解为什么15-17爆发后,就很少有连续不断更新的教程了,一个原因是风头过了,大批开发者离开这个领域,理由很简单,没啥东西了,AR就这点东西对普通开发者来说,三维跟踪注册,触屏交互。再深究下去,没有利润养不起自己养不起开发人员。所以现在存活的AR技术提供商,只有面向工业医疗制造业航天航空等狗大户才能继续活下去。
但是可以预见的是,AR即是未来。
只是需要突破。交互,光学显示,虚实融合
***最后,顶,点赞,评论,收藏三连!如果觉得有用的话!是对作者最大的鼓励
***最后,顶,点赞,评论,收藏三连!如果觉得有用的话!是对作者最大的鼓励
***最后,顶,点赞,评论,收藏三连!如果觉得有用的话!是对作者最大的鼓励

在《【Vuforia AR Unity 2018.3.12f1】MikuAR安卓程序开发实践(二)MMD篇_2019.4.23》中已经放出了项目源码,也给出了详细的操作的步骤。
本篇主要针对解析的是对于按钮及其他控制的代码详情。下面开始干货时间。

模型的三大操作(平移 旋转 缩放)代码

所有的物体操作代码,都是放在该物体GameObject上新建的Script脚本中,一般来说,直接写在void Update()函数中。

一.平移

网上的教程关于平移很多,只不过,emmm写的不是很清楚,比如背景环境没写明白,也没写清楚逻辑,实验了很多次伸手党,结果经过很多次很麻烦的测试,还是不行。hhhh还是得自己来一步一步解析

平移参考资源网址:

由2.3博客中不免发现,核心是transform.Translate()函数,emmm直接去1.Unity官网查询这个函数了。

unity发布程序没有Linux ARM_System


欸!官网给出了实例,超级棒!

好了具体的实践和分析过程我就不列举出来,直接给成功的代码和注释。【伸手党最爱】

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Translate : MonoBehaviour //Script脚本名
{
    float speed = 0.2f;
    float Y, X;//设置X,Y轴中间变量可不设
    // Start is called before the first frame update
    void Start()
    {
        
    }
    
    // Update is called once per frame
    void Update()
    {
        if (Input.touchCount > 0) //如果检测到有至少一个触屏点
        {
            //Touch touch = Input.GetTouch(0);//赋值第一个触碰点
            for (int i = 0; i < Input.touchCount; ++i)//所有触碰点移动均进行平移操作
            {
                if (Input.GetTouch(i).phase == TouchPhase.Began)//触碰点状态为 刚触碰
                {
                    //计算物体XY位移量=屏幕位移*放缩倍数,手指位移时间小于1s缩,大于1放大,speed为固缩
                    X = Input.GetTouch(i).deltaPosition.x * Time.deltaTime * speed;
                    Y = Input.GetTouch(i).deltaPosition.y * Time.deltaTime * speed;
                    transform.Translate(X, Y, 0, Space.World);//这个语句是核心,其他都是判断及参量构建
                }
            }
        }
    }
}

食用方法: 在需要平移的物体上挂载/新建 C# Script脚本 Component

unity发布程序没有Linux ARM_System_02


核心语句: transform.Translate(X, Y, 0, Space.World);

关于这里的X,Y。我实验得出的结果是,这个Translate是屏幕平面的X,Y轴,对应Sence中的X,Z轴。emmm不妨大碍。就是有点拧巴。

二.旋转缩放

拿来即可用,这是我查找到的最棒的代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotateScale : MonoBehaviour //新建的脚本名
{
    private Touch oldTouch1;  //上次触摸点1(手指1)  
    private Touch oldTouch2;  //上次触摸点2(手指2)  

    void Start()
    {

    }

    void Update()
    {
        //没有触摸  
        if (Input.touchCount <= 0)
        {
            return;
        }

        //单点触摸, 水平上下旋转  
        if (1 == Input.touchCount)
        {
            Touch touch = Input.GetTouch(0);
            Vector2 deltaPos = touch.deltaPosition;
            transform.Rotate(Vector3.down * deltaPos.x, Space.World);
            transform.Rotate(Vector3.right * deltaPos.y, Space.World);
        }

        //多点触摸, 放大缩小  
        Touch newTouch1 = Input.GetTouch(0);
        Touch newTouch2 = Input.GetTouch(1);

        //第2点刚开始接触屏幕, 只记录,不做处理  
        if (newTouch2.phase == TouchPhase.Began)
        {
            oldTouch2 = newTouch2;
            oldTouch1 = newTouch1;
            return;
        }

        //计算老的两点距离和新的两点间距离,变大要放大模型,变小要缩放模型  
        float oldDistance = Vector2.Distance(oldTouch1.position, oldTouch2.position);
        float newDistance = Vector2.Distance(newTouch1.position, newTouch2.position);

        //两个距离之差,为正表示放大手势, 为负表示缩小手势  
        float offset = newDistance - oldDistance;

        //放大系数
        float scaleFactor = offset / 100f;
        Vector3 localScale = transform.localScale;
        Vector3 scale = new Vector3(localScale.x + scaleFactor,
                                    localScale.y + scaleFactor,
                                    localScale.z + scaleFactor);

        //最小缩放到 0.1 倍  
        if (scale.x > 0.1f && scale.y > 0.1f && scale.z > 0.1f)
        {
            transform.localScale = scale;
        }
        
        //记住最新的触摸点,下次使用  
        oldTouch1 = newTouch1;
        oldTouch2 = newTouch2;
    }
}

三.操作代码解析

  1. 平移的话,没有太多要点,因为太简单了。主要就是先判断是否触摸,然后再读取一共有多少个触摸点,然后再进行循环,以保证所有的触摸点的移动都被读取,而不是只读第一个//Touch touch = Input.GetTouch(0);所以要进入for循环。Input.GetTouch()可以在Unity官网手册查询具体的,而且Unity的手册都带有实例,而且对于新手来说有可能超级有用,很基础很实用的例子。如果你找不到老师,请去官网翻手册,这是最高效的信息来源。
  2. 关于旋转和缩放,旋转核心语句是transform.Rotate(),需要确定旋转方向,即是Vector3.down及Vector3.right两个参量,这个两个参量是可以更改的,看你自己的需求,需要注意的是旋转是单点操作和平移是冲突的。缩放的话核心语句是transform.localScale属性的赋值,思路是先确定两触摸点距离变化是增大还是减小,然后获取模型原各轴缩放尺寸,将增大/减少值更新至原缩放尺寸,然后再赋值回到模型各轴缩放尺寸属性。
  3. 我个人将模型的旋转平移写在一个脚本里,将平移单独写在一个脚本里,通过VirtualButton来控制脚本的enable属性,从而实现对模型的平移与旋转可在一个程序中运行。如下图,是平移VB按钮的控制脚本,在OnPress的时候将Miku模型上的RotateScale脚本关闭,并同时将Translate脚本启用。

模型的选定(射线法)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RayControl : MonoBehaviour
{
    public Camera camera;
    public HighlightableObject ho;
    float speed = 0.5f;
    float Y, X;
    GameObject gameObj = null;
    RaycastHit hitInfo;
	//下面注释的是从某博客上获取的一段代码,主要是用于其他GameObject静态调用获取物体的公共方法
    /*public static GameObject getCollisionObc(Camera camera, Vector3 touchPos)
    {
        UnityEngine.Ray ray = camera.ScreenPointToRay(touchPos);//从摄像机发出到点击坐标的射线
        RaycastHit hitInfo;
        GameObject gameObj = null;
        if (Physics.Raycast(ray, out hitInfo))
        {
            //获取碰撞的物体
            Debug.DrawLine(ray.origin, hitInfo.point, Color.red);//划出射线,只有在scene视图中才能看到
            gameObj = hitInfo.collider.gameObject;
            return gameObj;
        }
        else
        {
            return null;
        }
    }*/
    // Start is called before the first frame update
    void Start()
    {
        //ho = GetComponent<HighlightableObject>();
        ho.FlashingOn(Color.red, Color.blue, 1f); //此函数是自发光插件的函数,用于启动发光
    }

    // Update is called once per frame
    void Update()
    {
        
        if (Input.touchCount > 0)
        {
             //Move the cube if the screen has the finger moving.
            for (int i = 0; i < Input.touchCount; ++i)
            {
                if (Input.GetTouch(i).phase == TouchPhase.Began)
                {

                     //Construct a ray from the current touch coordinates
                    UnityEngine.Ray ray = camera.ScreenPointToRay(Input.GetTouch(i).position);
                    //camera变量是在Unity中拖动ARCamera赋值的,因为他是Public可以在Unity看到赋值框

                     //Create a particle if hit
                    if (Physics.Raycast(ray, out hitInfo)) //如果从摄像机发出的射线碰到物体返回hitInof变量
                    {
                        gameObj = hitInfo.collider.gameObject; //获取该物体
                        X = Input.GetTouch(i).deltaPosition.x * Time.deltaTime * speed;
                        Y = Input.GetTouch(i).deltaPosition.y * Time.deltaTime * speed;
                        gameObj.transform.Translate(X, Y, 0, Space.World); //平移该物体
                        ho.FlashingOff(); //关闭模型自发光
                    }
                }
            }
        }
    }
}

食用方法

①这个脚本是 挂载/Add Component 在ARCamera上的

②需要被射线选中的对象必须加collider

③被射中的GameObject可以用"RaycastHit hitInfo"变量来调用。

unity发布程序没有Linux ARM_System_03

自发光组件

模型自发光资源:
HighlightingSystem插件学习文档 博客 魔卡先生

食用方法

  1. Import HighlightingSystem.unitypackage。
    在MUKIARProgram\MyTestAR_SourceCode\Assets\ImportPackage路径下
    //插件导入时如若报错,将错误行代码注释掉即可
  2. 在ARCamera上挂载 Highlighting Effect 脚本,用于渲染外发光。不需要自己写搜索脚本名即可。
  3. unity发布程序没有Linux ARM_System_04

  4. 在需要外发光效果的物体上添加 Highlightable Object 脚本,用于外发光效果显示。
  5. 在需要外发光效果的物体上添加控制脚本,或者是在其他GameObject的脚本中获取该物体的HighlightableObject ho对象。我是在ARCamera中的射线法脚本中通过Public HighlightableObject直接拖拽Miku对象获取Miku的ho对象,从而控制Miku的自发光。
  6. unity发布程序没有Linux ARM_sed_05

  7. 核心控制语句:
    //循环往复外发光开启(参数为:颜色1,颜色2,切换时间)
    m_ho.FlashingOn(Color.green, Color.blue, 1f);
    //关闭循环往复外发光
    m_ho.FlashingOff();
    //持续外发光开启(参数:颜色)
    m_ho.ConstantOn(Color.yellow);
    //关闭持续外发光
    m_ho.ConstantOff();

VirtualButton虚拟按钮

  • VirtualButton是依赖于ImageTarget生成的。
  • VirtualButton敏感度不是很有效,强烈不建议放置在识别图边缘区域,因为你会发现你无法控制按钮状态。
  • 并且通过实验可以得知VirtualButton不能放置ImageTarget外
  • VirtualButton最好放置在ImageTarget中心区域,那里手指触碰实际效果最好。
  • 单独的VirtualButton,可以使用,但无法在屏幕上看到,需要与Canvas搭配使用,Canvas是用来显示VirtualButton区域。VirtualButton本身是一个碰撞域,无形无色。

关于UI的Canvas使用方法请自行搜索

并不是特别困难

Canvas用途:显示文字

unity发布程序没有Linux ARM_缩放_06

关于按钮OnPress变色(红色)OnRelease蓝色

1.创建Canvas层,并创建下属材质子层
2.在材质子层创建下属文字层
3.在VB虚拟按钮中控制Canvas中对应的材质子层的材质

  • Canvas层【下属3个按钮的材质子层】
  • 材质子层【用于显示在用户屏幕上颜色】【材质在Materials文件夹中】
  • 文字层

VB按钮的控制脚本构建

所有的按钮都是一个模子。三个状态OnPress,OnRelease,OnHold
废话不多说,直接上代码。下面是Dance VB的控制脚本

  • Dance VB
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using Vuforia;

public class DanceControl : MonoBehaviour, IVirtualButtonEventHandler
{
	//按钮三个执行状态
    public UnityEvent onPress;
    public UnityEvent onHold;
    public UnityEvent onRelease;
	
	//获取VB对象
    public VirtualButtonBehaviour btn;
    //被摁下标识
    bool isPressed;
    Animation animOpenCurtain;
    public void OnButtonPressed(VirtualButtonBehaviour vb)
    {
        //if (onPress != null)
            onPress.Invoke();
        isPressed = true;
		//获取MIku模型,并进行播放
        animOpenCurtain = GameObject.Find("MikuV4X").GetComponent<Animation>();
        animOpenCurtain.Play();


    }

    public void OnButtonReleased(VirtualButtonBehaviour vb)
    {
        //if (onRelease != null)
        onRelease.Invoke();
        isPressed = false;
        //throw new System.NotImplementedException();
    }

    // Start is called before the first frame update
    void Start()
    {
    	//注册该按钮在全局事件监听
        btn = GetComponent<VirtualButtonBehaviour>();
        btn.RegisterEventHandler(this);
        //animator = GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update()
    {
        if (isPressed)
            onHold.Invoke();
    }
}
  • 平移,旋转缩放两个控制脚本【只是类名不同】
    代码里其实没有什么。只是在Unity面板里进行了简单可视化编程
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using Vuforia;

public class TranslateControl : MonoBehaviour, IVirtualButtonEventHandler
{
    public UnityEvent onPress;
    public UnityEvent onHold;
    public UnityEvent onRelease;
    
    public VirtualButtonBehaviour btn;

    bool isPressed;
    public void OnButtonPressed(VirtualButtonBehaviour vb)
    {
        //if (onPress != null)
        onPress.Invoke();
        isPressed = true;
    }

    public void OnButtonReleased(VirtualButtonBehaviour vb)
    {
        //if (onRelease != null)
        onRelease.Invoke();
        isPressed = false;
        //throw new System.NotImplementedException();
    }

    // Start is called before the first frame update
    void Start()
    {
        btn = GetComponent<VirtualButtonBehaviour>();
        btn.RegisterEventHandler(this);
        //animator = GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update()
    {
        if (isPressed)
            onHold.Invoke();
    }
}

unity发布程序没有Linux ARM_sed_07


unity发布程序没有Linux ARM_sed_08

代码终结篇总结

设计思路很简单,在实践的第一篇中有图做详细介绍。简单的来说就是,通过按钮控制模型的动画,模型的平移旋转缩放,还有通过射线法操控模型,更改模型属性等。
这个实践完全是自己盲人摸象一样的趟过来的,如果说反思的话,就是几个方面

  • 信息收集来讲:
    背景信息还不错,脑子里有相当的脉络。
    实操的信息来说,走了很多弯路,挨了很多毒打。尤其是AndroidSDK的变迁;其次就是平移这个最简单的坎儿,因为网上的代码有些缺乏背景信息,从电脑上制作打包APK再测试就很麻烦,在懒惰了很多次后,才选择看官网手册。不得不说,Unity真的很良心。果然高质量信息搜寻,还是优先官网吧。
  • 关于Unity的动画系统:
    因为这个程序走的是MMD插件,所以不太需要去了解Animator和Animation两个动画系统,比较遗憾,但是从VMD插入到MMD程序生成的过程来看,真的有点很复杂的样子,对于我这样不被要求很深入的了解的人来说,MMD真的是帮了很大的忙!不过好像Animator和Animation的控制函数不是很多,还是比较简单的样子,复杂的地方应该是在于制作动作和模型的方面。
  • Unity开发Android程序测试很麻烦浪费了很多时间。希望以后能找到更好的解决方法。
  • VB也测试了很多时间,因为它总是处于被摁下的状态,后来在一个博客里无意间看到,最好扔到中间,试了试,大致解决了这个问题,这也凸显出AR交互的一个十分不太方便的地方。
  • 关于移动端和PC端的区别,只是外界信息源获取的函数不同罢了
    GetTouch or Mouse
  • 做东西一定先写构思图,因为……很可能坐着做着就忘记要干嘛了……hhhhh,其次便是耐心,总会有解决办法,只是你没找到,一步一步去分解问题,一个一个解决,便会离最终目标越来越近。
  • 本项目在Github已开源。如果有优化的想法,且有时间,欢迎你的贡献。