TUIO是一个用途广泛,支持厂商众多的多点触摸协议。 其编码采用XML抽象描述,所以任何支持XML的语言都可以使用TUIO协议的触摸设备。另外,TUIO不受windows触摸点上限限制,即设备支持多少个点,你就能使用多少个。
这个类描述了一个TUIO触摸点,每次出现新的触摸点时我们实例化一个与其相对应的触摸点。由于传输过来的数据并没有告诉我们某个触摸点是刚出现的,我们必须从现有的触摸点集合中查找ID,如果没有找到则判断为新的触摸事件。
而由于从tuio里发送过来的事件都是以TUIO自己习惯进行编号的,为了防止与本地的触摸冲突,我们需要给每个触摸点一个本地的ID,本地的触摸编号从1开始到255结束,我们就从256开始,上限随你设定,尽管你设置的上限足够大,但是必须为其准备一个超出上限了就返回-1的默认值
最后 关键的处理TUIO的xml消息的函数
现在最新的TUIO协议版本是2.0 , 但是仍然没有普及,现有设备还是以1.0为默认支持。
希望在设备中使用TUIO,必须要与TUIO服务建立一个连接,TUIO协议的默认端口为 3000,这里跳过连接直接进入主题。
这是一个TUIO协议的一个数据包,其包含至少3个名为MESSAGE子项,每个子项的第一个子项的value属性定义了此子项的数据类型,
以此例为示,它有3个子项,第1个子项(alive)代表着当前存在的触摸点,这里是指所有在设备上的触摸点,包括那些正在移动的和静止的。第2个子项(set)对应了一个触摸点的详细数据,第3个子项(fseq)定义了包的序列ID。
- <OSCPACKET ADDRESS="127.0.0.1" PORT="55448" TIME="-3736546999771946328">
- <MESSAGE NAME="/tuio/2Dcur">
- <ARGUMENT TYPE="s" VALUE="alive"/>
- <ARGUMENT TYPE="i" VALUE="4"/>
- </MESSAGE>
- <MESSAGE NAME="/tuio/2Dcur">
- <ARGUMENT TYPE="s" VALUE="set"/>
- <ARGUMENT TYPE="i" VALUE="4"/>
- <ARGUMENT TYPE="f" VALUE="0.014778325123152709"/>
- <ARGUMENT TYPE="f" VALUE="0.03414634146341464"/>
- <ARGUMENT TYPE="f" VALUE="0"/>
- <ARGUMENT TYPE="f" VALUE="0"/>
- <ARGUMENT TYPE="f" VALUE="0"/>
- </MESSAGE>
- <MESSAGE NAME="/tuio/2Dcur">
- <ARGUMENT TYPE="s" VALUE="fseq"/>
- <ARGUMENT TYPE="i" VALUE="3"/>
- </MESSAGE>
- </OSCPACKET>
<OSCPACKET ADDRESS="127.0.0.1" PORT="55448" TIME="-3736546999771946328">
<MESSAGE NAME="/tuio/2Dcur">
<ARGUMENT TYPE="s" VALUE="alive"/>
<ARGUMENT TYPE="i" VALUE="4"/>
</MESSAGE>
<MESSAGE NAME="/tuio/2Dcur">
<ARGUMENT TYPE="s" VALUE="set"/>
<ARGUMENT TYPE="i" VALUE="4"/>
<ARGUMENT TYPE="f" VALUE="0.014778325123152709"/>
<ARGUMENT TYPE="f" VALUE="0.03414634146341464"/>
<ARGUMENT TYPE="f" VALUE="0"/>
<ARGUMENT TYPE="f" VALUE="0"/>
<ARGUMENT TYPE="f" VALUE="0"/>
</MESSAGE>
<MESSAGE NAME="/tuio/2Dcur">
<ARGUMENT TYPE="s" VALUE="fseq"/>
<ARGUMENT TYPE="i" VALUE="3"/>
</MESSAGE>
</OSCPACKET>
事实上,我们只需要使用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;
- }
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;
}
这个类描述了一个TUIO触摸点,每次出现新的触摸点时我们实例化一个与其相对应的触摸点。由于传输过来的数据并没有告诉我们某个触摸点是刚出现的,我们必须从现有的触摸点集合中查找ID,如果没有找到则判断为新的触摸事件。
- //根据ID查找TOUCH
- private function searchByRemoteName(remoteID:int):TuioObject{
- for each(var touch:TuioObject in _touchList){
- if(touch.remoteID == remoteID){
- return touch;
- }
- }
- return null;
- }
//根据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 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;
}
}
而由于从tuio里发送过来的事件都是以TUIO自己习惯进行编号的,为了防止与本地的触摸冲突,我们需要给每个触摸点一个本地的ID,本地的触摸编号从1开始到255结束,我们就从256开始,上限随你设定,尽管你设置的上限足够大,但是必须为其准备一个超出上限了就返回-1的默认值
- 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 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));
- }
- bsp; }
//应用新的触摸点
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));
}
}
最后 关键的处理TUIO的xml消息的函数
- //解析消息
- 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);
- }
- }
- }