许久不曾写随笔,即使许久的怠惰,是该抬抬头,看看天了。
公司项目,项目要求是在winForm端先获取下位机的肌电信号采集数据,然后根据这些数据的变化来控制flash游戏,这样一些患者在flash游戏中达到肢体训练的效果。
在这里 需要用到c#与flash的通信, 先上通信示例
ActionScript3.0 与ActionScript2.0 略有差异,但和C#的通信方式是不变的。
function xmlToArray(list:XML):Array { //xml转数组 list.ignoreWhite = true; var ar:Array = new Array(); var list_node:XMLNode = list.firstChild.childNodes[0]; var l:Number = list_node.childNodes.length; for (var i:Number = 0; i<l; i++) { var node:XMLNode = list_node.childNodes[i]; var ob = new Array(); ob.push(node.childNodes[0].childNodes[0].nodeValue); ob.push(node.childNodes[1].childNodes[0].nodeValue); ar.push(ob); } return ar; } ExternalInterface.addCallback("onPlatformMessage",null,onPlatformMessage); var xmlContent:XML; var messageName:String, messageType:String, gameId:String; function onPlatformMessage(args:String) { xmlContent = new XML(args); list_arr = xmlToArray(xmlContent); //fscommand('Message', '<Message name="GAME_SCORE_REPLY" type="2" gameId="4025"><PlayerScore>'+list_arr+'</PlayerScore></Message>'); messageName = xmlContent.firstChild.attributes.name; messageType = xmlContent.firstChild.attributes.type; gameId = xmlContent.firstChild.attributes.gameId; if (messageType == '1' && gameId == '4025') { switch (messageName) { case 'GAME_VERSION' : fscommand('Message', '<Message name="GAME_VERSION_REPLY" type="2" gameId="4025"><GameVersion>1.0</GameVersion></Message>'); break; case 'PLAYER_ENTER' : if (gameFinished) { playerMode = xmlContent.childNodes[0].childNodes[0].childNodes[0].nodeValue; if (playerMode != 3) { //playerMode=xmlContent.PlayerMode; playerName = xmlContent.childNodes[0].childNodes[1].childNodes[0].childNodes[0].nodeValue; level = xmlContent.childNodes[0].childNodes[2].childNodes[0].nodeValue; score = xmlContent.childNodes[0].childNodes[1].childNodes[1].childNodes[0].nodeValue; if(level>25)(level=1); gotoAndStop(3); fscommand('Message', '<Message name="GAME_ENTER_OK" type="2" gameId="4025"></Message>'); } } break; case 'GAME_PAUSE' : if (!gameFinished) { pauseGame(true); } break; case 'GAME_RESUME' : if (!gameFinished) { continueGame(); } break; case 'GAME_LEAVE' : gameFinished = true; fscommand('Message', '<Message name="GAME_LEAVE_OK" type="2" gameId="4025"><PlayerScore>'+score+'</PlayerScore><GameStage>'+level+'</GameStage></Message>'); break; case 'GAME_HELP' : break; case 'GAME_SOUND' : break; case 'GET_GAME_SCORE' : fscommand('Message', '<Message name="GAME_SCORE_REPLY" type="2" gameId="4025"><PlayerScore>'+score+'</PlayerScore></Message>'); break; case 'GET_GAME_STAGE' : fscommand('Message', '<Message name="GAME_STAGE_REPLY" type="2" gameId="4025"><GameStage>'+level+'</GameStage></Message>'); break; case 'SET_GAME_STAGE' : if (!gameFinished) { level = xmlContent.childNodes[0].childNodes[0].childNodes[0].nodeValue; gotoAndPlay(4); fscommand('Message', '<Message name="SET_GAME_STAGE_OK" type="2" gameId="4025"></Message>'); } break; case 'GET_GAME_STATUS' : //tmpInt = xmlContent.firstChild.attributes.GameStage; fscommand('Message', '<Message name="GAME_STATUS_REPLY" type="2" gameId="4025"><GameStatus>'+gameStatus+'</GameStatus></Message>'); break; case 'SET_LANGUAGE' : var language = xmlContent.firstChild.attributes.GameLanguage; break; default : searchHighscore(); } } //end if } //end onPlatformMessage
如上示例:
Flash接收c#消息方式:
ExternalInterface.addCallback("onPlatformMessage",null,onPlatformMessage);
引号中的onPlatformMessage是flash用于接收c#消息的方法名
后面的onPlatformMessage 是接收到的c#传递的参数,该参数可以有一个或多个(本项目中只用到一个参数)。
然后再onPlatformMessage方法中,AS(ActionScript)会先把c#传递过来的额XML的字符串进行解析,解析之后就可以执行flash的相应动作。
Flash回复c#消息
fscommand('Message', '<Message name="SET_GAME_STAGE_OK" type="2" gameId="4025"></Message>');
C#端的操作
C#端使用的是WinForm形式。
准备工作:
首先添加对 COM 组件 Shockwave Flash Object 的引用,将该组件拖到窗体上之后,可以设置如下关键属性:
Movie: Flash 的存放地址
EmbedMovie: 是否嵌入到程序的资源中。
或者在程序中把flash加载进去:
player.LoadMovie(0, Application.StartupPath + "\\EITest.swf");
// player.Play();
在使用player.Play()中遇到的问题是:
在c#与ActionScript2.0 通信中在Load使用使用Play() 命令可以使用flash正常播放,但在c#与ActionScript3.0 的通信中却遇到了错误。后来查找原因大概是在执行 player.LoadMovie() 的时候是把flash加载进来,player.Play() 会自动播放flash,相当于在ActionScript语法中执行了play方法,这样会与flash中原来的方法造成冲突,最后是没有使用后一步的player.Play()方法。
C# 接收flash的消息:
private void player_FSCommand(object sender, AxShockwaveFlashObjects._IShockwaveFlashEvents_FSCommandEvent e) { if (e.command == "Message") { lblreceive.Text = e.args; //游戏载入时,发送参与人的编号和姓名 if (e.args.IndexOf("GAME_LOAD_OK") != -1) { messageName = "PLAYER_LOAD"; LoadPlayer(); } } }
上面的player是与flash通信的客户端的ID,在winForm中注册player的FSCommand的方法,此方法的作用即时在主线程中不停地刷新来接收flash发送给c#的消息。
其中e.command 是flash中 fscommand函数的第一个参数
e.args是flash中 fscommand函数的第二个参数
c#发送给flash的消息
private void Command_SetSpeed(int speed) { messageName = "Action_Speed"; player.CallFunction("<invoke name=\"onPlatformMessage\" returntype=\"xml\"><arguments><string>" + " <Message name=\"" + messageName + "\" type=\"1\" gameId=\"" + gameId + "\"><Speed>" + speed.ToString() + "</Speed></Message>" + "</string></arguments></invoke>"); }
在c#中使用player.CallFunction()方法向flash发送消息,其中"onPlatformMessage"是flash接收消息的方法名。
在本项目中,向flash发送的消息,只有<string>标签中的消息不用,其他的格式都是通用的。
通信原理及过程:
通信原理个人不甚懂,摘抄他人的解释共享。
其基本过程为:flash中注册函数——将要调用的函数名、参数等信息打包为xml—>将xml传递到flash中执行调用—>执行ActionScript函数—>flash自动将结果打包为xml—>将结果xml传递回C# —>C#解析xml得到返回值
可见,flash与C#通信是通过特定格式的xml文件进行的,因此为了实现通信,C#必须实现一下功能:识别该xml格式以得到flash发送来的信心,将需要传送给flash的信息打包成flash能识别的xml格式。为了使用方便我们可以设计一个代理类专门负责数据的解析与打包工作,这样我们就可以透明的使用该代理类来实现flash与C#间的互相调用。
在flash的示例中有一个IntrovertIM_CSharp项目较好的实现了代理的编写,一般情况下,使用该示例中的ExternalInterfaceProxy类足以满足要求,因此方便起见我也直接采用了该代理类。
该类的使用非常简单,通过将flash控件作为参数传递给该类的构造函数,我们就可以建立一个给控件的代理,简单的响应代理的ExternalInterfaceCall事件及使用代理类的Call()方法就能实现与flash通信,中间的数据转换工作完全由代理透明的实现,用户无需关心。
其它语言及程序课根据上面原理编写自己的代理类来实现数据的解析与打包工作。