許久不曾寫隨筆,即使許久的怠惰,是該抬抬頭,看看天了。
公司項目,項目要求是在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通信,中間的數據轉換工作完全由代理透明的實現,用戶無需關心。
其它語言及程序課根據上面原理編寫自己的代理類來實現數據的解析與打包工作。
