SKYXY客服
x
遊戲角色同步技巧 II — 隱藏延遲技巧 | skyxy
快速建站
︿
TOP
首頁 > 研究專題 > 文章內容
遊戲角色同步技巧 II — 隱藏延遲技巧
2/18/2017 1:27:33 PM
瀏覽數:981
沈夜
遊戲角色同步_隱藏延遲
本範例使用 CharacterController 來驅動角色,關於第三人稱平台的操控應用並不在本範例的探討範圍內,請自行參考範例專案內下面的場景與腳本。
 
Assets/Scenes目錄下的Console場景
Assets/Scripts/GameScene目錄下的CharControl_Base.cs
隱藏延遲技巧分析
前言:
連線遊戲因為需要透過實體網路來與別的使用者互動,網路延遲的狀況是無法避免的,而使用者在即時互動性高的連線遊戲中,在網路環境不太穩定的狀態下如果沒有處理好遊戲角色的同步,使用者在遊戲體驗上會非常差。
 
隱藏延遲技巧的主要目的在於讓使用者察覺不出 lag 的現象,而是透過預測的方式來平滑地同步處理遊戲角色的運動,在實做之前我們先理解一下隱藏延遲的處理方式。
 
在常見的角色扮演或是動作遊戲遊戲中,使用者常常會操控遊戲角色在場景中來回移動,這時候我們就得到了遊戲角色從某個座標移動到另外一個座標的行為模式。
在連線遊戲中,我們需要讓其他一起進行遊戲的遠端程式知道遊戲角色的座標發生改變了,並且將遠端程式中對應的遊戲角色移動到新的座標,這就是角色同步。為了實現角色同步,我們還需要同步資訊的資料內容來當作同步的依據。
如果我們在本地端角色移動到目的地才要求其他的使用者同步遊戲角色的座標,甚至要求同步的訊息因為網路延遲使得其他玩家經過一段時間後才收到,這樣會造成本地端使用者已經在新的座標等待,但是其他的遠端使用者才開始同步處理遊戲角色的狀況,這與本地端使用者所看到的實際情況落差是非常大的。
角色移動狀態

角色移動時會由於網絡問題造成延遲,所以為了避免這個狀況,我們應該在本地端的角色在開始移動時就同時告知其他的遠端玩家。並且在遊戲角色抵達目的地之前持續的發送同步資訊,讓遠端玩家的程式可以藉由同步資訊不斷的預測、修正來達到遊戲角色的同步。
使用者對於遊戲角色會有以下幾種常見的操控行為。
  1. 角色由靜止狀態進入運動狀態。
  2. 角色由運動狀態進入靜止狀態。
  3. 角色在運動狀態時因為轉動攝影機或者輸入按鍵而改變移動的方向。
  4. 角色在運動狀態時因為遊戲機制而加快/減慢當前的移動速度。
  5. 角色在運動狀態時持續朝著某個方向前進,移動期間維持固定的移動速度。

我們可以由上面的幾種操控行為歸納出兩種運動狀態:
  1. 遊戲角色動量發生改變
  2. 遊戲角色持續地定向定速移動
 
針對這兩種運動狀態我們也需要不一樣的處理方式。
當本地端的使用者開始操控遊戲角色時,我們將可以取得遊戲角色的原始位置、
前進方向與移動速度。
考慮到網路環境的延遲時間差並非恆定的狀態下,我們還需要設定一個同步時鐘來定時發送同步的資訊,並且加入當下網路延遲的時間差當作依據。
這些同步資訊將會傳送給其他的遠端程式,並且開始計算預測點。後面的章節將會隨著需求而完善整個同步資訊的內容,而同步時鐘的概念與用法也會在後面的章節描述。
接著我們以下圖為例說明整個角色同步的過程,當ClientA的玩家操控PlayerA由靜止狀態開始運動時,ClietnA的程式將會定義一個同步資訊的訊息並且傳送給其他的遠端程式。
以下是同步資訊所定義的內容:
  • PlayerA 的起始位置 Position
  • PlayerA 的前進方向 EulerAngle.y
  • PlayerA 的移動速度 MoveSpeed
  • ClientA 的同步時鐘 SyncTimer
  • ClientA 當下的網路延遲 LagTime

當遠端程式在接收到同步資訊時,因為網路延遲的關係,在ClientA實際的畫面上PlayerA已經在PlayerA1的位置了,這裡的位移差是需要顧慮進去的。
ClientA的網路環境可能會隨著時間與空間的轉換,每次得到的網路延遲數據都不一樣,因此我們還要定義一個同步時鐘。以本例子來說,ClientA的遊戲角色在定向定速移動的情況下,每2秒將會發送一次同步訊息。
 
ClientB可以從同步資訊得到同步時鐘的數值為2秒鐘,在假定PlayerA會保持定向定速的情況下,我們可以計算出由PlayerA1的位置移動兩秒後的最終預測點PlayerA2。
此時ClientB上的PlayerA不論處在哪個座標點上,程式必須參考同步時鐘並控制PlayerA在兩秒鐘之後,精準地抵達預測點PlayerA2,如果ClientA的網路環境並沒有變化太大,當ClientB上的PlayerA在兩秒鐘之後移動到PlayerA2的位置時,應該會再次收到新的同步資訊。
 
如果PlayerA移動到PlayerA2還沒收到同步資訊,可以研判是ClientA的網路延遲加重了,在接收到新的同步資訊之前,ClientB只要讓PlayerA持續前進就可以了。
在遊戲角色定向定速移動的情況下,ClientA也會依據同步時鐘的數值不斷的發送同步資訊,而ClientB也會不斷的調整修正遊戲角色的運動方式。
接著我們以下面的例子說明當遊戲角色的動量發生改變時同步的處理流程。
PlayerA角色一開始先往direction1的方向前進,並且在PlayerAA的地方轉往direction2的方向前進,這時候ClientA將會馬上再次發送同步資訊,並且重置同步時鐘。
如果ClientA轉往direction2的同步資訊因為網路延遲還沒收到,ClientB的遊戲角色將會持續往PlayerA2移動。
當ClientB收到第二次的同步訊息之後可以取得新的預測點PlayerAA2,此時ClientB上的PlayerA不論處在哪個座標點上,程式必須參考同步時鐘並控制PlayerA在兩秒鐘之後,精準地抵達預測點PlayerAA2。
程式碼部分:

接著我們開啟位於Assets > Scripts > GameScene
目錄下的CharControl_Local.cs。
MovementDetector函式以moveDirection與lastMoveDirection的比較來判斷遊戲角色的動能是否發生改變。
當玩家轉動攝影機時,每個fream在定義上都算是方向發生改變,此時Client端會在瞬間發送出非常大量的同步訊息,為了避免這個狀況發生,我們可以限制當移動中的角色動能發生改變時,允許發送同步資訊的最短時間。
在角色定向定速保持移動的情況下,程式會以syncTime當作同步時鐘處理並且定時發送同步資訊。
CharacterSync函式負責定義同步資訊的內容,除了之前提到的五個數據之外,我們還需要加入poid讓其他遠端程式知道要控制哪一個遊戲角色,為了節省訊息傳送的資料量,我們可以在角色進入靜止狀態時定義一個比較簡短的同步資訊。
ClientA發送的同步資訊透過網路的傳遞可能使得ClientB收到的訊息順序不一致,因此我們也可以在同步資訊內加入Ticks,ClientB則可以透過此數據來過濾掉舊的同步資訊,確保遊戲角色參考的同步資訊是最新接收到的。
在發出同步資訊之後,所有的client將會在MainMsgIn()接收到同步資訊。
下面兩個函式會先過濾訊息來源的poid
在下面兩個函式中則會去尋找對應的remotePlayers元素,確定tick的先後順序沒問題之後則開始控制遠端角色的移動。
GameManager.MoveRemotePlayer函式負責解析同步資訊的內容。
在每個Client端呼叫GetDelayMilliSecond函式會因為當下網路環境的關係而得到不同的數據,取得的數值是由Client與arcalet雙向回傳之後得到的時間差當依據。
如果我們要取得ClientA傳送訊息到ClientB的延遲時間,我們只需要考慮單向傳送的延遲量就好。
單向傳輸的理論值為GetDelayMilliSecond()的一半,這裡的做法是將兩個Client取得的GetDelayMilliSecond()數據相加再除以一半。並且由realDelaySec將毫秒轉換成秒數,這個數值就是我們所實際需要的LagTime。
CharControl_Remote.cs負責控制遠端角色的移動,並且藉由CharacterSync函式取得moveDirection與moveSpeed的值來驅動角色。
為了更好理解預測點的變化,本範例為每位遠端角色綁定了一個可見的球體,球體會隨著預測點而改變自身的座標位置。
1
2
3
4
5
6
您也可能喜歡這些文章
留言給作者
不公開此留言     登入即可留言
讀者留言
載入更多留言