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官网查询这个函数了。
欸!官网给出了实例,超级棒!
好了具体的实践和分析过程我就不列举出来,直接给成功的代码和注释。【伸手党最爱】
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
核心语句: 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;
}
}
三.操作代码解析
- 平移的话,没有太多要点,因为太简单了。主要就是先判断是否触摸,然后再读取一共有多少个触摸点,然后再进行循环,以保证所有的触摸点的移动都被读取,而不是只读第一个//Touch touch = Input.GetTouch(0);所以要进入for循环。Input.GetTouch()可以在Unity官网手册查询具体的,而且Unity的手册都带有实例,而且对于新手来说有可能超级有用,很基础很实用的例子。如果你找不到老师,请去官网翻手册,这是最高效的信息来源。
- 关于旋转和缩放,旋转核心语句是transform.Rotate(),需要确定旋转方向,即是Vector3.down及Vector3.right两个参量,这个两个参量是可以更改的,看你自己的需求,需要注意的是旋转是单点操作和平移是冲突的。缩放的话核心语句是transform.localScale属性的赋值,思路是先确定两触摸点距离变化是增大还是减小,然后获取模型原各轴缩放尺寸,将增大/减少值更新至原缩放尺寸,然后再赋值回到模型各轴缩放尺寸属性。
- 我个人将模型的旋转平移写在一个脚本里,将平移单独写在一个脚本里,通过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"变量来调用。
自发光组件
模型自发光资源:
HighlightingSystem插件学习文档 博客 魔卡先生
食用方法
- Import HighlightingSystem.unitypackage。
在MUKIARProgram\MyTestAR_SourceCode\Assets\ImportPackage路径下
//插件导入时如若报错,将错误行代码注释掉即可 - 在ARCamera上挂载 Highlighting Effect 脚本,用于渲染外发光。不需要自己写搜索脚本名即可。
- 在需要外发光效果的物体上添加 Highlightable Object 脚本,用于外发光效果显示。
- 在需要外发光效果的物体上添加控制脚本,或者是在其他GameObject的脚本中获取该物体的HighlightableObject ho对象。我是在ARCamera中的射线法脚本中通过Public HighlightableObject直接拖拽Miku对象获取Miku的ho对象,从而控制Miku的自发光。
- 核心控制语句:
//循环往复外发光开启(参数为:颜色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用途:显示文字
关于按钮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();
}
}
代码终结篇总结
设计思路很简单,在实践的第一篇中有图做详细介绍。简单的来说就是,通过按钮控制模型的动画,模型的平移旋转缩放,还有通过射线法操控模型,更改模型属性等。
这个实践完全是自己盲人摸象一样的趟过来的,如果说反思的话,就是几个方面
- 信息收集来讲:
背景信息还不错,脑子里有相当的脉络。
实操的信息来说,走了很多弯路,挨了很多毒打。尤其是AndroidSDK的变迁;其次就是平移这个最简单的坎儿,因为网上的代码有些缺乏背景信息,从电脑上制作打包APK再测试就很麻烦,在懒惰了很多次后,才选择看官网手册。不得不说,Unity真的很良心。果然高质量信息搜寻,还是优先官网吧。 - 关于Unity的动画系统:
因为这个程序走的是MMD插件,所以不太需要去了解Animator和Animation两个动画系统,比较遗憾,但是从VMD插入到MMD程序生成的过程来看,真的有点很复杂的样子,对于我这样不被要求很深入的了解的人来说,MMD真的是帮了很大的忙!不过好像Animator和Animation的控制函数不是很多,还是比较简单的样子,复杂的地方应该是在于制作动作和模型的方面。 - Unity开发Android程序测试很麻烦浪费了很多时间。希望以后能找到更好的解决方法。
- VB也测试了很多时间,因为它总是处于被摁下的状态,后来在一个博客里无意间看到,最好扔到中间,试了试,大致解决了这个问题,这也凸显出AR交互的一个十分不太方便的地方。
- 关于移动端和PC端的区别,只是外界信息源获取的函数不同罢了
GetTouch or Mouse - 做东西一定先写构思图,因为……很可能坐着做着就忘记要干嘛了……hhhhh,其次便是耐心,总会有解决办法,只是你没找到,一步一步去分解问题,一个一个解决,便会离最终目标越来越近。
- 本项目在Github已开源。如果有优化的想法,且有时间,欢迎你的贡献。