【Unity】製作手遊的幀同步時遇到的問題

 網絡遊戲的同步可以做的很簡單,也可以做的很複雜。簡單來說就是通過Http或者Socket來跟服務器同步數據。而如果往復雜了說,可以有p2p、幀同步、航位預測等等高級課題可以研究。

        因爲我們的項目需求----幾百個獨立單位的實時同步(帶pvp的rts),所以研究了下幀同步。不過後來發現它其實有很多問題,所以具體如何還要看其他同事的研究成果。以我個人來說,修改遊戲方案反而是最合理的解決方案。

        首先從需求上來說,幾百個獨立單位的作戰,本身結果和士兵強弱從策劃的角度來看就是不可控制的。玩家更加不可控制。比如士兵的攻擊力增加10%或者弓箭手的射程增加50%,帶來的影響可能是幾倍於預想值。這樣一個不可控制的遊戲對玩家而言,即便再獨特,其吸引力也不如卡牌遊戲。 從這點上來說,項目的需求本身就是值得商榷的。

        幀同步的原理和實現可以參考這些文章:

http://clintonbrennan.com/2013/12/lockstep-implementation-in-unity3d/

http://blog.sina.com.cn/s/blog_674f1bd20101omv7.html

       大意是,遊戲運行時以10fps(100毫秒間隔,具體數值可根據實際情況調整)運行一個邏輯幀,邏輯幀負責物理、ai、攻擊判定等等。而動畫和實際位移由渲染幀負責。這樣動畫表現是流暢的。而客戶端每邏輯幀都會與服務器進行通信同步客戶端的操作,當操作同步完成客戶端的邏輯幀可以繼續向後模擬。比如運行第3幀要確保第一幀的數據是完整的。雙方客戶端接收的操作內容是一致的,初始狀態是一致的,所以運行的結果也應該是一致的。隨機數可以使用確定隨機種子的僞隨機數來解決。

       所以關鍵問題就是要確保客戶端以同樣的輸入可以獲得同樣的運行結果。 這個是理論上可行的,很多rts遊戲也都是基於此原理。比如魔獸爭霸3  最高指揮官2  全面戰爭  星際爭霸 帝國時代 等等。  以最高指揮官(Super Commander)爲例,其全部單位有上千個,如果要實時同步他們的狀態以現在的網絡條件都是很困難的,所以這些rts同步的僅僅是操作,即點擊了什麼位置,點擊了什麼按鈕,選擇了什麼目標,按了什麼按鍵。客戶端接收到對應的操作,進行同樣的模擬,達到同樣的輸出。從而完成網絡同步。

       魔獸爭霸的錄像機制也是基於此原理,記錄操作,而不記錄狀態。所以幾十分鐘的遊戲,其戰報只有幾百k。同樣因爲這個原理,所以在出bug的時候(概率不高,但是玩家多了,還是很容易碰到的)會造成錄像與實際玩的時候不一致。比如玩的時候劍聖暴擊殺死了對手,從而翻盤取得了經典的勝利,但是戰報播放的時候卻沒有發生暴擊,直接被對方給推了。  全面戰爭也是如此,所以也會出現戰報跟實際遊戲不一致的情況,不過概率確實不大,只有發生極限情況纔可能出現這樣的問題。

       但是,如果拿Unity實現一個RTS手遊,要實現確定性的模擬就非常困難了。因爲Unity中的Start Awake Update等函數的調用是不受控制的,協程、SendMessage、Invoke也是不受控制的,動畫事件更加不受控制,物理使用Physic也是不保證確定性模擬的,即OnTriggerEnter這些函數的調用或者RigidBody的運動都可能存在誤差。一開始只是非常小可以忽略的誤差,而誤差可能逐漸積累最終的蝴蝶效應就是戰鬥結果完全不一致。比如一開始只是一個單位的座標偏差了0.1米,但是可能影響到一個箭有沒有射中它,進而影響到它究竟死沒死,如果單位的死亡產生不一致了,那麼後續的戰鬥就完全錯亂掉了。

        這個就是幀同步的過程中的“不同步”現象。帝國時代和最高指揮官會在每邏輯幀計算當前所有單位的屬性的crc值,幾個客戶端之間比較這個crc值,如果發現不一致,那麼就是不同步現象。此時可以把不同步的客戶端踢出遊戲,也可以強制同步某個單位或者是所有單位的狀態。產生不同步現象的原因可能很多,比如機器卡了一下,網絡卡了一下。無論什麼原因,但是確實是出Bug了,悲劇的是,這個Bug是非常難查的。可能是使用某個特殊技能造成一個特殊效果的時候,恰好殺死了對方,這種情況下會出Bug。也可能是錯誤的使用了被內存池回收的數據,如果此時此數據已被重新分配出去,那麼就會出現Bug,如果沒有被重新分配出去,那麼可以“正常”的運行。 當遊戲越複雜,這種Bug可能越多,並且幾乎不可查,因爲只有當大量玩家玩的時候纔會暴露出這樣的問題,QA根本無法重現這個Bug。 即便不是幀同步,“確定性”模擬,這類Bug都是最讓人頭疼的,可能會“無法重現”幾年,更何況對精確度要求非常高的時候。

        實現一個“確定性”模擬的客戶端,其難度很高,並且維護成本非常大。每個添加的技能都要深思熟慮,並且要花大量的時間去測試。每個新添加的代碼都可能造成不同步現象,除非戰鬥部分寫好後一百年不動,否則無論是重構還是添加新功能都會戰戰兢兢,如履薄冰。

        不光是Unity本身的問題,底層和上層都需要做相應的處理。

        底層要保證浮點數的“確定性”,這個本身就是一個高級課題,保證後還要考慮修改後的效率問題,畢竟手游上單位多了,簡單ai也會成爲大問題,如果浮點數計算的效率低了,很可能會大大的降低運行效率。數據結構如List也可能會有不穩定排序的問題。

        上層的ai邏輯也要精心設計,以儘可能“準確”的描述來執行行爲,比如a單位移動到b點,攻擊c,這樣的ai執行起來相對可靠。而如果是a追蹤b單位,直到x單位進入攻擊範圍則開始攻擊,這個就埋下了很大的隱患,除非所有的計算和調用都是確定性的。

        

         以手游來說,以幀同步來實現戰鬥同步還有一些副作用。比如要保證客戶端完全匹配,無論是配置還是版本,差一點兒都沒有辦法一同遊戲。所以無論是安裝包還是配置,只要有修改就必須要讓玩家更新,否則不能進入戰鬥。  而不同設備、不同cpu,是否會造成不同的結果,這個不能百分百的肯定,也就是說,遊戲的兼容性有很大的隱患。


         總結一下就是,現在手游上使用幀同步來處理rts並不現實,迄今爲止我並沒有看到成功的例子。 而早期的rts遊戲,可能因爲遊戲相對簡單,自研引擎相對可控,再加上大量人力物力的投入,這些跟我們手遊使用Unity快速開發遊戲的模式並不相同。 而除開rts,我並不認爲其他的遊戲形式如ARPG、橫版格鬥等等需要使用幀同步,正常的行爲和狀態同步足夠了。注意,幀同步的確定性模擬這個需求是個大難題,並不是幀同步本身有多難。也並不是說不使用幀同步,客戶端就不能自己運行邏輯,進行動作先行或者是預測。



參考資料(這些資料都相當經典,強烈推薦閱讀):

1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond
http://www.gamasutra.com/view/feature/3094/1500_archers_on_a_288_network_.PHP       

Syncing System of TA spring
https://springrts.com/wiki/Syncing_System

Opinion: Synchronous RTS Engines And A Tale of Desyncs
http://www.gamasutra.com/view/news/126022/Opinion_Synchronous_RTS_Engines_And_A_Tale_of_Desyncs.php

SYNCHRONOUS RTS ENGINES 2: SYNC HARDER
http://forrestthewoods.com/synchronous-rts-engines-2-sync-harder/

Cross platform RTS synchronization and floating point indeterminism
http://gamasutra.com/blogs/MaksymHryniv/20150107/233596/Cross_platform_RTS_synchronization_and_floating_point_ind

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章