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

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

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

異步遊戲真的非常棒,因為玩家可以無需長期待在遊戲中便能享受到有趣的遊戲體驗。是在線服務器成就了這種便捷的遊戲風格,而本篇文章將著重解釋如何通過服務器加載之前所儲存的數據,並將其用於遊戲客戶端的用戶界麵上。(請點擊此處閱讀第1部分)

本篇文章將解釋:

1.如何查詢儲存於MySQL數據庫中的一列遊戲記錄,並將結果傳送給遊戲客戶端

2.如何在客戶端上說明查詢結果,並設計一個有意義的遊戲列表用戶界麵而幫助我們更有效地遊戲

3.如何通過一款異步遊戲而再次創造出生動且同步的多人體驗

4.如何重播動畫以呈現出玩家對手的移動

要求

預備知識

基於遊戲的開發體驗

熟悉ActionScript 3.0

閱讀過本係列文章的第二部分

產品要求

Flash Professional(試用版)

Smart Fox Server Pro(試用版)

MySQL數據庫

用戶級別

高級

生成玩家的遊戲列表

大受歡迎的異步遊戲,如《英雄法師》便使用了“遊戲列表”用戶界麵,即讓玩家能夠進入並繼續之前的異步遊戲過程。


在創造《英雄法師》的遊戲列表時,我最先創造了名為“HM_GamesList”的全新用戶界麵屏幕類。我想要先專注於數據和代碼組件,所以最初的設計便局限於數據頭和滾動列表組件,即能夠用於在服務器上填充信息:


這一界麵中有一個數據查詢庫,即帶有一列活躍玩家的遊戲數據。所有MySQL查詢生成都是發生在服務器一端,而與我們的在線服務器的交流如下:

1.遊戲客戶端:從服務器上請求數據

2.服務器:處理請求,向客戶端發送回應

3.遊戲客戶端:收到服務器回應

4.遊戲客戶端:基於數據執行預期任務

步驟1:請求遊戲列表

遊戲客戶端從服務器上請求遊戲列表:

//CODE EXAMPLE 1: Request Game List from Server
private function getGameList(lowerLimit:int){
//Create a new object to send command parameters
var params = new Object();
//Pass the player’s unique member id
params.pId = pId;
//Show user prompt while waiting for response
showPrompt(“ProcessingRequestPrompt”);
//Send Smart Fox Server an extension message
/*
sendXtMessage(xtName:String, cmd:String, paramObj:*, type:String = “xml”)
xtName = Name of your server side extension
cmd = Unique identifier name for this command
paramObj = Object contain parameters for command
type = Indicates whether we’re sending as XML or raw string
*/
smartFox.sendXtMessage(“HMServer”, “Game List”, params, “xml”);
}

步驟2:處理遊戲列表請求

服務器端將處理請求並向客戶端發送響應。在本係列文章的第二部分中,我曾經解釋過遊戲是如何使用兩個附錄(hm_games和hm_gameresults)保存到MySQL數據庫中。功能loadGameList將創建MySQL查詢並發回我們所需要的數據去生成遊戲列表。

//CODE EXAMPLE 2: Handle Game List Request on Server
function handleRequest(cmd, params, user, fromRoom, protocol){
if(protocol == “xml”){
//….CODE FOR OTHER EXTENSION COMMANDS OMITTED….
else if(cmd == “Game List”){
if(params.hmId != null){
//THE FOLLOWING MYSQL STATEMENT GATHERS A LIST OF GAMES PLAYER HAS PLAYED BY JOINING
//THE GAME AND GAME RESULTS TABLES CREATED IN PART 2
var sql = “SELECT ID_GAME from hm_games JOIN hm_gameresults using (ID_GAME) WHERE ID_MEMBER =1″+params.hmId;
//WE CREATE AN ARRAY TO STORE THE GAME LIST
var gameList = [];
//WE EXECUTE THE QUERY
var queryRes = dbase.executeQuery(sql);
//IF THE QUERY RETURNS RESULTS, POPULATE TO ARRAY
if(queryRes != null && queryRes.size() > 0){
for(var i = 0; i < queryRes.size(); i++){
//GET THE ACTIVE ROW
var dataRow = queryRes.get(i);
//CREATE GAME RECORD OBJECT
var gameRecord = {};
//STORE THE GAME ID IN THE RECORD
gameRecord.ID_GAME = dataRow.getItem(“ID_GAME”);
//ADD RECORD TO ARRAY
gameList.push(gameRecord);
}
}
//STORE THE GAME LIST IN THE SERVER RESPONSE
response.gameList = getGameList(params.hmId);
}
}
//….CODE FOR OTHER EXTENSION COMMANDS OMITTED….
}
}

步驟3:收到遊戲列表回應

遊戲客戶端收到服務器回應。

//CODE EXAMPLE 3: Receive Game List from Server
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 == “Game List”){
//HIDE OUR PROCESSING REQUEST PROMPT
hidePrompt();
//INSRUCT OUR GAME LIST CLASS TO RECEIVE THE LIST
gameList.receiveGameList(dataObj);
}
//….CODE OMITTED….
}
}

步驟4:填充遊戲列表

遊戲客戶端執行預期任務而填充列表:

//CODE EXAMPLE 4: Populate Game List
private function receiveGameList(gameList:Object):void{
//The game list is returned from server as array
var gameList:Array = dataObj.gameList;
//Create a new data provider to store the list
var dp:DataProvider = new DataProvider();
//Iterate through the list to add new items to data provider
for(var i:int = 0; i < gameList.length; i++){
var gameRecord:Object = gameList[i];
//Add a label property to object so it shows up in list cell
gameRecord.label = gameRecord.ID_GAME;
//Add item to data provider
dp.addItem(gameRecord);
}
//Set our UI list’s data provider
list.dataProvider = dp;
}

而以下便是我們的結果:


高級遊戲列表查詢

盡管具有功能性,但是上述所創造的基本遊戲列表缺少了穩定用戶體驗所需要的關鍵信息。玩家需要知道遊戲的創造時間,上一個回合是什麼時候,是誰的回合,最重要的是遊戲將加載哪個對手。

整合遊戲和遊戲結果列表

最理想的查詢需要使用最少的資源和帶寬將所有相關信息傳回客戶端。由Reflection Software的程序員Marco Rousonelos所設計的這一查詢結合MySQL能夠幫助各大論壇使用排名和派生表去生成預期結果集:

#CODE EXAMPLE 5: ADVANCED GAME LIST QUERY
SELECT IF(whoseTurn = 2 and status != 2, 1, 0) as myTurn, ID_GAME, ID_GAMETYPE, version, timeLastTurn, timeCreated, timeRecorded, status, isAsync, whoseTurn,
MAX(CASE WHEN PN = 1 THEN ID_MEMBER ELSE NULL END) AS ‘P1ID’,
MAX(CASE WHEN PN = 1 THEN memberName ELSE NULL END) AS ‘P1N’,
MAX(CASE WHEN PN = 1 THEN result ELSE NULL END) AS ‘P1R’,
MAX(CASE WHEN PN = 2 THEN ID_MEMBER ELSE NULL END) AS ‘P2ID’,
MAX(CASE WHEN PN = 2 THEN memberName ELSE NULL END) AS ‘P2N’,
MAX(CASE WHEN PN = 2 THEN result ELSE NULL END) AS ‘P2R’
FROM
(SELECT g.ID_GAME, g.ID_GAMETYPE, g.version, timeLastTurn, timeCreated, timeRecorded, status, isAsync, whoseTurn, r.ID_MEMBER, r.result,
( CASE g.ID_GAME
WHEN @curGame
THEN @curRow := @curRow + 1
ELSE @curRow := 1 AND @curGame := g.ID_GAME END
) AS PN
FROM hm_games g
JOIN hm_gameresults r USING(ID_GAME)
JOIN hm_gameresults pg ON g.ID_GAME = pg.ID_GAME AND pg.ID_MEMBER =2
,(SELECT @curRow := 0, @curGame := -1) n
) data
JOIN smf_members m USING(ID_MEMBER)
GROUP BY ID_GAME

基於這一查詢我們能夠生成如下結果集:

ID_GAME 1010

P1ID 1

P1N Ross

P1R 0

P2ID 2

PD2 Kelly

P2R 0

Status 1

whose Turn 1

……

添加額外的玩家

對於支持兩個以上玩家的遊戲,我們需要在查詢中添加如下附加內容:

#CODE EXAMPLE 6: Additional Player Support
MAX(CASE WHEN PN = 3 THEN ID_MEMBER ELSE NULL END) AS ‘P3ID’,
MAX(CASE WHEN PN = 3 THEN memberName ELSE NULL END) AS ‘P3N’,
MAX(CASE WHEN PN = 3 THEN result ELSE NULL END) AS ‘P3R’,
MAX(CASE WHEN PN = 4 THEN ID_MEMBER ELSE NULL END) AS ‘P4ID’,
MAX(CASE WHEN PN = 4 THEN memberName ELSE NULL END) AS ‘P4N’,
MAX(CASE WHEN PN = 4 THEN result ELSE NULL END) AS ‘P4R’

結果排序

我們應該按照如下順序設置結果集:

1.遊戲狀態(首先呈現出進行中的遊戲)

2.回合(首先呈現出玩家所處回合)

3.最新更新(首先呈現出遊戲的最新更新)

我們可以通過添加一些排序次序而做到這一點,即對於查詢的聲明:

#CODE EXAMPLE 7: Order Statement
Order by status asc, myTurn desc, timeRecorded desc

限製結果

我們必須清楚這一查詢結果將為所有發出請求的會員賬號發送所有遊戲記錄結果。但是隨著遊戲變得更加受歡迎,即越來越多玩家開始進入遊戲,我們將麵對越來越龐大的數據集。所以為了確保服務器,網絡和用戶設備不會負荷過大,我們必須包含限製聲明,如此用戶便隻能接收到特定的結果:

#CODE EXAMPLE 8: Limit Statement
Limit 0, 30

定製查詢

我們可以根據不同個性化的遊戲調整並定製查詢,同時也能夠通過包裝服務器請求中的額外參數對此進行控製。例如你可以儲存一個“lowerLimit”屬性和一個“limitSpan”屬性去控製查詢的限製。

基於穩定的查詢,即能夠傳送必要的結果集,我們將準備生成更有效的用戶體驗而呈現出結果。

設計異步多人UI

玩家的遊戲列表是異步多人遊戲體驗的核心。該列表是用於導航,檢查狀態,並反映遊戲的發展。除此之外,遊戲列表也是一個非常棒的排行榜/記錄工具,能夠用於回顧過去的戰鬥,敵人等等內容!


相關遊戲記錄信息

一個優秀的遊戲列表是始於一個優秀的遊戲記錄。每一個遊戲記錄都應該包含如下信息:

最後一個回合或者完成遊戲所需要的時間

遊戲創造的時間

狀態(不管是輪到玩家攻擊,等待回合,防禦,或獲得勝利)

參與其中的玩家名字

這些屬性能夠有效地幫助玩家選擇想要加載的遊戲。除此之外我們也可以添加更多細節以及其它可能性:

獨特的遊戲記錄ID

是否進行排名

地圖的名稱

遊戲對象

最理想的情況便是設計能夠匹配列表大小的遊戲記錄,從而讓它們能夠更有效地呈現在任何規格的手機設備上。《英雄法師》便是利用玩家形象去呈現角色肖像:


填充列表

在設計好遊戲的記錄單元格布局後,我們可以將遊戲記錄類別添加到列表組件中,從而確保玩家可以使用該內容去訪問遊戲過程。往列表中添加記錄的過程與在UI列表上添加內容一樣,隻不過這是在添加一些簡單的單元格,而我們所添加的則是自己定製設計的單元格。

《英雄法師》使用了AURA多屏幕組件UI。AURA代表麵線ActionScript 3.0動畫,實用工具和資源。這是我所編寫的一個類別和組件庫,即用於提升像監聽器與資源管理和UI設計等任務的速度。在屏幕截圖下方的列表是符合屏幕規格以及用戶設備輸入控製的高級組件。舉個例子來說吧,如果你正在一個觸屏輸入手機設備上玩遊戲,我們便可以通過滑動去操作該列表。而如果麵對的是台式機,你則需要使用標準滾動條進行導航。我們也可以麵向手機GPU去優化該列表,並在像第一代iPad等設備商基於60幀/秒去渲染單元格。

遊戲加載

遊戲列表的主要功能是讓玩家能夠通過在列表上選擇一個項目去加載之前保存的遊戲環節。與遊戲保存的過程類似,遊戲加載也要求客戶端和服務器代碼去創造遊戲加載請求,並從數據庫中檢索遊戲狀態,並啟動遊戲引擎去恢複玩家想要玩的遊戲內容。這一次我們也需要遵循4個步驟:

1.遊戲客戶端:向服務器請求數據

2.服務器:處理請求,向客戶端發送回應

3.遊戲客戶端:收到服務器回應

4.遊戲客戶端:基於數據執行預期任務

注:盡管我們能在遊戲列表查詢中收集遊戲狀態數據,但是我仍建議使用另一個服務器請求去獲得新遊戲記錄,如此才能有效節省帶寬。

步驟1:請求遊戲加載

遊戲列表中的每個單元格將使用如下代碼向服務器發送請求:

//CODE EXAMPLE 9: Client Side Load Game Request
//In the Game List constructor, add an event listener to our list for when a cell is clicked
public function HM_GameList(){
//…CODE OMITTED
list.addEventListener(ListEvent.ITEM_CLICK, gameSelected, false, 0, true);
}
//The game selected function handles our server request
private function gameSelected(evt:Event):void{
//First ensure a valid cell is selected
if(list.selectedIndex != -1){
/*
It’s possible that older games may not be compatible with newer versions of the engine.
So, It’s a good idea to store the required game version in the game record data.
You can write an compatability check function to ensure the version is compatible.
*/
if(HM_App.isCompatibleVersion(list.selectedItem.v) == false){
HM_Main.HMMain.showPrompt(“HM_MessagePrompt”, “Version Mismatch”, “Your game version ‘”+ HM_App.appVersion +”‘ is not compatible with this recorded game’s version ‘” + list.selectedItem.v+”‘.”);
return;
}
//Once again, create a new params object to store request parameters
var params = new Object();
params.gId = list.selectedItem.ID_GAME;
//Show our request prompt to the user
showPrompt(“ProcessingRequestPrompt”);
//And send the message to server
smartFox.sendXtMessage(“HMServer”, “Load Game”, params, “xml”);
}
}

步驟2:處理遊戲請求

在服務器這端,我們將為“遊戲加載”請求添加一個狀態。可能有人會問,既然《英雄法師》也能夠使用Smart Fox進行同步遊戲,為什麼我們不在玩家同時在線時將異步遊戲變成在線實時對抗呢?遊戲空間將在服務器上循環訪問空間列表,並檢查是否有任何空間的遊戲ID符合玩家想要異步加載的記錄。如果能夠找到合適的對抗,那麼服務器將傳回空間ID讓它能夠與在線玩家進行連接。而對於實時對抗,玩家可以直接從遊戲空間中加載遊戲數據。但是如果找不到實時對抗,那麼服務器便隻能從數據庫加載遊戲狀態。

注:為了實現這一循環,我們必須在創造一個實時遊戲空間時將遊戲記錄ID當成空間變量儲存起來。但是如果你隻對異步遊戲玩法感興趣的話,你便無需這麼做。

//CODE EXAMPLE 10: Server Side Handle Load Game Request
function handleRequest(cmd, params, user, fromRoom, protocol){
if(protocol == “xml”){
//….CODE FOR OTHER EXTENSION COMMANDS OMITTED….
else if(cmd == “Load Game”){
//First, we package our response to include the game record id and a room id
response.gId = params.gId;
response.rId = -1;
//HERE WE WANT TO GO THROUGH LIST OF ACTIVE GAMES AND SEE IF ANY MATCH TARGET GAME ID, IF SO, JOIN THAT ROOM, OTHERWISE, FIRE UP GAME
var rooms = _server.getCurrentZone().getRooms();
for(var i = 0; i < rooms.length; i++){
var room = rooms[i];
if(room.getName().indexOf(“#”+params.gId) != -1 || (room.getVariable(“gId”) != null && room.getVariable(“gId”).getValue() == params.gId)){
response.rId = room.getId();
break;
}
}
//A live room matching the game id was not found, so we need to load the game
if(response.rId == -1){
var gameRecord = loadGame(params.gId, response);
response.cL = gameRecord.cL;
var memberId = user.getVariable(“hmId”).getValue();
sql = “SELECT lastCmd from hm_gameResults WHERE ID_MEMBER = “+memberId+” and ID_GAME =”+params.gId;
queryRes = dbase.executeQuery(sql);
dataRow = queryRes.get(0);
response.lC = dataRow.getItem(“lastCmd”);
}
}
//….CODE FOR OTHER EXTENSION COMMANDS OMITTED….
}
}

遊戲加載功能將為遊戲加載檢索必要的信息。

//CODE EXAMPLE 11: Server Side Load Game Function
function loadGame(ID_GAME, response){
//Generate a MySQL statement to load the record for the provided game record id
sql = “SELECT cmdLog, timeCreated, timeRecorded, timeLastTurn, status from hm_games WHERE ID_GAME = ” + ID_GAME;
queryRes = dbase.executeQuery(sql);
//If the query was unsuccessful, add an error message to the prompt to inform user
if(queryRes == null || queryRes.size() <= 0){
response.error = “Unable to load game”;
return “”;
}
else{
//Hero Mages can be played synchronously and asynchronously
//Whenever an unfinished game is loaded as an async game, we change isAsync property to reflect
if(queryRes.get(0).getItem(“status”) != 2){
dbase.executeCommand(“Update hm_games set isAsync = 1 WHERE ID_GAME = ” + ID_GAME);
}
//Package our response with the cmdLog, which is where we store game state in PART 2
var gameRecord = {};
gameRecord.cL =queryRes.get(0).getItem(“cmdLog”)
return gameRecord;
}
}

步驟3:接收遊戲加載回應

回到客戶端,我們的Smart Fox服務器擴展響應監聽器需要一個新的狀態去接收來自服務器的“遊戲加載”回應。基於回應類型,我們可以選擇加入現有的實時遊戲或使用收到的參數創造一款新遊戲:

//CODE EXAMPLE 12: Client Side Load Game Response
private function onExtensionResponse(evt:SFSEvent):void{
//….CODE OMITTED….
//ADD CONDITION FOR SERVER RESPONSE LOAD GAME
else if(cmd == “Load Game”){
//Hide the waiting for response prompt
hidePrompt();

//Check to see if server provided a room id
if(dataObj.rId == -1){
//Room id not provided, so we load the game state directly from response object
loadGame(dataObj);
}
else{
/*
The server provided a room id. This means there is a
live game for this session already created by another player
so all we have to do is join the game
*/
joinRoom(dataObj.rId, “”, false);
}
}
//….CODE OMITTED….
}

步驟4:遊戲狀態加載

基於不同遊戲引擎,遊戲功能的加載也會不同,不過遊戲功能都需要處理以下一些任務:

1.將接收到的遊戲狀態數據串轉化回對象中

2.設置兩個不同的標記“isRunningCommandList”=正確以及“useAnimations”=錯誤

3.在引擎中運行命令列表從而在幕後有效地“播放遊戲”。你的引擎代碼應該檢查isRunningCommandList標記以確保當命令的自動回應(遊戲邦注:如反攻行動)已包含於命令列表時,我們不會再次啟動它。

重放動畫

遵循上述步驟你將能從遊戲列表中加載任何遊戲,並恢複與最後一次移動記錄相符合的遊戲狀態。然而我們也需要考慮到異步遊戲組件也會在其他玩家離開時改變遊戲狀態。而簡單地加載當前的遊戲狀態會讓玩家感到困惑,因為他們並不知道自己的組件正在執行怎樣的命令。為了創造有效的異步多人遊戲體驗,我們便需要對命令記錄過程和遊戲加載代碼做出一定的修改。

記錄最後一次移動

為了分別為每個玩家記錄最後一次移動,我們需要在hm_gameresults(是在第二部分文章所創造的名為“lastCmd”的列表)上添加額外的屬性。這一屬性是一個整數值,即用於儲存玩家最後一次移動的相關索引。

當我們需要發送新的遊戲命令時,隻要將命令記錄索引沿著命令進行傳達便可。隨後,我們將在代碼塊中(也就是用於處理遊戲狀態更新)添加如下代碼:

//CODE EXAMPLE 13: Storing lastCmd
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
}
//***NEW CODE BEGIN***
//Get list of all users in this room and update the lastCmd property in game results for everyone who witnessed this move live
var lastCmd = cmdObj.lC; //Store the lastCmd to record last witnessed move in live players’ gameresult records
var allUsers = room.getAllUsers();
for(i = 0; i < allUsers.length; i++){
var memberId = allUsers[i].getVariable(“hmId”).getValue();
sql = “UPDATE hm_gameResults set lastCmd =”+lastCmd+” WHERE ID_MEMBER = “+memberId+” and ID_GAME =”+gId;
trace(“GAME RECORD UPDATE: ” + sql);
dbase.executeCommand(sql);
}
//***NEW CODE END***
}
_server.sendResponse([params[1]], -1, null, recipients, “str”);
return;
}

為那些看不見的移動呈現動畫

我們將在服務器上的遊戲加載處理程序上添加一個額外內容去儲存玩家的lastCmd,如下:

//CODE EXAMPLE 14: Getting lastCmd
var memberId = user.getVariable(“hmId”).getValue();
sql = “SELECT lastCmd from hm_gameResults WHERE ID_MEMBER = “+memberId+” and ID_GAME =”+params.gId;
queryRes = dbase.executeQuery(sql);
dataRow = queryRes.get(0);
response.lC = dataRow.getItem(“lastCmd”);

以下便是完整的遊戲加載回應:

//CODE EXAMPLE 15: Revised Game Load Handler
function loadGame(ID_GAME, response){
//Generate a MySQL statement to load the record for the provided game record id
sql = “SELECT cmdLog, timeCreated, timeRecorded, timeLastTurn, status from hm_games WHERE ID_GAME = ” + ID_GAME;
queryRes = dbase.executeQuery(sql);
//If the query was unsuccessful, add an error message to the prompt to inform user
if(queryRes == null || queryRes.size() <= 0){
response.error = “Unable to load game”;
return “”;
}
else{
//Hero Mages can be played synchronously and asynchronously
//Whenever an unfinished game is loaded as an async game, we change isAsync property to reflect
if(queryRes.get(0).getItem(“status”) != 2){
dbase.executeCommand(“Update hm_games set isAsync = 1 WHERE ID_GAME = ” + ID_GAME);
}
//Package our response with the cmdLog, which is where we store game state in PART 2
var gameRecord = {};
gameRecord.cL =queryRes.get(0).getItem(“cmdLog”)

//***NEW CODE BEGIN***
//CODE EXAMPLE 14: Getting lastCmd
var memberId = user.getVariable(“hmId”).getValue();
sql = “SELECT lastCmd from hm_gameResults WHERE ID_MEMBER = “+memberId+” and ID_GAME =”+params.gId;
queryRes = dbase.executeQuery(sql);
dataRow = queryRes.get(0);
response.lC = dataRow.getItem(“lastCmd”);
//***NEW CODE END***

return gameRecord;
}
}

在儲存了最後的命令索引後,我們可以通過再次激活useAnimations標記而有效地播放適當的動畫。

其它注意事項

除了我所解釋的這些步驟,我們還可以通過許多方法去定製異步體驗並添加額外的功能。舉個例子來說吧,如果玩家能夠擁有無限的世界去回應一款異步遊戲的話會怎樣?一個缺乏運動精神的玩家會無限期延緩他的回合,並阻止獲勝玩家宣布勝利。而解決這一問題的一大方法便是設置“最大等待”期限,讓玩家可以與長時間未回到遊戲中的對手解除關係。就像在《英雄法師》中,我便規定如果玩家在3天內未回到之前的遊戲回合中,那麼對手玩家就可以選擇與之解除關係。

使用過濾器隻呈現出活躍的遊戲,基於特定對手尋找遊戲,以及查閱對手的狀態等也是非常有幫助的功能。

總結

本篇文章主要解釋了異步多人遊戲用戶界麵的創建,已儲存的遊戲環節的加載,以及對手最後一次移動的動畫重放。而在接下來的文章中我們將專注於異步多人遊戲匹配係統的相關理念,從而讓玩家可以無需在線連接而開始玩一款新遊戲或加入現有的遊戲中。

原文發表於2012年5月30日,所涉事件和數據均以當時為準。
华体会hth体育网 賞析
  • 2101期學員李思庭作品

    2101期學員李思庭作品

  • 2104期學員林雪茹作品

    2104期學員林雪茹作品

  • 2107期學員趙淩作品

    2107期學員趙淩作品

  • 2107期學員趙燃作品

    2107期學員趙燃作品

  • 2106期學員徐正浩作品

    2106期學員徐正浩作品

  • 2106期學員弓莉作品

    2106期學員弓莉作品

  • 2105期學員白羽新作品

    2105期學員白羽新作品

  • 2107期學員王佳蕊作品

    2107期學員王佳蕊作品

專業問題谘詢

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

同學您好!

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