當前位置:首頁 >教程首頁 > 华体会hth体育app在线登录 > 遊戲UI設計師班 >分享製作異步多人遊戲的方法和經驗(1)

分享製作異步多人遊戲的方法和經驗(1)

發布時間:2018-11-17 19:57:24
作者:Ross Przybylski

毫無疑問,異步多人遊戲玩法(也就是允許玩家幾天登錄一次遊戲)是手機遊戲開發的新趨勢。許多熱門的多人遊戲都是異步的,比如《填字遊戲》(一種每次移步一步的拚字遊戲)和《你猜我畫》(Zynga以1.8億美元收購的一款看圖說詞遊戲)。

當我的跨平台多人遊戲《英雄法師》在iOS上發布時,我以為人們會為它的玩法——允許使用PC或Android設備與朋友一起在線戰鬥,感到興奮。讓我驚訝的是,我收到的反饋中,絕大多數的意思是“這款遊戲如果能夠多名玩家不同時在線也能玩就太好了!”

正如大多數《英雄法師》的玩家所知道的,我並沒有感到沮喪——我立即將下一個包括異步多人玩法的更新當作優先工作。我以前從來沒有編寫過“異步多人”的代碼,所以我想我得從我一慣的做法入手:穀歌搜索。“如何編寫異步多人遊戲玩法的代碼”的搜索結果並不實用:我發現有不少關於“異步玩法多麼了不起”和“了解大量支持異步玩法的遊戲”的文章,但它們並沒有講到“如何製作”的點子上。因此我想到“這對遊戲開發者來說應該是很重要的資源”,所以我決定記錄我製作《英雄法師》的異步多人玩法的過程,並發表出來,這樣我們都能從我所希望寫成的“如何製作”係列文章中獲益。

《英雄法師》是用Adobe Flash製作的,所以我的程序代碼案例是ActionScript 3格式的。但是,異步多人的設計和機製適用於任何開發語言。

向大師學習

學習如何編程的最好辦法就是,研究成功地實現你想要的結果的應用。我的目標是在我的幻想風格、回合製、策略遊戲中實現異步多人玩法。蘋果應用商店裏正好有一款類似的遊戲,並且它的異步玩法做得非常棒。它就是Robot Entertainment的《英雄學院》。所以我花了一些時間玩這款遊戲(這是最好的研究方法)。我發現,這款遊戲將社交媒體如Facebook和Twitter,與有效的異步多人UI相結合,很好地解決了“孤立社區”的難題——這也是我自己的“在線即時”多人遊戲遇到的困境。



以下是《英雄學院》的異步多人玩法的概述:

1、啟動遊戲時自動登錄服務器

2、通過Facebook和Twitter邀請/挑戰玩家

3、創建新遊戲的選項或尋找隨機對手的選項

A、如果玩家創建新遊戲,則新遊戲將被添加到內部遊戲列表中,等待其他玩家加入。

B、如果玩家選擇加入遊戲,則玩家將加入在內部遊戲列表中顯示的隨機遊戲。加入的玩家得到第一回合。

4、玩家在自己的回合中,在提交命令以前,可以執行全部的5次移動或取消所有移動。

5、一旦回合提交,遊戲的數據庫將更新遊戲狀態,並“推送”一個提示給對手,告訴該玩家輪到他的回合。

6、對手有24個小時可完成他的回合,否則玩家可以宣布該回合失效。

7、玩家可以選擇在這個遊戲中輪流,或返回個人“遊戲列表”中加載任何已經玩過的遊戲。遊戲以對手、創建數據、最後一次移動時間和狀態(即勝利、失敗、等待回合或就緒)為標簽。

《英雄法師》的特殊要求

對《英雄學院》的研究使我深入了解了異步多人玩法,但《英雄法師》因為其特殊的遊戲機製,還必須考慮到其他問題:

1、《英雄學院》具有取消功能,對異步玩法來說是非常棒的,因為玩家在提交最終選擇以前,可以實驗不同的移動組合。《英雄學院》能夠這麼設定,是因為遊戲中的所有傷害量是固定的。而《英雄法師》要根據骰子數計算傷害,所以撤消的選項就不可行了,因為它會影響遊戲的關鍵機製:運氣。

2、《英雄學院》不支持即時多人玩法。所有移動和甚至玩家聊天都是通過數據庫更新來記錄遊戲狀態的。雖然有可能和其他人在同一個房間內玩《英雄學院》,但這種體驗並不理想,因為你必須等待推送提示你對手完成他的命令。對於《英雄法師》,這個係統的改進辦法是,當雙方玩家均在線時,能夠保存了“即時”遊戲鏈接——這樣你就能實時看到你的對手的移動和交流信息。

如何製作異步多人玩法

了解了《英雄學院》的UI,以及認真考慮《英雄法師》的特殊要求後,我想到以下執行異步多人玩法的必要步驟:

1、想辦法將遊戲狀態保存到在線數據庫中。

2、編寫一個數據庫查詢,用來加載玩家的遊戲列表,同時通過點擊列表上的項目,使玩家載入和恢複遊戲。

3、在載入遊戲時,點擊可查看該遊戲是否可以“即時”玩。如果可以,則加入該遊戲,並與目前在線的玩家關聯。如果不可以,則創建一個“即時”遊戲房間,並載入該遊戲。

4、想辦法回放任何自玩家上一次登錄後沒有“看到”的動畫。

5、在實際遊戲中,使數據庫中已保存的遊戲狀態更新玩家的命令(這與《英雄學院》是不同的,因為《英雄學院》要求你等到對方按下回合結束鍵,數據庫才更新)。通過編寫持續的、更小的更新,可以節省帶寬,而且可以很自然地從異步過渡到即時玩法。

6、想辦法將即時玩法元素(回合計時器、掉落計時器、遊戲持續時間表、AI變化控製器)過渡到異步玩法(遊戲邦注:這是《英雄法師》的特殊要求,不適用於其他異步遊戲)。

7、設計一個UI,用於瀏覽和加入異步多人遊戲。

8、製作一個匹配係統,允許玩家選擇軍隊參數、對手類型等,還可以將玩家與數據庫中的可用對手相匹配。

9、當遊戲回合結束時,通過郵件或設備推送提示發送回合開始的信息給下一個對手。


異步多人遊戲允許兩個或以上的玩家參與遊戲,不需要同時登錄。支持異步玩法的關鍵是,將遊戲狀態保存到在線數據庫中,這樣你和你的對手才能在自己的回合時重新取回遊戲。本文將解釋我如何實現遊戲狀態儲存和重新載入,並且提供實用的代碼案例,希望在你為华体会hth体育app在线登录 相同的玩法時能派上用場。

本文將介紹:

1、如何通過簡單的2D網格表現法和基於該表現法的命令記錄表現遊戲狀態。

2、如何使用Smart Fox Server Pro的服務器端擴展將遊戲數據寫入在線數據庫。

要求

前提:

開發回合製遊戲的經驗

熟悉ActionScript 3.0

知道如何設置MySQL數據庫

知道如何編寫Smart Fox服務器擴展

必需產品:

Flash Professional

Smart Fox Server Pro

MySQL Database

用戶水平:

中級到高級

將遊戲狀態表現為數據

遊戲狀態是由所有定義遊戲麵板當前狀態的元素組成的:遊戲麵板的布局、遊戲部件的位置、遊戲中的所有角色的當前屬性和作用、各玩家手中的卡片,以及(如果對遊戲很重要)產生遊戲當前狀態的一係列移動。如何根據這些因素的複雜度良好地展示遊戲數據。在《英雄法師》中,我使用了兩種辦法——簡單的2D網格表現法和基於表現法的命令記錄。

簡單的2D網格表現法

可以使用用二維數組表現遊戲部件在遊戲麵板網格上的位置。例如,一個簡單三連棋遊戲可以表現如下:

//CODE EXAMPLE 1: TIC TAC TOE REPRESENTED AS 2D ARRAY
var ticTacToeGameState:Array = [];
ticTacToeGameState[0] = [X, O, X];
ticTacToeGameState[1] = [X, X, O];
ticTacToeGameState[2] = [O, X, O];

這個基於表現法的數據在程序代碼的情況下是足夠的,但對於異步遊戲,必須使用平麵數據結構將這個表現法保存到在線數據庫中。當表現遊戲狀態時,為了節約帶寬和服務器空間,最好使用盡可能少的信息。

假設我們知道三連棋總是3×3,那麼這個遊戲可以使用平麵字符串表示如下:

//CODE EXAMPLE 2: TIC TAC TOE REPRESENTED AS FLAT STRING
var ticTacToeGameState:String = “XOXXXOOXO”;

在取回遊戲狀態數據時,我們可以再次建立如上所示的二維數組:

//CODE EXAMPLE 3: CONVERT 2D GAME STRING TO 2D ARRAY
var ticTacToeGameGrid:Array = [];
for(var i:int = 0; i < 3; i++){
var gridRow:Array = [];
for(var j:int = 0; j < 3; j++){
gridRow.push(ticTacToeGameState.charAt(i+j));
}
ticTacToeGameGrid.push(gridRow);
}

《英雄法師》使用這個簡單的2D網格表現法將地圖布局保存成一係列X和O。X表示牆,O表示開放空間,起始位置是一係列表示玩家和特殊單位類型放置區域的數值組合。

基於表現法的命令日誌

如上所示,拉成一條單行文本串的二組數組可以用來表現許多基於網格的遊戲。那種根據特定遊戲活動發生時間的遊戲很適合用基於現表法的命令日誌來表示。

基於表現法的命令日誌的好處

命令日誌表現法的作用是,使遊戲引擎通過提供產生當前狀態的遊戲命令列表,重製保存好的遊戲狀態。命令被儲存成簡化符號,以節省文件空間和帶寬。當接收命令日誌時,動畫將不可播放,這樣遊戲就可以立即重製了。

使用命令日誌重製遊戲確保所有必要的遊戲細節:遊戲卡片、麵板部件和這些部件的狀態(遊戲邦注:準確地表現為完全相同的樣式,這個樣式產生最初的遊戲狀態)。另外,命令日誌顯示了完整的遊戲曆史。對於異步遊戲來說,這是極其有益的,因為玩家可以回顧活動列表,然後想起他們的當前狀態是如何產生的,從而製定相應的策略。玩家還可以恢複自己沒有機會看到的活動。

縮略的遊戲符號

編寫有效的基於表現法的命令日誌,困難的地方在於設計一種既簡單又準確的符號形式。為命令格式定義一係列預期標準也是很重要的。以下是我為《英雄法師》製作的句法:

1、各個獨立命令放在“<c>command</c>”內

2、命令內容由“|”分開,內容分配由“=”表示

3、所有命令都包含定義命令類型(“cT”)的內容。不同的命令類型來自相應函數名稱的縮寫符號。例如,指示單位執行某活動的命令可以表示為“cT=uA”。

4、複雜的數據結構如單位和活動由特殊的數值id表示。

A、根據單位被添加到遊戲麵板的順序分配id。

B、根據咒語在麵板數組的索引來分配id。

C、根據能力在單位能力數組的索引來分配id。

5、命令的目標用逗號隔開;網格坐標(X和Y)用冒號隔開。

通過遵守嚴格的符號和用數值id引用複雜的實例對象,遊戲命令可以用來表示簡單的字符串,如下所示:

//CODE EXAMPLE 4: OBJECT TO STRING FUNCTION
function objectToString(object:Object, separator:String, valAssignment:String):String{
var string:String = “”;
for (var prop:* in object){
string += prop + valAssignment + object[prop] + separator;
}
string = string.substr(0, string.length – separator.length);
return string;
}

如果你的對象包含嵌套的數組,你就必須首先使用特殊分離器和分配字符將那些數組編碼成字符串:

//CODE EXAMPLE 5: CONVERT ARRAY TO STRING
var myArray:Array = [4, 5, 6, 7];
var myStringArray:String = myArray.toString();

以下是《英雄法師》中的完整遊戲命令:

//CODE EXAMPLE 6: GENERATE GAME COMMAND
function generateUseActionCommandString():String{
//Create a new object to store command properties
var HM_UseAction:Object = new Object();

//Store the command type: “uA” represents “useAction”
HM_UseAction.cT = “uA”;

//The abilityUser is a complex, custom datatype
//So, we store the id of the unit using ability
HM_UseAction.uId = abilityUser.unitId;

//The unit’s ability is also a complex, custom datatype
//So, we store the id that represents its index in the abilities array
HM_UseAction.i = abilityIndex;

//pT represents the primary targets
//In actual game, a function discerns between target types (units, spaces)
//Here, we simply convert the array of choices to a comma deliniated string
HM_UseAction.pT = primaryTargetsToActOn.toString();

//Encapsulate the command within c-tags
var strCmd:String = “<c>”+objectToString(HM_UseAction, “|”, “=”)+”</c>”;

//A preview of the assembled command
trace(strCmd) //<c>cT=uA|uId=1|i=1|pT=4,5,6″</c>

//Return the command
return strCmd;
}

作為參考,下列函數可以用來將字符串轉換回對象:

//CODE EXAMPLE 7: STRING TO OBJECT FUNCTION
function stringToObject(string:String, separator:String, valAssigment:String):Object{
var object:Object = new Object();
var props:Array = string.split(separator);
for(var i:int = 0; i < props.length; i++){
var vals:Array = props[i].split(valAssigment);
object[vals[0]] = vals[1];
}
return object;
}

將遊戲寫入數據庫

如果遊戲應用可以用簡單的文本文件表現它的保存狀態,那麼下一步就是將遊戲的保存數據寫入在線數據庫,這樣其他玩家就可以隨時恢複遊戲狀態。為此,你需要一個帶MySQL、SQL或其他形式的數據庫的網上服務器,以及一個網上服務或服務器來與數據庫交流、運行必要的查詢,和發送/接收來自應用的數據。

《英雄法師》使用Smart Fox服務器完成實時多人連接(在線聊天和異步玩法),所以我使用服務器端代碼來處理與數據庫的交流活動。《英雄法師》的數據儲存在MySQL數據庫中,我已經通過我的主機供應商GoDaddy.com提前做好這個數據庫了。我喜歡使用SmartFox服務器,是因為我可以通過ActionScript 1.0直接使用MySQL,而不必擔心不懂PHP或其他服務器語言的問題。

定義儲存遊戲數據的表格

定義將用來把遊戲信息保存在數據庫中的表格也非常重要。為此,《英雄法師》使用兩套表格:

表格“hm_games”用來保存所有相關的遊戲數據。“cmdLog”欄保存遊戲引擎將用於重建遊戲狀態的符號命令的實際列表。


表格“hm_gameresults”用來保存與遊戲相關的玩家特定信息。某一遊戲的所有玩家都通過ID_GAME與hm_games表格關聯起來。這個表格保存結果(無論玩家是勝利還是失敗)、排名變化(如果遊戲有排名的話),同時還要進一步更新,以幫助決定再次加入遊戲的玩家必須看到的動畫。


創建新遊戲記錄

《英雄法師》具有異步多人玩法,但我還沒有給異步匹配係統做過UI。然而,令人興奮的遊戲創建屏幕非常適合用來解釋如何將新遊戲記錄保存到數據庫。

遊戲客戶端和在線服務之間的基本通信運作如下:

1、如果遊戲主機已配置所有遊戲選項,並且玩家覺得滿意,則他們會按下“Start Game”。

2、遊戲客戶端將遊戲背景格式化為符號遊戲命令,發送命令給服務器,並等待回應。

3、服務器端腳本接收命令並創建遊戲記錄,執行一個MySQL聲明,以便在hm_gameresults表格中創建新入口,和在hm_gameresults表格中為各名玩家創建新記錄。

4、如果服務器的數據庫運行完全順利,則服務器對客戶端作出回應,反饋新創建的遊戲記錄的ID_GAME。如果服務器運行失敗,客戶端接收到新遊戲無法創建的反饋。

5、如果客戶端接收ID_GAME,遊戲主機就用這個內容重新裝配命令符號,並發送開始遊戲命令給所有玩家。如果收到“操作失敗”,則遊戲客戶端將顯示錯誤信息。

注:如果你需要學習如何給Smart Fox Server Pro編寫服務器端擴展,請參考其他網上教程。

當符號化遊戲命令裝配完畢,就使用以下函數發送命令給服務器端擴展:

//CODE EXAMPLE 8: SEND CREATE GAME COMMAND
private function sendStartGameCommand(HM_GameVars:Object):void{
//Store a reference to the created game settings object for use later
gameVarObj = HM_GameVars;

//Check to see that smartFox is connected and that there are at least 2 players
if(smartFox.isConnected == true && playerSettingsList.length > 1){
//Send the command to create new game record to Smart Fox Server extension
//Commands can be sent as string or xml; normally, I use string for speed
//In this case, I use xml to save the work of encoding to string
smartFox.sendXtMessage(“HMServer”, “CreateGameRecord”, HM_GameVars, “xml”);
}
else{
//If this is a practice game with only 1 player, no need to store to database
//Fire game up immediately
fireUpGameWithRecordID(-1);
}
}

在服務器端,我給“Create Game”命令添加了新條件,用來在數據庫中插入新遊戲記錄:

//CODE EXAMPLE 9: CREATE GAME RECORD IN DATABASE
function handleRequest(cmd, params, user, fromRoom, protocol){
if(protocol == “xml”){
//….CODE FOR OTHER EXTENSION COMMANDS OMITTED….

else if(cmd == “CreateGameRecord”){
//EXTRACT THE PLAYER INFORMATION AND TURN ORDER FROM THE RECEIVED COMMAND
var players = String(params.pS).split(“,”);
var randomTurnOrders = params.rTO.split(“,”);

//GENERATE THE MYSQL STATEMENT TO ADD NEW GAME RECORD BASED ON GAME SETTINGS
var gameRecordSQL = “INSERT into hm_games (ranked, timeCreated, version, status, timeRecorded, whoseTurn, cmdLog) VALUES (”
gameRecordSQL += “‘” + params.r + “‘, “                               //ranked
gameRecordSQL += “‘” + Math.floor(getTimer() / 1000) + “‘, “     //timeCreated
gameRecordSQL += “‘” + params.v + “‘, “                               //version
gameRecordSQL += “‘” + 1 + “‘, “                                      //status
gameRecordSQL += “‘” + Math.floor(getTimer() / 1000) + “‘, “     //timeRecorded
gameRecordSQL += “‘” + stringToObject(players[randomTurnOrders[0]], “;”, “:”).hmId + “‘, “          //whoseTurn
gameRecordSQL += “‘” + “<c>” + objectToString(params, “|”, “=”) + “</c>” + “‘”  //cmdLog
gameRecordSQL += “)”;

//EXECUTE MYSQL COMMAND AND CHECK IF IT WAS SUCCESSFUL
success = dbase.executeCommand(gameRecordSQL);
if(success == false){
//IF THIS FAILS, WE NEED TO REPORT BACK AN ERROR TO CLIENT
trace(“UNABLE TO CREATE GAME RECORD”);
response.error = “Unable to create new game record in database”;
}
else{
//ONCE GAME RECORD IS ADDED, GRAB ITS ID (WE KNOW ITS THE LAST INSERTED RECORD)
sql = “SELECT LAST_INSERT_ID()”
var queryRes = dbase.executeQuery(sql);
var dataRow = queryRes.get(0);

//STORE THE GAME RECORD ID IN OUR RESPONSE OBJECT
response.id = dataRow.getItem(“LAST_INSERT_ID()”);

//CREATE A STATEMENT TO INSERT A NEW RECORD IN GAME RESULTS TABLE FOR EACH PLAYER
var gameResultsSQL = “INSERT into hm_gameresults (ID_GAME, ID_MEMBER, result, ratingChange) VALUES “;
for(var i = 0; i < players.length; i++){
//CONVERT THE PLAYER OPTIONS FROM STRING TO OBJECT
//THIS IS SO WE CAN EXTRACT PROPERTIES LIKE PLAYER ID
var playerOptions = stringToObject(players[i], “;”, “:”)

gameResultsSQL += “(LAST_INSERT_ID(), ‘” + playerOptions.hmId + “‘, ‘” + “-2″ + “‘, ‘” + “0″ + “‘)”
if(i < players.length – 1){
gameResultsSQL += “, “;
}
}
success = dbase.executeCommand(gameResultsSQL);
if(success == false){
//IF THIS FAILS, WE NEED TO REPORT BACK AN ERROR TO CLIENT
trace(“UNABLE TO CREATE GAME RESULTS RECORD IN DATABASE”);
response.error = “Unable to create game results records in database”;
}
}
}

//….CODE FOR OTHER EXTENSION COMMANDS OMITTED….
}
}

返回客戶端,我給來自服務器的“Create Game”命令的響應添加了新條件:

//CODE EXAMPLE 10: RECEIVE SERVER SIDE RESPONSE
private function onExtensionResponse(evt:SFSEvent):void{
//EXTRACT RESPONSE TYPE AND RESPONSE DATA FROM SFSEvent
var type:String = evt.params.type;
var dataObj:Object = evt.params.dataObj;

//….CODE OMITTED….

//EXTRA COMMAND FROM RETURNED DATA OBJECT
cmd= dataObj.cmd;
var error:String = dataObj.error;

//….CODE OMITTED….

if(error != “”){
//IF RESPONSE RETURNS AN ERROR, SHOW USER A MESSAGE PROMPT
showPrompt(“HM_MessagePrompt”, cmd + ” Error”, error);
}
else{
//….CODE OMITTED….

//ADD CONDITION FOR SERVER RESPONSE CREATE GAME RECORD
else if(cmd == “CreateGameRecord”){
//INSTRUCT GAME OPTION SCREEN TO FIRE UP GAME RECORD
gameOptionsScreen.fireUpGameWithRecordID(dataObj.id);
}
//….CODE OMITTED….
}
}

這個調用遊戲大廳的最後一個函數來發出開始遊戲的命令:

//CODE EXAMPLE 11: FIRE UP GAME
public function fireUpGameWithRecordID(gameRecordId:int):void{
//Recall in previous step we stored gameVarObj for future use
//Here, we add the database id for the new game record
gameVarObj.gId = gameRecordId;

//Assemble the game command into abbreviated string notation
var HM_Command:String = Utils.objectToString(gameVarObj, “|”, “=”);

//Add the command to client side que
addToCommandQue(HM_Command);

//Send the command to start game to any live players
if(smartFox.isConnected == true){
smartFox.sendCmd(HM_Command);
}
}

更新遊戲狀態

當創建在在線數據庫中的遊戲記錄和遊戲客戶端可以獲得引用記錄的ID時,遊戲狀態的改變就可以由附加的新遊戲命令輕鬆記錄到命令日誌欄中。

在本文的前半部分,我想到遊戲狀態的更新應該根據遊戲創建的方式來決定。在Robot Entertainment的《英雄學院》一例中,玩家在提交回合以前可以選擇取消,更新遊戲狀態自然要在回合提交後發生。相反地,《英雄法師》允許玩家秘各個可用單位互動、施放咒語和發動攻擊(根據骰子數決定傷害程度)。因為結果的隨機性,《英雄法師》就不能使用取消功能了。因此,我決定,玩家每發送一次命令,遊戲的命令日誌就更新一次。

因為《英雄法師》也可以即時玩,所以我決定把我現在的服務器擴展(用於交換即時玩家之間的遊戲命令)也更新了,使它也能處理儲存在數據庫中的遊戲狀態。這樣,隻需要讓客戶端發送一次命令給服務器,我就可以最有效地利用帶寬。

以下是處理遊戲狀態更新的代碼:

//CODE EXAMPLE 12: UPDATE GAME RECORD
function handleRequest(cmd, params, user, fromRoom, protocol){
if(protocol == “str”){

//GENERATE LIST OF RECIPIENTS THE SERVER WILL SEND THIS COMMAND TO
//….CODE OMITTED….

//params[2] stores game record id
//If this game record id is included, we need to write this command to stored game log
if(params[2] != undefined){
if(params[1].indexOf(“cT=eT”) != -1){//If this is an end turn command
//Convert notated command into object
var cmdObj = stringToObject(params[1]+”", “|”, “=”);
//Get the id of player whose turn is next
var nextTurnId = cmdObj.nId;
//Write update to game record in database
sql = “UPDATE hm_games set cmdLog = CONCAT(cmdLog, ‘<c>” + params[1] + “</c>’), timeRecorded = ” + Math.floor(getTimer() / 1000) + “, timeLastTurn  = ” + Math.floor(getTimer() / 1000) +”, whoseTurn = “+nextTurnId+” WHERE ID_GAME = ” + params[2];
}
else{
//Write update to game record in database
sql = “UPDATE hm_games set cmdLog = CONCAT(cmdLog, ‘<c>” + params[1] + “</c>’), timeRecorded = ” + Math.floor(getTimer() / 1000) +” WHERE ID_GAME = ” + params[2];
}
success = dbase.executeCommand(sql);
if(success == false){
//THE DATABASE DID NOT RECORD THE MOVE CORRECTLY
//CREATE A NEW RESPONSE TO NOTIFY GAME CLIENT OF THE ERROR
}
}
_server.sendResponse([params[1]], -1, null, recipients, “str”);
return;
}
}

總結

本文介紹了製作一個異步多人遊戲的最基本的步驟:將遊戲狀態表現為數據,並保存到在線數據庫中。在下一篇文章中,我將分享如何從數據庫中恢複遊戲狀態,以及如何使用Flash、ActionScript和Smart Fox服務器拓展在異步多人模式和即時在線模式之間無縫地轉換。
华体会hth体育网 賞析
  • 2101期學員李思庭作品

    2101期學員李思庭作品

  • 2104期學員林雪茹作品

    2104期學員林雪茹作品

  • 2107期學員趙淩作品

    2107期學員趙淩作品

  • 2107期學員趙燃作品

    2107期學員趙燃作品

  • 2106期學員徐正浩作品

    2106期學員徐正浩作品

  • 2106期學員弓莉作品

    2106期學員弓莉作品

  • 2105期學員白羽新作品

    2105期學員白羽新作品

  • 2107期學員王佳蕊作品

    2107期學員王佳蕊作品

專業問題谘詢

你擔心的問題,火星幫你解答
×

同學您好!

您已成功報名0元試學活動,老師會在第一時間與您取得聯係,請保持電話暢通!
確定