1. 程式人生 > >UE4中藍圖實現小地圖——雷達圖篇

UE4中藍圖實現小地圖——雷達圖篇

前言

小地圖是遊戲中常見的組成部分。大概可以分為兩到三種。本文根據在實習期間修改的一個藍圖小地圖,將其中分為雷達圖世界圖兩種。
其中,雷達圖沒有世界地圖場景,以角色為中心,圖上標註了敵我NPC的朝向、位置以及任務點等內容。
世界圖只有角色,沒有其他目標,有世界地圖場景,世界地圖不移動,標註了角色位置,並實時移動。

雷達圖的實現

雷達圖由畫板兩部分組成。其中,畫板是最終的雷達圖UI部件。

首先在 Content 目錄下UI的相應位置建立 RadarMap 資料夾。再在 RadarMap 資料夾中建立 BP 資料夾和 Texture 資料夾。
在 BP資料夾 中,右鍵,建立新的User Interface / Widget Blueprint

,並命名為 UI_Point。雙擊開啟。

Designer檢視中,設定為 Fill Screen。在Canvas Panel 中新增 Image 控制元件,重新命名為PointImg,並將其 Anchors 設定為四周,且四周的Offset均為0。如下圖所示。
這裡寫圖片描述

在 Graph 檢視中,在“The MyBlueprint tab”中的 Functions 中新增新的函式 SetColor,函式具有一個Input,稱為 Color,型別為 Linear Color。

雙擊開啟 SetColor 的藍圖Graph,拖拽左側中的 Variables 中的 PointImg 進來,並給它新增“Set Color and Opacity”,把輸入與該函式的輸入相連即可。如下圖所示。

這裡寫圖片描述

這麼一來,地圖的組成部分“點”就算基本完成了。

畫板

畫板是雷達圖的核心內容。

首先,在BP資料夾下新建Widget Blueprint,命名為 UI_RMMovePanel。其中RM為RadarMap的縮寫。

先來張圖。

這裡寫圖片描述

圖中,在Hierarchy Tab中,Image、BgImg 和 ViewPort 其實是可以合為一張 Texture 放進來的。它們表示的是中間雷達圖上除了兩個較大的帶刻度的圓圈之外的其他內容。因為雷達圖永遠以角色為中心及方向的正向的,因此如果要使雷達圖具有羅盤功能,即顯示當前的飛機航向,則需要一個可以 Rotate 的 Ring (Image控制元件)。左側這四個(其實可以是兩個) Image 控制元件的Anchors 均為四周型,且四周的 Offset 均為 0 。整個UI的尺寸為 Custom,寬高均為400。

再來張該 Widget BluePrint 的 Graph 圖。

這裡寫圖片描述

這裡需要說明一下。該小地圖是比較定製化了的,因此其中的 AimSys,是玩家角色飛機的瞄準系統,其父類為 Actor Component。對世界中其他 Actor 的捕捉、識別等,由該 AimSys 完成。其主要功能是獲取世界場景中的所有敵機,並判斷哪些是在本機的射程距離內的,哪些是被本機導彈系統已經鎖定了的。因此,在其他應用場景下,這裡並不是必須的,可以由負責相應功能的類實現。

這其中很重要的是 Points 變數。是個剛剛完成的 UI_Point 類變數的 Array,UE4的4.12及以前版本還沒有其他的結構體,只有Array(陣列)或者Element(單變數)。但是已經足夠。

變數 ConvertRatio 用於控制對整個小地圖的縮放。

事件圖(Event Graph)裡的邏輯比較簡單。構建事件裡,獲取當前的角色(或者其他可以獲取整個場景中其他目標的類,可以是GameState,或者NPCManager之類的。),並把它 提升為變數 (Promote to variable)。Tick時鐘事件裡,主要完成兩件事,一是完成剛剛提到的羅盤方向刻度盤 ( Image 控制元件 Ring ) 的旋轉,再一個,就是對整個地圖上所有的點進行處理(Manage Points)。

那麼,我們來看 Manage Points 函式裡有些什麼呢。。。。來張圖。

這裡寫圖片描述

嗯,非常簡單。先把所有的點全部移除。然後再繪製上所有需要繪製的點。

那麼接下來,我們先看 Remove Points 函式。上圖片~

這裡寫圖片描述

嗯嗯,還是很簡單。如果 Points 陣列中有值的話,那麼就依次把 Points 裡的那些值(Image 控制元件)從他們的父節點(MovePanel)上移除(相當於把“點”那張圖片從整個雷達圖的圖片上去除)。完成後,將陣列 Points 清空。

將畫板清空之後再繪製上新的點,就完成了整個雷達圖的實時更新。那麼我們看 Draw Points 函式。來,圖。

這裡寫圖片描述

看起來很亂很複雜。。但其實還是很簡單。只有 Add Points 函式是重要的。在本專案中,只是重複呼叫Add Points 來新增顏色、紋理(style)或大小不同的目標點而已。而其他的幾個 Branch, 則是用來判斷角色的瞄準系統(相當於雷達探測器)有任何捕獲內容,而並非空陣列,以免產生執行時的 Warning 或者 Error。

這裡有個數組變數 Visible Tgts,是來自 Aim Sys 的。是一個自定義結構體。具體如下圖所示:

這裡寫圖片描述

所以,當瞄準系統從 GameState 或者 NPCManager 那裡獲取了所有的其他目標之後,將依次計算這些目標與玩家角色之間的距離,(Z軸)方位角(相當於從上向下俯視圖裡,目標與自己的連線 和 自己前進方向射線 的夾角),並根據距離和角度來得出目標的繪製等級(比如0為可見不可攻擊的;1為可見可攻擊的;2為當前導彈瞄準的)。至於這其中的具體演算法,此處不再贅述。這不是這篇文章關注的點。(後來發現這個結構體基本上是個廢棄的。。。Oh,no。只是,在雷達圖裡,並沒有用到這些值,但是在 Aim Sys 中,是用到了的。)

我們還是來看 Add Points 函式。它有幾個輸入,分別是目標陣列Tgts、點顏色Color、點樣式Style、點尺寸Size、角色自身的Z軸旋轉值(俯檢視中的航向)Self ZRot,以及角色自身的位置Self Pos。So,我們來看內部圖。

這裡寫圖片描述

哈哈。依然簡單。只是把陣列拆開,依次呼叫 Add Point 函式。而 Add Point 函式則比 Add Points 函式又多了兩個變數,分別是目標位置 Tgt Pos 和目標Z軸旋轉值(航向) Tgt ZRot。為了避免可能出現的執行時 Warning 或 Error,添加了對每個目標的有效性檢驗。

閒話少說,我們來看 Add Point 函式。直接上圖。看圖說話。

這裡寫圖片描述

嗯。。。這個函式內部就有點麻煩了。不過還算一目瞭然,首先建立一個點 Create Point,然後設定這個點的畫筆 Set Brush from Texture。因為建立的點,是之前我們做好的 UI_Point,那個 Widget BluePrint 類有個變數是 Image 控制元件 PointImg,可以通過這樣的函式來動態地設定該控制元件的紋理圖,然後接下來設定這個控制元件的渲染方向 Set Render Angle,這是為了顯示出目標的移動方向或者朝向。接下來,對整個 UI_Point 設定顏色 Set Color,設定大小 Set Size,以及 設定位置 Set Position。其中,設定大小和位置的時候需要把 UI_Point Slot as Canvas Slot。。(暫時我也沒弄清楚為啥要這麼做,只是知道應該這麼做。)

那麼,這裡面還有兩個自定義函式,分別是 Create Point 和 Location Convert。至於說 Set Render Angle那裡為什麼要用 Tgt ZRot - Self ZRot,為什麼Set Position要減去尺寸的一半。就不再詳述了,相信有點數學基礎知識的都能想通。

嗯。現在我們來看最後剩下的這倆,Create Point 和 Location Convert。先看純數學的 Location Convert 吧。請看圖片。

這裡寫圖片描述

首先,這是個純函式(Pure),也就是它不需要白色線的事件驅動,也不會產出驅動。然後,我們看,World Position 就是目標的世界座標,減去本機自身的世界座標,得到的將是一條由本機指向目標點的向量。那麼,去掉這條向量的 z 值,只看 x y 值的話,那麼就相當於是從上帝的視角俯視這個世界看到了雷達圖一樣。所以,除以 Convert Ratio,進行一定比例的縮小之後,繪製到雷達圖上就可以了。但是,這裡有兩點需要注意,分別是圖中用紅色框框起來的兩處。

首先是,剛剛那麼減出來之後,得到了一個座標,但是如果把它應用之後會發現,整個雷達圖是歪的,意思就是說,如果我開著飛機,我的右前方有架敵機,那麼在雷達圖上,它出現在了我的飛機的左上角,或者左下角,或者右下角。反正不是正確的本應出現在的右上角。所以要對上面減完並且縮小後的向量繞著Z軸(0,0,1)旋轉一下下,轉到合適的角度(90*n)(這個可以不斷地編譯、執行、然後檢視效果,再做相應修改)之後就可以啦。但是,需要注意的是,本機飛機也是在飛行,旋轉的,所以要把本機的Z軸上的選轉考慮在內。因此就最終得出了這樣的, Self ZRot * (-1) - 90 的結果。

另外,要在 X Y 的值上分別加 200。其實,這裡的200更好的方法是獲取 RootCanvas 的尺寸,然後尺寸的 x y 值除以2,加到這裡來。對,就是因為,如果不加這個200,那麼雷達圖相當於只有第一象限的那部分了。我們需要把雷達圖上的原點,從圖的左下角,挪到中心點上來。正是因為我們在建立這個 RMMovePanel 的時候,在 Design 視圖裡,把畫布設定成了 Custom 的 400 * 400,所以,這裡要分別加200 。

最後的最後,看看 Create Point。來,圖。

這裡寫圖片描述

首先獲取Player,然後建立一個上面做好了的 UI_Point 類,並將其提升為 Local Variable,並將其新增到 Root Canvas 下,作為子節點(這與 Remove Points 裡的 Remove from the Parent對應),設定 ZOrder 為 1,嗯嗯。記一下這裡,等會兒我們要出去看看視圖裡的 ZOrder。 最後把它新增進 Points 數組裡,並且返回該點。

這裡寫圖片描述

回到檢視窗口裡,我們可以看到,之前把 Image、BgImg、ViewPort分開的意義了。我們把其他幾項的眼睛閉上不看,只看ViewPort,和它的 ZOrder,意圖很明顯了。這是個遮罩,因為所有的點的 ZOrder 為1,上面那些 Image 控制元件的 ZOrder 為 0,所以,當目標點出了這個 ViewPort 的中間的圓圈空白範圍後,將被它的四角的藍色擋住了。

當然,有人會說,可以通過藍圖程式設計的方法實現遮罩,的確。我只是提供一種更懶的,更不需要計算機去計算圓圈判斷是否顯示的方法而已。

當然,還有人會說,要不要當目標跑出去之後,新增一個箭頭,指向目標的位置。就像下圖裡地圖最上方的箭頭這樣,它表示有個敵人在距離自己更加遠的地方,已經遠到出了小地圖的可視範圍了:

這裡寫圖片描述

這些都可以有。只要按照一定的規則呼叫 AddPoints 函式就可以了。

總結

其實最主要的思想就是,用一個 Widget Blueprint 類 作為 點,並且新增到另一個作為雷達圖的 Widget Blueprint 的子節點裡。並且不斷地進行刪除、重繪就能實現了。最終,要實現把 雷達圖 放到飛機上,應該不是很難的事情,搜一下應該都會有。如果有需要,可以評論,視情況決定是否寫篇這樣的說明吧。

這裡寫圖片描述

這是雷達圖最終的應用效果,顯示在飛機的雷達儀表顯示區。其中,帶圈的綠色目標(朝右(W))的那個就是當前正在被瞄準的,也就是圖中被紅色框框住的這個。

歡迎關注我的微信公眾號:VR_Tech。

公眾號剛剛起步,還十分不完善。更多內容,敬請期待。

這裡寫圖片描述

這裡寫圖片描述