Unity使用TUIO协议接入雷达(大屏互动方案)
前言:我们经常可以在一些商场或者科技馆之类的看到一些大屏互动的应用,比如大屏照片墙,会根据人的走动而产生一些交互效果,或者是地板的LED水波纹,踩上去会产生交互等等,下面就是一些实际项目的现场效果图:
本文就是提供一种方案思路,让Unity开发者也能开发这些大屏互动应用。主要是有一次我去科技馆,看到了那个大屏互动,启动应用的logo,就是Unity...
前期准备
[*]Unity(这里版本用的2018,当然也可以尝试新版本,理论上是兼容的)
[*]Visual Studio(可以安装最新版,主要用于代码开发)
[*]一个雷达(当然,调试的时候也可以没有),其实雷达设备也不便宜,比Kinect方案贵不少。
[*]TouchScripts插件, 这个插件在Unity的Asset Store上是免费的(github上开源),可以直接搜索下载。
建议Store上(或者Github)下载最新的(当然,看github也有几年停更了),从他的介绍里面可以看到他是支持TUIO协议的。
[*]一个工具,这是一个TUIO的模拟工具,可以用来模拟雷达的消息。
关注VX公众号:优三帝,回复消息:大屏互动,获取下载链接。
开始工作
导入插件
我们下载下来之后全部导入
注意,这里要点Go Ahead,不要点No Thanks,表示升级下API以兼容当前版本的Unity。
导完了之后会有一个弹出框,注意勾选Enable TUIO,这个是重点(圈起来)!!
导入后可以去了解下插件的Examples。
启用TUIO
在导入插件的弹框里,我们已经将Enable TUIO给安排上了,说明已经支持TUIO了,接下来是使用
我们直接使用它里面的一个demo来做介绍,比如说就这个
打开之后他是一个类似球球大作战的小游戏,接下来找到这么一个脚本
这个脚本处理TUIO的输入,需要把它添加到场景中,但是当你拖到一个GameObject中发现拖不上去,打开脚本发现他被sealed关键字修饰了,简单粗暴的方法当然是直接删掉
删了 sealed关键字:
好了,现在可以拖了
效果:
总结与建议
1、不建议使用Drag,上面使用的模拟器当然不会有什么问题,问题是部分雷达可能会出现掉帧的情况,这就导致在拖动过程中物体随时可能会掉。这个你无法避免,只能避免使用Drag这种交互
2、注意产生事件的时间,我们经常用OnButtonDown对应TouchScripts里面的Press事件来检测用户的点击,这种对鼠标用户当然没有问题,问题是雷达在安装的时候不可能像鼠标那样映射的那么准确,以及雷达扫射面与互动面的距离、用户点击姿势不规范等等问题容易导致看起来是点中了一个Button,然而实际上并没有,别人就会说你这软件点不动,有问题啊。然而你在测试的时候一点一个准,没问题。然后两人就开始撕逼了。
所以在此先建议尽量把触发区域弄大一点。
3、关于地面互动
地面互动的话建议就不要用事件了。因为人一走进互动区域就一直是Pressed状态,即便踩到一个button,button的Press,Tap事件都不会被触发,因为他的上一个状态还没有被释放。而且你不能期望体验者把脚抬高来释放。所以死穴。我的建议是回归原始,使用射线。具体方法是隔一定时间遍历所以Pressed状态的“指针”(即按下的鼠标以及雷达扫描到的物体,找不到一个合适的词来表达,就用插件提供的“Pointer”吧,然后在指针的位置向世界发送一条射线,看碰撞物。
最后附上我之前写地面互动的一个脚本
using UnityEngine;
using System.Collections;
using TouchScript;
using System.Collections.Generic;
/// <summary>
/// 这是一个土得掉渣的步步生花
/// </summary>
public class FlowerSpawner : MonoBehaviour
{
/// <summary>
/// 花的预制体
/// </summary>
public GameObject FlowerPerfab;
/// <summary>
/// 软件扫描的频率
/// </summary>
public float Rate = 30;
private float time;
/// <summary>
/// 用来发射射线的摄像机
/// </summary>
public Camera EventCamera;
public void Start()
{
time = 1f / Rate;
StartCoroutine(Spawner());
}
private IEnumerator Spawner()
{
while (true)
{
///时间间隔小于Time.deltaTime没有意义
if(time < Time.deltaTime)
time = Time.deltaTime;
Check();
yield return new WaitForSeconds(time);
}
}
private void Check()
{
///遍历所有按下状态的指针
IList<TouchScript.Pointers.Pointer> PressedPointers = TouchManager.Instance.PressedPointers;
foreach(TouchScript.Pointers.Pointer pointer in PressedPointers)
{
///向每个指针发送射线
ToRay(pointer.Position);
}
}
/// <summary>
/// 根据点击屏幕的位置发送射线
/// </summary>
/// <param name=&#34;position&#34;></param>
private void ToRay(Vector2 position)
{
Ray ray = EventCamera.ScreenPointToRay(position);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
///不要重复生成
///
///这里可以做一下比较,看一下点中(踩中)的是什么,看你的逻辑
//hit.collider.tag == &#34;&#34;
}
else
{
Vector3 spawnerPos = EventCamera.ScreenToWorldPoint(new Vector3(position.x, position.y, 50));
GameObject go = Instantiate(FlowerPerfab);//这里为了方便演示直接实例化了,建议用池子
go.transform.position = spawnerPos;
}
}
}
页:
[1]