網路遊戲中子彈與單位的命中判定問題
電子遊戲中,子彈一經發射,就會在有效射程內作用於其運行軌跡上的單位。判定子彈是否能命中單位,只需看子彈和單位的碰撞體是否會在某個時刻處於重疊位置。
這是容易理解的,在單機遊戲中也是容易實現的。但在聯網遊戲中,由於網路時延的存在,子彈的命中判定則要複雜很多。
子彈命中單位的基本判定方法
一個子彈有如下主要組成部分:
1)飛行控制邏輯。飛行控制邏輯用於隨時間推移,計算和更新子彈的位置,最終得到一條飛行軌跡。
2)命中判定。子彈的命中,則是指子彈飛行過程中會碰撞每一個處于飛行軌跡上的單位,併產生作用效果。子彈和單位是否碰撞,要看子彈作用半徑和單位形狀,在邏輯上,又被稱為子彈碰撞體和單位碰撞體。
3)效果。在現實世界中子彈的命中效果往往是穿透。在遊戲世界中,子彈命中效果可以是穿透,也可以是直接產生AOE,改變飛行軌跡,甚至彈射等等。
為了性能,遊戲邏輯往往是離散的。這意味著,子彈的命中判定時機是離散的。如果子彈判定時僅僅使用判定時子彈和單位的空間位置,那麼一定會漏判中間某些時刻的命中。雖然Frame1和Frame2時刻,子彈和單位都沒有位置重疊,但是中間某個時刻卻有可能發生碰撞。
所以計運算元彈和單位是否命中時,需要考慮子彈和單位掃過的軌跡。嚴格的說,子彈和單位的運行軌跡不可能總是沿著直線,即使是相鄰的兩幀之內,運行方向也可能在時刻變化。要想得到命中的準確判定,有兩種方法。一種是得到子彈和單位各自運行軌跡的解析表達式,用解析的方法計算中間時刻是否可能命中。另一種是在中間插值,在更細的粒度上做命中判定。
但是這兩種方法都很麻煩,在場景中有數百顆子彈的情況下,計算量也很大。實踐中通常採用一些方法去簡化和近似。
1)如果幀間隔足夠短,那麼可以認為兩相鄰幀之間的運行軌跡是一條直線。
2)通常情況下子彈的飛行速度很快,而單位的移動速度很慢。所以計算命中時採用單位在當前幀的位置與子彈在上一幀內掃過的軌跡求交,通常就可以達到比較好的結果。
3)幀間隔較小的情況下,子彈在上一幀內掃過的軌跡與單位在上一幀內掃過的軌跡只要有重疊,就可以認為命中。那種由於飛行速度和初始位置差異導致先後掠過同一位置卻未碰撞的情況非常少見。而且由於子彈和單位都有碰撞體積,這種誤差通常是用戶可以接受的。
網遊環境下子彈和單位碰撞需要額外考慮的問題
同步會給網遊環境下子彈和單位碰撞的判定帶來額外複雜度。在一個簡單的C/S同步環境下,所有單位和子彈的運算都是服務端優先算,計算結果再同步到客戶端。客戶端的模擬總是延後於服務端的,是服務端的簡單重放。這種情況下,命中判定的方法和單機環境下並沒有本質不同。DOTA2等遊戲採用這種方法。
服務端領先於客戶端的壞處是,玩家的輸入必須等待一個RTT才會有反饋,雖然公平但體驗不好。有一些遊戲在客戶端向服務端同步輸入後,立刻播放相應的動作,可以給玩家即時反饋。這種反饋不會做位置和朝向的預計算,效果也不夠好。
最好的方法是玩家的位置在客戶端做預計算,這也是守望先鋒和暗黑3的做法。這種情況下,對於玩家單位來說,客戶端比服務端要先算。其他單位還是服務端先算。如果子彈也是服務端先算,上述的命中判定就會有額外的誤差。
對於玩家來說,子彈的位置需要經過RTT/2才會同步到客戶端,玩家的輸入需要RTT/2才會同步到服務端。玩家在客戶端看到的子彈和玩家單位的位置關係是(x1,y1),而服務端實際計算命中時子彈和玩家的位置關係是(x2,y1),這兩者是有可能不一致的。也就是說,服務端如果採用(x2,y1)計算命中,就會導致在客戶端,看起來命中了實際判定卻未命中,或者玩家做了個閃避動作,看起來躲開了子彈,但是服務端仍然判定命中。這樣的命中判定結果在伺服器看來並沒有錯,但是玩家體驗不好。
要解決這個問題也很簡單。玩家在客戶端看到的子彈和玩家的位置關係是(x1,y1),只要在服務端也採用(x1,y1)計算命中就可以。也就是說,服務端計算命中時,需要將子彈位置回滾RTT時間。
子彈同步的兩種方式
剛才說了玩家位置同步的兩種方式,即,服務端先於客戶端,或客戶端先於服務端。對於子彈來說,除了服務端先於客戶端算,也可以採用客戶端先行模擬的方式。守望先鋒就是這種做法。如果玩家單位和子彈都採用客戶端先於服務端計算的方法,服務端計算命中時也不需要將子彈位置回滾。
遊戲中,一定還存在必須採用服務端先於客戶端計算的單位,比如NPC等。子彈若在客戶端先行,如果要計運算元彈和NPC的命中關係,還是要考慮同步因素的影響。
客戶端先行模擬子彈有局限性。客戶端通常並沒有足夠的信息判斷是否生成某顆子彈,這時就必須在服務端先於客戶端模擬。若子彈在飛行過程中會改變軌跡和形態,服務端處理起來會更方便。此外,客戶端先行,服務端就必須做嚴密的防外掛處理。
子彈和玩家單位命中判定的設計取捨
剛才講到,玩家單位在客戶端先行模擬,子彈由服務端先行模擬,子彈和玩家單位命中判定時應該用玩家單位當前位置和子彈一個RTT之前的位置計算。
但是這個設計帶來的問題是子彈和玩家單位的碰撞,在其他玩家看來是不準確的。因為客戶端看到的子彈的位置和其他玩家的位置都是服務端同步過來的,如果客戶端要看到準確的命中結果,那麼在服務端,子彈就應該用其當前的位置與其他玩家的當前位置計算碰撞。
其結果就是,如果玩家和子彈的當前位置做碰撞計算,那麼客戶端看到的子彈和其他玩家的碰撞是和視覺吻合的。但是自己和子彈的命中判定看起來不準確。
如果玩家和子彈歷史位置計算碰撞,那麼客戶端看到的子彈和自己的碰撞是吻合的,和其他玩家的碰撞看起來卻是不準確的。
採用哪種策略,就看遊戲設計者如何根據實際情況做取捨了。
推薦閱讀:
※如何看待DNFIP發布會?
※《指環王Online》魔多版本 —— 端游比爛大賽又一匹黑馬
※選擇玩新的網遊跟選擇追求新的女友有什麼相似之處??
※如何讓大家知道《坦克世界》開通官方知乎賬號了?