現現在,精品遊戲競爭激烈,各大遊戲廠商都在爭相推出「3A」級品質的遊戲。而目前比較熱門的科幻、戰爭、動做等品類大多都有槍械射擊內容,所以,「玩槍」便成了很大一部分遊戲中常見的玩法。當前比較熱門的使命召喚系列、戰地系列、無主之地系列、戰爭機器系列還有最近流行的吃雞類遊戲(絕地求生、堡壘之夜)等等都是玩槍,核心玩法類型上都是屬於射擊類遊戲。所以作好武器與射擊體驗,還原逼真的射擊情景,便成了這類遊戲的核心賣點。html
目前的射擊遊戲主要分爲兩種,一種爲「第一人稱射擊遊戲」(FPS)與「第三人稱射擊遊戲」(TPS)。c++
第一人稱射擊遊戲是以玩家主視角進行的射擊遊戲。玩家再也不像別的遊戲類型同樣操縱屏幕中的虛擬人物來進行遊戲,而是身臨其境的主視角,體驗遊戲帶來的視覺衝擊,這就大大加強了遊戲的主動性和真實感。服務器
如上兩張圖分別是1999年發售的反恐精英( Counter-Strike )與2019年發售的使命召喚:現代戰爭 ( Call of Duty: Modern Warfare )。能夠看到,時隔20年,場景與槍械變得更加精細與逼真,界面變得精美與合理,但不變的是,屏幕中間都作有一個「準心」。函數
第三人稱射擊遊戲與第一人稱區別在於,屏幕上顯示的主角的視野不一樣,而且第三人稱中玩家控制的遊戲人物在遊戲屏幕上是可見的,於是第三人稱射擊遊戲增強調更強調動做感。測試
如上3張圖都是「幽靈行動:斷點」的遊戲畫面,能夠看到,在沒有瞄準時屏幕中央是沒有準心的,這時能夠將注意力集中在環境、角色動做、遊戲劇情等等。當打開瞄準時,就會出現屏幕中央的準心,還能夠經過Alt鍵切換第一人稱與第三人稱(第二張圖與第三張圖)。動畫
經過上述的介紹能夠看到,不管是第一人稱仍是第三人稱,屏幕中央都會有「準心」以方便玩家瞄準。那爲何要有準心呢?爲何有了準心以後,咱們在遊戲中就能夠瞄準目標呢?this
咱們在物理課程中瞭解過,若是拋去槍的複雜結構不談,槍的工做原理簡單來說就是,彈頭在槍管中受到推動力後作拋物線運動。spa
若是彈頭在運動過程當中碰撞到目標,則表示擊中;若未碰撞到目標,則表示未擊中。那麼咱們如何瞄準才能擊中目標呢?code
如圖所示,藍色的水平線則是瞄準線,表明瞄準方向;綠色是槍管軸心線,表明槍管的指向方向;紅色則是子彈的飛行軌跡。假定子彈每次從槍口中射出的速度是固定的,空氣阻力也是固定的,子彈受到的重力也是固定的,那麼按照上圖的瞄準方式,射擊命中的「遠交點」也是固定的。換句話說,用這把槍和這個瞄準角度的話,只能擊中距離槍口x米的目標。那麼若是咱們射擊同一方向上比x米更近或更遠的目標怎麼辦呢?orm
如上圖所示,分別爲步槍和狙擊槍的射擊距離調整方法。步槍中有瞄準距離刻度線,調節刻度便可;狙擊槍在瞄準高倍鏡中有刻度線,根據目標的距離,使用對應的瞄準刻度線便可;手槍由於射擊距離短(大部分射擊距離在50米之內),瞄準偏差較小,因此不用調節。
而在遊戲中,未開瞄準鏡的情形下,角色的持槍動做每每如上圖所示,將槍固定於肘關節處。爲何要這樣持槍呢?由於這樣持槍姿式能夠減少在第一人稱人下槍所遮住的視野,提高遊戲體驗,而這樣的持槍動做,在現實中是打不許目標的(由於沒有三點一線的瞄準)。在遊戲裏爲了能更加便捷開槍,所以加入了準心的概念。
有了準心以後,玩家就能夠更加方便地瞄準目標,從而得到更好的射擊體驗。但這種準心「指哪打哪」的遊戲效果是如何實現的呢?
我剛開始在UE4中實現子彈彈道時,想法很簡答,既然子彈是從槍中射出的,那麼子彈的飛行方向固然就是槍口的朝向。爲了使子彈可以擊中「準心」的位置,我調整了槍在手中的朝向。
<iframe src="//player.bilibili.com/player.html?aid=74612974&cid=127622995&page=1&high_quality=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" width="800" height="550"> </iframe>   通過反覆測試後發現,不管怎麼調,只能保證在某一固定射擊距離時,彈着點能與準心重合。當射擊距離改變時,子彈就沒法擊中準心位置,這是什麼緣由呢?
如上圖所示,假定玩家(攝像機)與槍處在同一水平面,從俯視的角度來看,玩家的準心永遠指向正前方(用紅色線條表示),而槍則在玩家左手或右手,其朝向(用綠色線條表示)與玩家朝向存在夾角Θ。在該夾角下,只有玩家與目標距離y時,彈着點才能恰好在準心上。不管怎麼調整夾角Θ,都只有某一個距離下彈着點恰好落在準心上(由於兩條不平行的直線永遠只有一個交點)。而實際上槍與玩家(攝像頭)並不在同一水平面,在三維座標系下,兩條不平行的直線最多有一個交點(可能沒有交點)。
那麼該如何解決該問題呢?既然兩條線只有一個交點,那麼能不能根據不一樣的射擊距離改變槍的朝向,使交點始終在準心瞄準位置呢?
要想調整槍的朝向,就必須調整槍在玩家動畫的骨骼中的相對位置。並且要根據射擊距離實時調整(玩家準心瞄到的障礙物的距離),這個 運算量會很是大,幾乎是不可能實現的,因此這條路走不通,該怎麼辦呢?
既然槍的方向不方便調整,子彈的方向總能夠控制吧!能夠在開槍時,先根據玩家(攝像機)的朝向,作射線檢測,肯定目標彈着點,而後再控制子彈的飛行方向爲槍口飛向彈着點,這不就能夠實現不管玩家瞄向哪裏,子彈均可以擊中「準心」的位置的需求嗎。
如上藍圖代碼所示,用攝像機方向(即準心的朝向)作射線檢測,返回擊中的彈着點座標與材質(方便後續播放擊中特效),若未擊中任何物體(例如朝天上開槍),則返回射線檢測的終點,方便客戶端展現彈道。
如上藍圖代碼所示,得到彈着點座標與材質後進行廣播,在全部客戶端上播放該玩家的開槍動畫(武器特效),並調用C++函數,利用GamePlayTask生成彈道。
void AWeapon::GenerateBulletTrack(FVector HitLocation, UPhysicalMaterial* HitMaterial) { // 槍口座標 FVector MuzzleLocation = Mesh->GetSocketLocation("Muzzle"); // 向量方向:終點 - 起點 FVector Direction = HitLocation - MuzzleLocation; // 飛行速度 FVector Speed = Direction.Rotation().Vector() * 10000; // 飛行時間:距離 / 速度 float Time = Direction.Size() / Speed.Size(); // 擊中點特效 UParticleSystem* HitFX = nullptr; // 擊中點材質 if (HitMaterial) { EPhysicalSurface SurfaceType = HitMaterial->SurfaceType; // 該槍已設置該材質類型的擊中特效 if (VarHitFXs.Contains(SurfaceType)) { HitFX = VarHitFXs[SurfaceType]; } } // 運行子彈軌跡的GamePlayTask UBulletTrackTask * TrackTask = UBulletTrackTask::InitBulletTrack(BulletTrackComponent, MuzzleLocation, Speed, TargetFX, HitFX, Time, this, HitLocation); TrackTask->ReadyForActivation(); }
如上C++代碼所示,根據擊中點座標,計算出子彈的飛行方向與距離,而後計算出飛行時間,並用這些參數開啓子彈軌跡的GamePlayTask,用於顯示客戶端的子彈軌跡及擊中特效。
void UBulletTrackTask::TickTask(float DeltaTime) { // 記錄飛行時間 ActiveTime += DeltaTime; UWorld* World = GetWorld(); check(World); const FVector OldLocation = CurrentLocation; // 勻速距離公式:距離 = 速度 * 時間 FVector MoveDistance = CurrentVelocity * DeltaTime; // 新位置 CurrentLocation = OldLocation + MoveDistance; // 更新軌跡特效的位置與朝向 if (TrackComponent) { TrackComponent->SetWorldLocationAndRotation(CurrentLocation, UKismetMathLibrary::MakeRotFromX(CurrentVelocity.GetSafeNormal())); } // 超過飛行時間 if (ActiveTime >= LifeTime) { // 有擊中特效 if (HitFX) { // 顯示擊中特效 UGameplayStatics::SpawnEmitterAtLocation(World, HitFX, HitLocation); } EndTask(); } }
經過運行該子彈軌跡的GamePlayTask,每幀會更新子彈軌跡特效的位置,顯示子彈的飛行過程,若是超過了子彈的飛行時間,則在服務器已計算出的擊中點產生擊中特效(受傷害特效)等等,實現了子彈老是能擊中游戲準心瞄準的目標。
<iframe src="//player.bilibili.com/player.html?aid=74613873&cid=127622995&page=1&high_quality=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" width="800" height="550"> </iframe> # 展望
上述解決方案是彈道實現的較簡化版本,由於沒有考慮子彈的飛行時間、子彈重力、槍械後坐力、空氣阻力等諸多因素。若是考慮這些因素的話,服務器就不適合直接用射線檢測來計算彈着點,而是一樣改用GamePlayTask來實現,以便可以精確計算子彈飛行過程當中的受力、飛行碰撞等等問題。但這樣作勢必會增長服務器中彈着點計算耗時(由於會增長子彈飛行時間),加大客戶端的表現困難(讓玩家感覺到開槍有延時),這也是該方案後續的難點。