幀同步
游戲同步主要方向是 狀態(tài)同步和幀同步。
狀態(tài)同步
狀態(tài)同步簡單來說就是有一個(gè)權(quán)威服務(wù)器運(yùn)行著一個(gè)沒有圖形界面的客戶端,然后服務(wù)器收集所有人的操作數(shù)據(jù) 計(jì)算后再把所有人的關(guān)鍵數(shù)據(jù)廣播給所有人,玩家的客戶端只是服務(wù)器的一個(gè)展示。
缺點(diǎn)是有些情況下同步數(shù)據(jù)量會很大 因?yàn)橛?jì)算全在服務(wù)器所以可能壓力會比較大。
幀同步
幀同步中的每一個(gè)客戶端都是要計(jì)算所有數(shù)據(jù),服務(wù)器只需要轉(zhuǎn)發(fā)彼此的操作即可。
幀同步的同步過程:
1.收集所有人的輸入,廣播給所有人。
2.客戶端接收到所有人的輸入,客戶端本地開始根據(jù)輸入計(jì)算得到游戲結(jié)果。
優(yōu)點(diǎn) 服務(wù)器壓力小,同步的數(shù)據(jù)量也很小相對應(yīng)的可以有更低的延遲和帶寬占用,可以直接沒有服務(wù)器。天生支持錄像。
缺點(diǎn) 反作弊難度較大,所有數(shù)據(jù)都在客戶端本地。
在Unity中實(shí)現(xiàn)幀同步的注意事項(xiàng)
如果要做幀同步 就必須保證所有客戶端再執(zhí)行相同操作的情況下的結(jié)果必須百分百一樣,這樣我們就不可以使用Unity自帶的物理引擎。(親測 會不同步)
因?yàn)椴煌琧pu和操作系統(tǒng)可能float的精度也不同 所以也要避免使用float。(雖然說float有固定標(biāo)準(zhǔn)的,按理來說現(xiàn)在不同平臺已經(jīng)都一樣了,但是謹(jǐn)慎起見我還是沒用float)
幀同步的核心邏輯也不可以寫在Unity腳本的生命周期里 例如Update
幀同步的大致實(shí)現(xiàn)原理
首先要區(qū)分 邏輯幀和渲染幀。
我們同步的是邏輯幀,所有位移和傷害判定什么的也都是在邏輯幀中,渲染幀中做平滑處理。
Unity中的幀就可以當(dāng)作是渲染幀。 每次Update就是一個(gè)渲染幀,每次FixedUpdate就可以當(dāng)作是邏輯幀。 渲染幀是沒有固定間隔的 性能高刷的就快間隔就短。而邏輯幀是必須固定間隔的。
我兩臺手機(jī)要創(chuàng)建房間進(jìn)行游戲。
首先手機(jī)A點(diǎn)擊創(chuàng)建房間,手機(jī)A啟動服務(wù)器,不停的把自己的IP和端口號 以及房間信息(人數(shù),地圖,模式。。。等) UDP局域網(wǎng)廣播給一個(gè)固定端口。 其他手機(jī)客戶端固定監(jiān)聽這個(gè)端口,收到房間信息的廣播就在UI界面中顯示。
廣播房間之后等待其他客戶端鏈接進(jìn)來,房間滿員之后 所有人開始加載地圖。
-
服務(wù)器收到所有人加載完畢消息之后,服務(wù)器開始循環(huán)記時(shí) 服務(wù)器來驅(qū)動邏輯幀。比如 1秒20邏輯幀 就是0.05秒驅(qū)動一幀,把收到的客戶端輸入廣播給所有人。(服務(wù)器的主要職責(zé)就是定時(shí)轉(zhuǎn)發(fā)輸入)。
image.png 客戶端在服務(wù)器開始計(jì)時(shí)之后 在每一個(gè)Unity渲染幀中都把自己的輸入發(fā)送到服務(wù)器,服務(wù)器每一個(gè)邏輯幀按照最后一次收到的輸入廣播給所有人。
客戶端發(fā)送Input數(shù)據(jù)

服務(wù)器接收Input數(shù)據(jù)

服務(wù)器廣播Input消息

- 客戶端自己本身不進(jìn)行邏輯幀計(jì)時(shí) 收到一個(gè)服務(wù)器發(fā)來的邏輯幀,先給所有角色設(shè)置input 然后再客戶端本地就按照固定的時(shí)間間隔更新物理 驅(qū)動邏輯幀(比如1秒20邏輯幀 就是每次更新物理0.05秒)


- 然后物理更新的時(shí)候 自己用事件管理器廣播一下PhysicsUpdate事件 其他地方監(jiān)聽物理更新事件。寫核心邏輯的地方 用物理更新的Update代替Unity的Update 就可以像寫單機(jī)游戲一樣做幀同步游戲了。
上面的例子中 服務(wù)器的那臺機(jī)器 如果渲染幀卡了一秒 而邏輯幀1秒20幀 ,那么就直接一個(gè)渲染幀中調(diào)用20次邏輯幀 追趕上進(jìn)度。 如果服務(wù)器卡住了 所有人也都會同步暫停。 如果你有需要 也可以做主機(jī)遷移 隨時(shí)接替原來的主機(jī) 防止房主掉線 大家一起掉線。
最終效果



預(yù)測回滾
無論幀同步還是狀態(tài)同步理論上來說都要做預(yù)測回滾,狀態(tài)同步做這個(gè)還好做一點(diǎn)。
預(yù)測回滾基本就是按照上一次的操作客戶端自動多模擬一幀或者幾幀 來抵消網(wǎng)絡(luò)延遲的感覺,但是如果預(yù)測結(jié)果和之后真實(shí)發(fā)生的結(jié)果不符的時(shí)候 就需要回滾客戶端到正常的結(jié)果上 然后再次預(yù)測。 (如果一個(gè)人在游戲里 反復(fù)左右移動 比如CS里對槍時(shí) 瘋狂ADADADADAD左右移動 這種情況下 如果有客戶端有預(yù)測回滾 就會瘋狂的預(yù)測錯(cuò)誤而回滾 客戶端也會增加額外的計(jì)算壓力)
預(yù)測回滾因?yàn)槲易约鹤龅男Ч懿缓?大概如下圖一樣,所以我就不講具體怎么做了。
(英雄聯(lián)盟的預(yù)測回滾做的也不咋樣,設(shè)置里面有個(gè)預(yù)測選項(xiàng)默認(rèn)是關(guān)閉的 手動勾選打開之后 就算網(wǎng)絡(luò)正常效果也跟下圖一樣。)

預(yù)測回滾的方案很多 并不是只有一種,像《戰(zhàn)地3》的預(yù)測回滾就特別魔性 你如果擊中敵人了 但是因?yàn)檠舆t導(dǎo)致服務(wù)器判定沒擊中,但是你客戶端已經(jīng)提示擊中了 再對其他人影響不大的情況下 它會讓其他所有玩家和服務(wù)器陪著他一塊回滾 讓子彈打中那個(gè)人。。。。 (難道這就是我快速躲進(jìn)掩體里之后還被打死的原因?)。
幀同步的作弊檢查
游戲過程中檢測
玩家每一邏輯幀執(zhí)行結(jié)束之后 把所有角色的關(guān)鍵數(shù)據(jù)加密為MD5 上傳服務(wù)器,服務(wù)器進(jìn)行比對。如果是所有人上傳上來的MD5都一樣 說明所有人的游戲結(jié)果都是同步的,如果有人修改了血量那么就會造成游戲不同步 自己的MD5和其他人的不一樣,服務(wù)器可以強(qiáng)行糾正它 或者 踢掉它。
事后檢測
游戲結(jié)束之后上傳整場比賽左右的操作數(shù)據(jù),服務(wù)器開一個(gè)客戶端 一瞬間跑完所有操作所有幀 看最終的結(jié)果與客戶端上報(bào)上來結(jié)果是否一致,如果不一致肯定有人作弊了。
不容易檢測的作弊
像透視 自瞄這種 本質(zhì)上沒有改變游戲數(shù)據(jù)的作弊 就不太容易檢測。
