UE4攝像機系統解析
在遊戲中,攝像機是玩家的眼睛,他控制了玩家的視點(POV即PointOfView,後面簡稱POV)位置以及玩家的視野大小(FOV即FieldOfView,後面簡稱FOV)。一句話,攝像機決定了我們去觀察這個遊戲世界。
遊戲的型別多種多樣,有第一人稱的FPS遊戲,有第三人稱的動作遊戲,還有需要統籌全域性來觀察的RTS遊戲。簡單來說,第一人稱就是把POV放在人眼睛的位置,第三人稱就是把POV放在人身後一定距離的位置,RTS就是把POV放到離地面很高的位置。這裡說POV要比說攝像機更為準確,因為有的時候攝像機只是一個為了方便大家理解與觀察的實體,其實他的位置可以隨意,不過為了便於大家理解與使用,我們一般將攝像機位置與視點位置同步。下圖的攝像機位置即為POV的位置,紫色的框即為FOV。
圖1-1值為90的FOV
圖1-2值為40的FOV
因為POV在遊戲中是會隨時變化的,所以我們需要在Tick裡面去更新他的位置。所以,從本質上講,調整攝像機就是不斷地更新POV的位置,這樣我們也能平滑流暢的觀察遊戲世界。如果,我們想要切換不同的視角,那就切換我們的POV。如果想要做一些鏡頭移動特效,就可以利用一些插值的演算法來處理POV的位置。如果想要做一些視覺特效,可以直接在攝像機上加一些後處理效果。總之,還是那句話,攝像機決定你如何觀察遊戲世界。
二.UE4攝像機關係梳理
首先放一張攝像機相關類的關係圖,
圖2-1攝像機相關類圖
下面開始一一分析各個類之間的關係。
由於大家一般會先去看官方文件,這裡就按照官方文件的順序來解釋。
CameraComponent元件一般放在控制角色的根節點上,常規的行為與屬性都可以在其中進行設定包括POV位置(即元件的位置),FOV,觀察模式(分為正交模式和透視模式),寬高比,後處理效果等。
而CameraActor的作用更為簡單,就是將CameraComponent元件封裝到一個Actor裡面來使其可以直接放到一個Level裡面。
圖2-2Mods裡面可以放置Camera
CameraComponent元件裡面還包括另外兩個元件,一個是UDrawFrustumComponent,另一個是UStaticMeshComponent。UStaticMeshComponent大家很好理解,用來渲染Actor的物件模型,而UDrawFrustumComponent是用來顯示攝像機視窗的。(圖1-1的紫框,只在編輯器下有)。
從名字上理解,我們可以叫他為攝像機管理器,那麼他管理的是什麼?答案就是視點POV。POV位置真正的計算過程是在這裡進行的,你可以在這裡處理不同人稱視角的POV計算流程,處理攝像機震動與碰撞,也可以對攝像機增加鏡頭粒子特效(濺血效果等)。
顧名思義,即觀察目標,也就是我們想讓攝像機跟隨的物件。對於一般的第三人稱遊戲,我們的攝像機一直是對準著我們控制的角色的,這個角色可以認為就是ViewTarget(不過ViewTarget不僅僅是一個Actor)。ViewTarget定義在PlayerCameraManager裡面,負責提供給Manger一個理想的POV。一個ViewTarget結構包含一個Actor物件,該Actor對應的POV資訊以及當前的PlayerState。換句話講,我們一般通過ViewTarget包含的Actor的位置來計算攝像機POV的位置,並把計算後的結果再儲存到ViewTarget裡面的POV。
圖2-3ViewTarget內部結構
2.3 CameraComponent,CamerActor與PlayerCameraManger
經過前面的介紹,我們知道PlayerCameraManager通過繫結一個ViewTarget來計算POV的位置。一般來說,我們將CameraComponent放進的Actor就是我們理想的ViewTarget。同理,擁有CameraComponent的CameraActor也同樣是ViewTarget。
PlayerCameraManager一般有幾個攝像機模式(CameraStyle),如下圖
而在構造時他預設的模式是另一個
預設模式下,PlayerCameraManager在更新POV的時候會呼叫Actor的CalcCamera。CalcCamera先判斷是否有CameraComponent並且bFindCameraComponentWhenViewTarget是否為true,是的話就獲取CameraComponent的位置與朝向,否則就獲取VIewTarget的Actor的座標與朝向來更新視點POV的資訊。
而其他的模式需要玩家去進一步做詳細的計算。
如果當前的ViewTarget是一個CameraActor的話,PlayerCameraManager就會直接獲取CameraComponent的位置與朝向來更新POV。
我們知道,在UE4裡面每個Pawn都會有一個對應的Controller,我們自然想讓Controller擁有控制Rotation(朝向)的功能(為什麼不控制Location?因為Location跟隨Viewtarget就可以了)。所以這裡有一個UpdateRotation函式來控制朝向。那麼他是如何影響到PlayerCameraManager裡面對視點的計算呢?
我們可以再回頭看一下圖2-1,通過觀察,我們可以看到一個PlayerController裡面有一個PlayerCameraManager屬性。當我們旋轉滑鼠時,這個偏移量會通過AddControllerYawInput與AddControllerPitchInput傳入RotationInput。在PlayerController執行UpdateRotation時會獲取PlayerCameraManager並通過ProcessViewRotation計算出移動後(也就是將RotationInput計算進去)的Rotation,把這個Rotation賦值給ControlRotation以及Controller所控制的Pawn(也就包括他身上的CameraComponent元件)。
圖2-4Controller接收滑鼠旋轉流程
最後,經過上面流程,我們的CameraComponent朝向已經被修改,PlayerCameraManager在更新POV的Rotation時獲取CameraComponent的朝向即可。(這個就是UE4預設情況下的攝像機處理流程)
假設我們沒有任何CameraComponent元件呢?這其實也很簡單,在圖2-1可以看到一個PlayerCameraManager類會有一個PlayerController屬性,這樣PlayerCameraManager就可以隨時獲取當前ViewTarget對應的PlayerController。而且我們在圖2-4的流程裡面其實已經通過SerControlRotation的獲取了當前攝像機計算後的朝向,所以在PlayerCameraManager更新POV朝向的時候,獲取Controller的ControlRotation既可。這樣,我們在更新POV朝向的時候就不需要獲取CameraComponent的朝向了,沒有藉助任何CameraComponent或是CameraActor。
注:我們看到圖2.4裡面最後的兩步是處理角色朝向的。經過該圖裡面的處理,玩家會隨著攝像機的旋轉而旋轉。但是目前很多流行的遊戲是通過玩家方向鍵來控制朝向的。
通過上面的介紹,我們基本上了解了攝像機相關類之間的關係。這裡再重新梳理一下,
如果我們想實現攝像機的控制,有兩個基本方案。
方案一、UE4官方教程,將一個CameraComponent放到我們想控制目標的身上。然後通過獲取攝像機元件的位置與朝向更新POV。
方案二、控制的目標可以任意,Viewtarget身上沒有CameraComponent。PlayercameraManager通過獲取ViewTarget的位置+一定的偏移(PlayerCameraManager有一個TPVCameraOffset和一個FreeCamOffset)來確定POV的位置。而對於朝向,PlayerCameraManager通過獲取PlayerController的ControlRotation既可。
我們要知道,PlayerCameraManager這裡的計算其實是偏底層一些的。有的時候我們不需要去修改這裡的計算也可以處理一些攝像機的設定,比如旋轉角度限制,自定義的計算POV朝向這些可以寫在PlayerController的CalcCamera裡,並在UpdateCameraRotation裡面呼叫。總而言之,我們需要弄清的就是視點POV的位置與朝向到底是如何計算的。
最後再總結一下,攝像機POV計算的要點有兩個
一、 你要保證你的計算過程是在Update裡面進行的
二、 你要在PlayercameraManager計算POV的時候能獲取到你更新後的位置與旋轉
有了上面兩個條件,你就可以平滑的更新POV的位置與朝向了。
注:到這裡,我們已經發現了兩個CalcCamera,一個在Actor裡面,是用來獲取Actor或者他身上的CameraComponent元件位置與朝向的。另一個是PlayerController裡的,用來進行自定義對攝像機POV進行處理計算的,預設是不呼叫的。
圖3-1攝像機更新視點POV流程
.
4.1 攝像機位置處理
2.4節我們簡單提到過一種攝像機的使用流程。遊戲初始化後,PlayerCameraManager會把玩家角色的Pawn設定為ViewTarget,然後獲取ViewTarget的座標作為一個基礎值。基礎值加上玩家身上的CameraOffset就得到了視點POV的位置。不過這裡的CameraOffset是相對於ViewTarget區域性座標系的偏移,還要根據當前的角色的朝向以及Mutiplier來做進一步處理。
如果想讓玩家在不同狀態下調整不同的視角,可以增加多個CameraOffset(如果射擊CameraOffset)。然後在不同的狀態下切換不同的CameraOffset即可。
4.1 攝像機FOV處理
在很多遊戲裡面,如果使用弓箭,槍一類的武器,可以進行瞄準。瞄準的實現方法就是修改攝像機的FOV。可以在武器上設定一個OverrideFOV屬性,通過在瞄準時更新當前的FOV可以實現瞄準的效果。
5.1 攝像機震動
圖5-1攝像機調整相關類圖
這裡我們把攝像機相關類圖再拿出來看一下。觀察到有一個繼承於UObject的UCameraModifer類,他的作用就是對攝像機進行相關的調整,這裡我們定義任意的Modify型別來繼承於UCameraModifer,但實際上我們最常用的就是UCameraModifier_CameraShake(即攝像機震動),他是真正負責執行攝像機震動的類。
在PlayerCameraManager更新POV資訊的時候,或根據條件判斷是否呼叫CamraModifier,在該類中有一個TArray<classUCameraModifier*> ModifierList;來儲存當前所有的Modifier。遍歷的時候如果找到Modifier就會執行對應的修改。預設情況下,引擎提供了一個CameraModifier_CameraShake的Modifier,在執行PlayerManger初始化的時候會通過APlayerCameraManager::CreateCameraModifier新增到ModifierList裡面。
同時,我們還可以看到一個UCameraShake類,這個類就是用來設定攝像機震動引數的類,包括震動時間震動幅度等。比如,在UGameplayStatics::PlayWorldCameraShake裡 面。我們就需要傳入一個TSubclassOf<classUCameraShake> Shake引數。PlayerController裡面也有一個ClientPlayCameraShake方法讓客戶端去呼叫,裡面也需要傳入UCameraShake型別。其實,執行攝像機震動的基本原理就是根據傳入的UCameraShake引數來新增一個FCameraShakeInstance。具體邏輯參考函式APlayerCameraManager::PlayCameraShake。
FCameraShakeInstance是攝像機震動例項,在UCameraModifier_CameraShake裡是以陣列成員ActiveShakes的形式存在的。每次執行APlayerCameraManager::PlayCameraShake的時候,都會新增一個攝像機的震動例項,當震動結束後,例項會被從ActiveShakes移除。預設的相機震動UCameraModifier_CameraShake只能執行震動邏輯,並不能修改玩家的Rotation,但是CameraModifier裡面預留了修改玩家Rotation的介面,UCameraModifier::ProcessViewRotation。我們可以通過修改CameraModifier的這個介面來在震動的同時調整玩家的朝向。
在遊戲裡面,我們可能呼叫攝像機震動的情況(及實現方法)有
1. 靠近大型NPC(在NPC的動畫藍圖裡面新增Notify,通知客戶端執行)
2. 開槍(在武器上繫結UCameraShake)
3. 玩家受傷
4. 揮動武器擊中目標時(在武器上繫結UCameraShake)
還有很多情況,根據專案需求
5.2 攝像機鏡頭粒子特效
在PlayerCameraManager類定義中我們可以看到這樣一個屬性,他通過在攝像機前面繫結一個粒子特效來實現滴血等效果。通過ClientSpawnCameraLensEffect_Implementation可以使用。效果如下
圖5-3CameraLensEffect實現效果
5.3 攝像機碰撞
攝像機的碰撞的邏輯可以寫在了PlayerCameraManager的UpdateViewTarget裡面(也就是更新視點POV資訊的地方),他需要在最後做一個盒型的碰撞檢測,如果碰到符合通道條件的物體就會更新視點的座標。
5.4 攝像機模式切換
前面介紹了UE4預設提供了幾種攝像機模式給開發者使用,我們常用的有自由攝像機、第一人稱、第三人稱等。旁觀者模式是類似一種自由攝像機的模式。
PlayerCameraManager裡面在更新POV時會判斷當前的CameraStyle,從而進行不同的計算,比如我們從第三人稱視角切換到第一人稱視角,POV相對玩家的位置就會改變(從背後變為眼睛附近)。
有一點也要注意一下,不同攝像機的碰撞檢測通道可能是不同的。
5.5 攝像機後處理
UE4裡面的後處理可以通過兩種方法完成(另一篇部落格簡單的介紹了後處理 UE4後處理簡述。)。第一種是通過在場景裡面新增PostProcessVolume,然後攝像機處於該體積內才能產生效果。第二種是在攝像機元件裡面新增與設定,這樣就不需要把攝像機放在某個特別的位置。前面圖4-3的第一張圖就是經過高斯模糊以及顏色混合的後處理效果。
5.6 攝像機平滑與延遲Lag
在大部分遊戲中,為了得到更好的遊戲體驗,攝像機的移動都是平滑的。因為攝像機是在每幀都進行更新,所以我們只要保持與我們的ViewTarget固定的距離,就可以得到平滑的效果。
但是如果我們想從當前攝像機立刻切換到另一個攝像機機位,或者我們的ViewTarget發生瞬移,這時候如果不做任何處理,攝像機就會突然的切換(有的時候這樣也沒什麼不好)。假如我們覺得這樣切換有點突然,我們只要簡單的處理一下就可以了。設定一個插值,讓當前的位置逐漸插值到目標位置。(FMath::VInterpTo(當前位置,目標位置, DeltaTime,速度);)
同理,如果想做一個攝像機延遲效果(就是玩家可能突然用技能走出一大段位移,為了體現效果,想讓攝像機慢慢的追上玩家),也可以使用類似的方法來實現。
如果有興趣,可以參考一下UE4第三人稱官方例程,然後找到彈簧元件,搜尋EnableCameraLag屬性,試試效果。當然也可以到程式碼裡面看看他實現的細節。
六.其他
1.UE4裡面的DoUpdateCamera會處理相機viewTarget切換,如果當前攝像機要切換到另一個就在這裡處理插值與混合,這個時候就不應該呼叫UpdateViewTarget。要切換的物件是PendingViewTarget,混合引數為BlendParams。
2.PlayerCameraManager是通過void APlayerController::SpawnPlayerCameraManager()生成的
3.把CameraManager的位置與旋轉與視點同步到一起。這樣,我們也可以通過攝像機的Manager獲取視點的相關資訊。同理,把PlayerController的朝向與視點同步,我們也可以獲取PlayerController的朝向來獲取視點的朝向。
4.下面流程圖描述了角色是如何被PlayerCameraManger設定為ViewTarget的。
圖6-1設定玩家控制的Pawn為ViewTarget流程圖
原文連結(轉載請標明):http://blog.csdn.net/u012999985/article/details/68947410