TUIO是一个用途广泛,支持厂商众多的多点触摸协议。 其编码采用XML抽象描述,所以任何支持XML的语言都可以使用TUIO协议的触摸设备。另外,TUIO不受windows触摸点上限限制,即设备支持多少个点,你就能使用多少个。
现在最新的TUIO协议版本是2.0 , 但是仍然没有普及,现有设备还是以1.0为默认支持。
希望在设备中使用TUIO,必须要与TUIO服务建立一个连接,TUIO协议的默认端口为 3000,这里跳过连接直接进入主题。
这是一个TUIO协议的一个数据包,其包含至少3个名为MESSAGE子项,每个子项的第一个子项的value属性定义了此子项的数据类型,
以此例为示,它有3个子项,第1个子项(alive)代表着当前存在的触摸点,这里是指所有在设备上的触摸点,包括那些正在移动的和静止的。第2个子项(set)对应了一个触摸点的详细数据,第3个子项(fseq)定义了包的序列ID。
事实上,我们只需要使用alive和set消息,其他的消息都不在我们的考虑之内,
set消息传过来的参数只是TUIO设备上的坐标比例,从0-1,将其与本地舞台大小相乘才能得到舞台坐标。
alive消息描述了当前存在的点,根据这个参数我们可以排除那些已经消失的点。首先,我们先创建一个触摸点类
import flash.display.InteractiveObject;
public class TuioObject
{
/** 触摸id */
public var id:int;
/** 远程触摸编号 */
public var remoteID:int;
/** 指针对象 */
public var target:InteractiveObject;
/** 屏幕上的位置 */
public var x:int;
/** 屏幕上的位置 */
public var y:int;
/** 生效时候的位置 */
public var startX:Number;
/** 生效时候的位置 */
public var startY:Number;
/** 是否存在 */
public var isExist:Boolean;
public function TuioObject(touchID:int,x:Number,y:Number)
{
id = touchID;
this.x = startX = x;
this.y = startY = y;
isExist = true;
}
//根据ID查找TOUCH
private function searchByRemoteName(remoteID:int):TuioObject{
for each(var touch:TuioObject in _touchList){
if(touch.remoteID == remoteID){
return touch;
}
}
return null;
}
当有一个新点出现时,我们必须为它指定一个指向显示对象,以便从其向舞台冒泡事件,如果从这个点没有找到显示对象,则直接对舞台抛事件。注意,只有InteractiveObject才能抛出触摸事件,而Bitmap或Shape则不能接受鼠标事件
//搜索舞台元素
private function findDisplayObject(x:Number,y:Number):InteractiveObject{
var obj:DisplayObject;
var objArray:Array = _stage.getObjectsUnderPoint(new Point(x,y));
if(objArray.length>0){
obj=objArray[objArray.length-1];
if(!(obj is InteractiveObject)){
obj = obj.parent;
}
return obj;
}else{
return _stage;
}
}
private function getFreeID():int{
for(var i:int=256;i<1024;i++){
var used:Boolean=false;
for each(var temp:TuioObject in _touchList){
used = i == temp.id;
}
if(!used)return i;
}
return -1;
}
为了让程序更清晰一点,我们将 触摸点生效、触摸点移动、触摸点消失 三个状态的处理函数分开来写。
//应用新的触摸点
private function addTouch(touchPoint:TuioObject):void{
touchPoint.target = findDisplayObject(touchPoint.x,touchPoint.y);
if(touchPoint.target == _stage){
_stage.dispatchEvent(new TouchEvent(TouchEvent.TOUCH_BEGIN,
true,false,touchPoint.id,false,touchPoint.x,touchPoint.y));
}else{
var local:Point = touchPoint.target.globalToLocal(new Point(touchPoint.x,touchPoint.y));
touchPoint.target.dispatchEvent(new TouchEvent(TouchEvent.TOUCH_BEGIN,
true,false,touchPoint.id,false,local.x,local.y));
}
}
//触摸点移动
private function moveTouch(touchPoint:TuioObject):void{
var oldTarget:InteractiveObject = touchPoint.target;
touchPoint.target = findDisplayObject(touchPoint.x,touchPoint.y);
var local:Point = touchPoint.target.globalToLocal(new Point(touchPoint.x,touchPoint.y));
if(touchPoint.target == oldTarget){
oldTarget.dispatchEvent(new TouchEvent(TouchEvent.TOUCH_MOVE,
true,false,touchPoint.id,false,local.x,local.y));
}else{
var old:Point = oldTarget.globalToLocal(new Point(touchPoint.x,touchPoint.y));
oldTarget.dispatchEvent(new TouchEvent(TouchEvent.TOUCH_OUT,
true,false,touchPoint.id,false,old.x,old.y,NaN,NaN,NaN,touchPoint.target));
touchPoint.target.dispatchEvent(new TouchEvent(TouchEvent.TOUCH_OVER,
true,false,touchPoint.id,false,local.x,local.y,NaN,NaN,NaN,touchPoint.target));
touchPoint.target.dispatchEvent(new TouchEvent(TouchEvent.TOUCH_MOVE,
true,false,touchPoint.id,false,local.x,local.y));
}
}
//触摸点消失
private function removeTouch(touchPoint:TuioObject):void{
var oldTarget:InteractiveObject = touchPoint.target;
touchPoint.target = findDisplayObject(touchPoint.x,touchPoint.y);
var local:Point = touchPoint.target.globalToLocal(new Point(touchPoint.x,touchPoint.y));
if(touchPoint.target == oldTarget){
oldTarget.dispatchEvent(new TouchEvent(TouchEvent.TOUCH_END,
true,false,touchPoint.id,false,local.x,local.y));
}else{
var old:Point = oldTarget.globalToLocal(new Point(touchPoint.x,touchPoint.y));
oldTarget.dispatchEvent(new TouchEvent(TouchEvent.TOUCH_OUT,
true,false,touchPoint.id,false,old.x,old.y,NaN,NaN,NaN,touchPoint.target));
touchPoint.target.dispatchEvent(new TouchEvent(TouchEvent.TOUCH_OVER,
true,false,touchPoint.id,false,local.x,local.y,NaN,NaN,NaN,touchPoint.target));
touchPoint.target.dispatchEvent(new TouchEvent(TouchEvent.TOUCH_END,
true,false,touchPoint.id,false,local.x,local.y));
}
}
//解析消息
private function processMessage(msg:XML):void{
//点是否存在 不存在则从列表中移除
var node:XML;
var touch:TuioObject;
for each(node in msg.MESSAGE)
{
if(node.ARGUMENT[0].@VALUE == "alive")
{
for each (touch in _touchList)touch.isExist=false;
for each(var aliveItem:XML in node.ARGUMENT.(@VALUE != "alive"))
{
touch = searchByRemoteName(int(aliveItem.@VALUE));
if(touch){
touch.isExist=true;
}
}
}
}
//处理事件
var remoteID:int;
var x:Number;
var y:Number;
var touchObj:TuioObject;
for each(node in msg.MESSAGE)
{
if(node.ARGUMENT[0] && node.@NAME == "/tuio/2Dcur")
{
if(node.ARGUMENT[0].@VALUE == "set")
{
remoteID = int(node.ARGUMENT[1].@VALUE);
x = Number(node.ARGUMENT[2].@VALUE) * _stageWidth;
y = Number(node.ARGUMENT[3].@VALUE) * _stageHeight;
touchObj=searchByRemoteName(remoteID);
if(!touchObj){
var id:int = getFreeID();
if(id == -1)return;
touchObj = new TuioObject(id,x,y);
touchObj.remoteID = remoteID;
_touchList.push(touchObj);
addTouch(touchObj);
}else{
touchObj.x = x ;touchObj.y = y;
moveTouch(touchObj);
}
}
}
}
//处理不存在的点
for(var i:int=_touchList.length-1;i>=0;i--){
if(!_touchList[i].isExist){
removeTouch(_touchList[i]);
_touchList.splice(i,1);
}
}
}