声明:欢迎任何人和组织转载本blog中文章,但必须标记文章原始链接和作者信息。
本文链接:http://blog.csdn.net/li_007/archive/2010/01/01/5118098.aspx
开拓进取的小乌龟------->CSDN点滴点点滴滴Blog
TUIO.as文件详细注释
TUIOObject.as TUIOEvent.as TUIOCursor.as文件详细注释
研究了一段时间的multiTouch,根据自己的理解也对代码做了很多注释,今天整理了一下Touchlib这个库的Actionscript 3版本的源码注释,现在发布出来,希望和大家一起讨论修正。也欢迎大家指出其中的错误或者发表自己的意见。后续的as文件,会在我整理完成后慢慢发布出来。好了直接贴代码(本人很想办一个openframeworks的中文论坛,然后会发布一些ccv和openframeworks的个人心得,希望有这个兴趣的朋友一起来研究研究)
package com.touchlib { import flash.events.DataEvent; import flash.events.Event; import flash.events.IOErrorEvent; import flash.events.ProgressEvent; import flash.events.SecurityErrorEvent; import flash.geom.Point; import flash.net.URLLoader; import flash.net.URLRequest; import flash.net.XMLSocket; //import flash.system.System; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; //import flash.events.MouseEvent; import flash.display.*; import app.core.element.Wrapper; import flash.events.MouseEvent; public class TUIO { static var FLOSCSocket:XMLSocket; static var FLOSCSocketHost:String; static var FLOSCSocketPort:Number; static var thestage:Stage; // 存储了所有可用TUIOObject实例 static var objectArray:Array; // 存储了所有可用TUIOOject对象的ID号,但是在本源文件中并没有用到这个变量 static var idArray:Array; public static var debugMode:Boolean; static var debugText:TextField; static var debugToggle:TextField; static var recordedXML:XML; static var bRecording:Boolean = false; //static var xmlPlaybackURL:String = "www/xml/test.xml"; static var xmlPlaybackURL:String = ""; static var xmlPlaybackLoader:URLLoader; static var playbackXML:XML; static var bInitialized = false; // s : 当然舞台,传递文档类的this即可 // host : 服务器IP地址 // port : 端口,CCV默认为3000 // debugXMLFile : 调试用xml文件路径 // debug : 一个Boolen值,表示当前是否为调试状态(不一定需要调试xml) // // 这个是TUIO AS 3 Library的初始化函数,在flash工程的文档类开始调用就可以。 // public static function init (s:DisplayObjectContainer, host:String, port:Number, debugXMLFile:String, dbug:Boolean = true):void { if(bInitialized) return; debugMode = dbug; FLOSCSocketHost=host; FLOSCSocketPort=port; bInitialized = true; thestage = s.stage; // 获取当然文档类的舞台 thestage.align = StageAlign.TOP_LEFT; thestage.displayState = StageDisplayState.FULL_SCREEN; objectArray = new Array(); idArray = new Array(); try { // 与服务端建立xml socket连接,比如CCV。 // 关于flash的xmlsocket编程大家可以自己去查阅Actionscript 3的文档 FLOSCSocket = new XMLSocket(); FLOSCSocket.addEventListener(Event.CLOSE, closeHandler); FLOSCSocket.addEventListener(Event.CONNECT, connectHandler); FLOSCSocket.addEventListener(DataEvent.DATA, dataHandler); FLOSCSocket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); FLOSCSocket.addEventListener(ProgressEvent.PROGRESS, progressHandler); FLOSCSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); FLOSCSocket.connect(host, port); } catch (e) { } // 如果当前是调试状态,设置一些调试所需元件 if(debugMode) { var format:TextFormat = new TextFormat(); debugText = new TextField(); format.font = "Verdana"; format.color = 0xFFFFFF; format.size = 10; debugText.defaultTextFormat = format; debugText.autoSize = TextFieldAutoSize.LEFT; debugText.background = true; debugText.backgroundColor = 0x000000; debugText.border = true; debugText.borderColor = 0x333333; // 将一个动态文本对象加到显示列表,并且将它放在显示列表的最上层。 // 其实这个debugText对象是实时显示调试状态下,触摸点的ID号,x和y坐标 thestage.addChild( debugText ); thestage.setChildIndex(debugText, thestage.numChildren-1); recordedXML = <OSCPackets></OSCPackets>; // 创建一个Sprite对象,调试作用,稍后介绍 var buttonSprite = new Sprite(); buttonSprite.graphics.beginFill(0xFFFFFF,1.0); buttonSprite.graphics.drawRoundRect(0, -10, 50, 60,10); buttonSprite.addEventListener(MouseEvent.CLICK, toggleDebug); // 创建一个Wrapper对象,Wrapper对象类在app.core.element包中 var WrapperObject:Wrapper = new Wrapper(buttonSprite); thestage.addChild(WrapperObject); thestage.setChildIndex(WrapperObject, thestage.numChildren-1); //trace(thestage.numChildren); // 调试xml文件处理 if(xmlPlaybackURL != "") { xmlPlaybackLoader = new URLLoader(); xmlPlaybackLoader.addEventListener("complete", xmlPlaybackLoaded); xmlPlaybackLoader.load(new URLRequest(xmlPlaybackURL)); thestage.addEventListener(Event.ENTER_FRAME, frameUpdate); } } else { recordedXML = <OSCPackets></OSCPackets>; bRecording = false; } } // 调试xml文件加载完成事件处理函数 private static function xmlPlaybackLoaded(evt:Event) { trace("Loaded xml debug data"); playbackXML = new XML(xmlPlaybackLoader.data); } // private static function frameUpdate(evt:Event) { if(playbackXML && playbackXML.OSCPACKET && playbackXML.OSCPACKET[0]) { processMessage(playbackXML.OSCPACKET[0]); delete playbackXML.OSCPACKET[0]; } } // 从名字就可以看出来函数功能。根据id来查找所有存储在objectArray数组中的TUIOObject实例 public static function getObjectById(id:Number): TUIOObject { for(var i=0; i<objectArray.length; i++) { if(objectArray[i].ID == id) { //trace("found " + id); return objectArray[i]; } } //trace("Notfound"); return null; } // 这个函数要结合TUIOObject类来理解。 public static function listenForObject(id:Number, reciever:Object) { var tmpObj:TUIOObject = getObjectById(id); if(tmpObj) { tmpObj.addListener(reciever); } } // 这个是最重要的数据处理函数 public static function processMessage(msg:XML) { var fseq:String; var node:XML; // 处理fseq消息,格式如下: // // /tuio/[profileName] fseq int32 // profileName 代表定义好的常用可感知用户界面配置 // fseq 消息标示 // int32 一个类型为int32的fseq值 for each(node in msg.MESSAGE) { if(node.ARGUMENT[0] && node.ARGUMENT[0].@VALUE == "fseq") fseq = node.ARGUMENT[1].@VALUE; } /// trace("fseq = " + fseq); // 处理alive消息,格式如下: // // uio/[profileName] alive [list of active sessionIDs] // profileName 代表定义好的常用可感知用户界面配置 // alive 消息标示 // [list of active sessionIDs] 一系列当前有用的目标对象的id号 for each(node in msg.MESSAGE) { if(node.ARGUMENT[0] && node.ARGUMENT[0].@VALUE == "alive") { // 重置objectArray数组中存储的所有TUIOOject实例的isAlive属性 for each (var obj1:TUIOObject in objectArray) { obj1.isAlive = false; } // 在这个地方重新定义了一个array,但是是无用的变量。 var newIdArray:Array = new Array(); // 循环获得alive消息所有id // // alive消息是配合set消息来确定哪些blobs点是有用,也就是哪些set消息中包含的数据是有效的 // 可以结合CCV中的源码来理解 for each(var aliveItem:XML in node.ARGUMENT.(@VALUE != "alive")) { // 根据alive消息中的id,相应的TUIOObject实例的isAlive属性设置为true,也即是当前点可用 if(getObjectById(aliveItem.@VALUE)) getObjectById(aliveItem.@VALUE).isAlive = true; } //trace(idArray); idArray = newIdArray; } } // 处理set消息 for each(node in msg.MESSAGE) { if(node.ARGUMENT[0]) { var type:String; // 在这里要注意区别2Dobj和2Dcur这两种profile的区别,结构如下,具体请阅读TUIO Protocol Specification // // /tuio/2Dobj set s i x y a X Y A m r // /tuio/2Dcur set s x y X Y m // // 由于本人结合研究的CCV发送的是/tuio/2Dcur消息结构,所以我先注释/tuio/2Dcur结构消息处理 // 对于/tuio/2Dobj消息处理的注释在以后补上。 if(node.@NAME == "/tuio/2Dobj") { type = node.ARGUMENT[0].@VALUE; if(type == "set") { var sID = node.ARGUMENT[1].@VALUE; var id = node.ARGUMENT[2].@VALUE; var x = Number(node.ARGUMENT[3].@VALUE) * thestage.stageWidth; var y = Number(node.ARGUMENT[4].@VALUE) * thestage.stageHeight; var a = Number(node.ARGUMENT[5].@VALUE); var X = Number(node.ARGUMENT[6].@VALUE); var Y = Number(node.ARGUMENT[7].@VALUE); var A = Number(node.ARGUMENT[8].@VALUE); var m = node.ARGUMENT[9].@VALUE; var r = node.ARGUMENT[10].@VALUE; // send object update event.. var objArray:Array = thestage.getObjectsUnderPoint(new Point(x, y)); var stagePoint:Point = new Point(x,y); var displayObjArray:Array = thestage.getObjectsUnderPoint(stagePoint); var dobj = null; // if(displayObjArray.length > 0) // dobj = displayObjArray[displayObjArray.length-1]; var tuioobj = getObjectById(id); if(tuioobj == null) { tuioobj = new TUIOObject("2Dobj", id, x, y, X, Y, sID, a, dobj); thestage.addChild(tuioobj.spr); objectArray.push(tuioobj); tuioobj.notifyCreated(); } else { tuioobj.spr.x = x; tuioobj.spr.y = y; tuioobj.x = x; tuioobj.y = y; tuioobj.dX = X; tuioobj.dY = Y; tuioobj.setObjOver(dobj); tuioobj.notifyMoved(); } try { if(tuioobj.obj && tuioobj.obj.parent) { var localPoint:Point = tuioobj.obj.parent.globalToLocal(stagePoint); tuioobj.obj.dispatchEvent(new TUIOEvent(TUIOEvent.TUIO_MOVE, true, false, x, y, localPoint.x, localPoint.y, tuioobj.oldX, tuioobj.oldY, tuioobj.obj, false,false,false, true, m, "2Dobj", id, sID, a)); } } catch (e) { } } } // // /tuio/2Dcur set s x y X Y m // else if(node.@NAME == "/tuio/2Dcur") { // trace("2dcur"); type = node.ARGUMENT[0].@VALUE; // 判断消息类型是否为set if(type == "set") { // 当前点的id var id = node.ARGUMENT[1].@VALUE; // 将x,y分别乘以舞台宽度和高度转化为当前点全局舞台坐标中的水平和垂直坐标 // 注意在这里为什么是分别对应乘以舞台宽度和高度,需要认真理解 var x = Number(node.ARGUMENT[2].@VALUE) * thestage.stageWidth; var y = Number(node.ARGUMENT[3].@VALUE) * thestage.stageHeight; // 分别是当前点的位移量 var X = Number(node.ARGUMENT[4].@VALUE); var Y = Number(node.ARGUMENT[5].@VALUE); // 这个值根据TUIO Protocol Specification的解释和CCV中源码实现,我的理解产生了矛盾。 // 需要我后续的实验验证。故再次不做注释说明,也希望那位与我共同讨论([email protected]) var m = node.ARGUMENT[6].@VALUE; //var area = node.ARGUMENT[7].@VALUE; var stagePoint:Point = new Point(x, y); // 下面代码段是一个理解重点 // 根据当前点坐标得到舞台上该点下面所有显示对象。关于getObjectsUnderPoint函数 // 可以查阅Actionscript 3的Docs,它返回的包含所有对象的一个数组 var displayObjArray:Array = thestage.getObjectsUnderPoint(stagePoint); var dobj = null; // 根据返回的数组,得到最上层的显示对象 // // 为什么要得到这个最上层的显示对象呢?怎么实现触摸响应的呢? // 比如:舞台上有一个MovieClip,在这个MC上有一个Button。 // 当你的手触摸到这个button的时候,我们得到了当前点坐标(肯定是stageX和stageY),这样我们 // 用上面的方法获得这个点下面所有的显示对象,包括button,MovieClip等。这个时候我们就要获取 // 最上层这个button,然后让这个button发送一个TUIOEvent.TUIO_DOWN等这样的事件。然后我们只需 // 要在我们的flash中的button上监听这个TUIO_DOWN事件就可以了。 // // 理解了么?这就是由屏幕上一个点,到flash舞台上元素响应事件的原理。好好学习下Actionscript 3 // 的事件机制后理解这些吧。因为这个让我也想起了Box2D这个库中实现鼠标事件的原理。 if(displayObjArray.length > 0) dobj = displayObjArray[displayObjArray.length-1]; var sztmp:String=""; // for(var i=0; i<displayObjArray.length; i++) // sztmp += (displayObjArray[i] is InteractiveObject) + ","; // trace(sztmp); // 根据当前id号在objectArray数组中查找是否存在对于的TUIOOjbect实例 var tuioobj = getObjectById(id); if(tuioobj == null) { // 不存在,则创建响应TUIOObject对象,并加入到舞台和数组中(个人觉得没必要加载到舞台, // 况且为了加载还重新定义了一个Sprite,真是有点多此一举) // // 注意这里创建TUIOObject实例所传递的参数中最后一个参数。当然这也要结合TUIOObject类 // 来理解。具体的将会在TUIOObject类中注释。 tuioobj = new TUIOObject("2Dcur", id, x, y, X, Y, -1, 0, dobj); //tuioobj.area = area; thestage.addChild(tuioobj.spr); objectArray.push(tuioobj); // 发送TUIO_OVER和TUIO_DOWN消息(请结合TUIOObject代码来理解) tuioobj.notifyCreated(); } else { // 存在,则更新当前点的信息 tuioobj.spr.x = x; tuioobj.spr.y = y; tuioobj.x = x; tuioobj.y = y; //tuioobj.area = area; tuioobj.dX = X; tuioobj.dY = Y; tuioobj.setObjOver(dobj); tuioobj.notifyMoved(); } try { // 发送TUIO_MOVE消息 if(tuioobj.obj && tuioobj.obj.parent) { var localPoint:Point = tuioobj.obj.parent.globalToLocal(stagePoint); tuioobj.obj.dispatchEvent(new TUIOEvent(TUIOEvent.TUIO_MOVE, true, false, x, y, localPoint.x, localPoint.y, tuioobj.oldX, tuioobj.oldY, tuioobj.obj, false,false,false, true, m, "2Dobj", id, sID, a)); } } catch (e) { trace("Dispatch event failed " + tuioobj.name); } } } } } // 设置调试显示文本 if(debugMode) { debugText.text = ""; debugText.y = -2000; debugText.x = -2000; } for (var i=0; i<objectArray.length; i++ ) { // 如果当前点非alive,则总objectArray数组中去掉,并且清楚当前点。 if(objectArray[i].isAlive == false) { objectArray[i].kill(); thestage.removeChild(objectArray[i].spr); objectArray.splice(i, 1); i--; } else { // 调试状态下显示当前点的坐标(stageX和stageY) if(debugMode) { debugText.appendText(" " + (i + 1) +" - " +objectArray[i].ID + " X:" + int(objectArray[i].x) + " Y:" + int(objectArray[i].y) + " /n"); debugText.x = thestage.stageWidth-160; debugText.y = 40; } } } } private static function toggleDebug(e:Event) { if(!debugMode){ debugMode=true; FLOSCSocket.connect(FLOSCSocketHost, FLOSCSocketPort); e.target.x=20; } else{ debugMode=false; FLOSCSocket.connect(FLOSCSocketHost, FLOSCSocketPort); e.target.x=0; } // show XML //bRecording = false; //debugMode = false; //debugText.text = recordedXML.toString(); //debugText.x = 0; //debugText.y = 0; } private static function closeHandler(event:Event):void { //trace("closeHandler: " + event); } private static function connectHandler(event:Event):void { // trace("connectHandler: " + event); } private static function dataHandler(event:DataEvent):void { //trace("dataHandler: " + event); if(bRecording) recordedXML.appendChild( XML(event.data) ); processMessage(XML(event.data)); } private static function ioErrorHandler(event:IOErrorEvent):void { // thestage.tfDebug.appendText("ioError: " + event + "/n"); trace("ioErrorHandler: " + event); } private static function progressHandler(event:ProgressEvent):void { //trace("progressHandler loaded:" + event.bytesLoaded + " total: " + event.bytesTotal); } private static function securityErrorHandler(event:SecurityErrorEvent):void { trace("securityErrorHandler: " + event); // thestage.tfDebug.appendText("securityError: " + event + "/n"); } } }
ps:说实话,这个代码真的不规范,看的有点让人恼火。