HexMap學習筆記(二)——單元格顏色混合

遊資網發表於2019-03-05
HexMap學習筆記(二)——單元格顏色混合


HexMap學習筆記(一)——建立六邊形網格
HexMap學習筆記(二)——單元格顏色混合
HexMap學習筆記(三)——海拔高度與階梯連線

HexMap學習筆記(四)——不規則化
HexMap學習筆記(五)——更大的地圖

前言

這裡是HexMap系列教程的第二篇翻譯,難度與上一篇相當。

本篇主要內容是對單元格邊界做一個顏色混合,用到了更為細緻的三角剖分方式和一般很少用到的頂點顏色的應用。

本篇原文地址:Hex Map 2

本篇難度:★☆☆☆☆

單元格顏色混合

此教程是六邊形地圖的第二部分,上一篇教程中我們構建了六邊形網格的基本結構並實現了編輯單元格顏色的功能。現在每個單元格都是一個單色,在單元格之間的顏色轉換顯得很突兀。這次我們將引入一個過渡區域,讓相鄰單元格之間的顏色進行混合。

HexMap學習筆記(二)——單元格顏色混合
類似擦除顏料留下的汙漬一般的單元格顏色過渡

1.相鄰單元格

在混合顏色之前,首先得知道哪些單元格是彼此相鄰的。每個單元格都有六個可以通過方向識別的相鄰單元格。這些方向分別是東北(NE)、東(E)、東南(SE)、西南(SW)、西(W)、西北(NW)。為此新建一個列舉型別放入指令碼檔案中。

HexMap學習筆記(二)——單元格顏色混合

什麼是列舉?
使用enum關鍵字來定義列舉型別,是有名字的有序列表。這種型別的變數可以使用這些名稱中的一個作為它的值,每個名稱都對應一個數字,預設情況下從0開始。當你需要有限長度的可命名有序列表時,這些非常有用。
實際上列舉就是簡單的整數。你可以對它們進行加、減操作,轉換成整數再轉回來。同樣也可以轉換成少數其他型別,但整數是其基本型別。

HexMap學習筆記(二)——單元格顏色混合
六個相鄰單元格,六個方向

在HexCell中新增一個陣列來儲存相鄰單元格。雖然能設定其為公共欄位,但基於為後面考慮還是設定為私有,並用一個方法為其提供方向引數來獲取它們。確保陣列欄位為序列化的以便能在程式碼編譯後能顯示出來。

HexMap學習筆記(二)——單元格顏色混合

需要去儲存相鄰單元格的引用麼?
我們也能通過座標去確認並獲取相鄰單元格。但直接儲存引用關係更直接和方便一些,所以就這麼做。

相鄰單元格的陣列顯示在了指令碼的檢視皮膚上。因為每個單元格可以有6個相鄰單元格,就把HexCell預製體上陣列預設長度設定為6。

HexMap學習筆記(二)——單元格顏色混合
預製體為容納鄰居建立的容器

現在新增一個公共方法在每個方向上去獲取相鄰單元格。因為方向列舉轉換成的整數肯定是在0-5之間,所以這裡不用去檢查陣列下標是否可能越界。

HexMap學習筆記(二)——單元格顏色混合

同樣新增一個方法去設定相鄰單元格。

HexMap學習筆記(二)——單元格顏色混合

相鄰單元格之間的相對關係是雙向的,所以當在一個方向上設定了相鄰單元格,在反方向上也同樣成立。

HexMap學習筆記(二)——單元格顏色混合

HexMap學習筆記(二)——單元格顏色混合
相鄰單元格的相對方向是相反的

當然,這是假設我們有一個方法能獲取指定方向的相反方向(Oppositie())的情況下。我們可以在HexDirection裡去新增一個擴充套件方法去實現這個功能。獲取相反方向相當於在前三個方向上加上3,後三個方向上減去3。

HexMap學習筆記(二)——單元格顏色混合


什麼是擴充套件方法?
擴充套件方法是一個靜態類中的靜態方法,但使用起來像是某些型別的例項方法。這個型別可以是任何東西:自定義類、介面、結構體、原生資料結構或者是列舉。擴充套件方法的第一個引數之前必須有this關鍵字,它定義方法將操作的型別和例項的值。
這個特性允許我們在任何東西上新增方法,就像是靜態方法的引數可以是任何型別。在有節制適量使用的情況下這個特性非常方便。但過渡使用會造成程式碼結構的混亂。

1.1連線相鄰單元格

我們可以在HexGrid.CreatCell裡初始化單元格之間的相鄰關係。當我們從左到右逐行檢視單元格時,我們知道已經建立了哪些單元格。這些是我們可以連線的單元格。

最簡單的是E-W向的連線,每一行的第一個沒有E向相鄰的單元格,而其他的都有。由於相鄰單元格是在遍歷到這裡之前就建立好的,所以可以直接連線它們。

HexMap學習筆記(二)——單元格顏色混合
建立單元格時連線由東向西方向的單元格

HexMap學習筆記(二)——單元格顏色混合

HexMap學習筆記(二)——單元格顏色混合
東西方向的單元格已經相連

還有另外兩個雙向連線需要建立,因為這些連線是在不同行之間,我們只能通過之前建立的行連線起來。這意味著第一行被完全跳過了。

HexMap學習筆記(二)——單元格顏色混合

由於每行之間的連線時呈鋸齒狀交錯,所以需要不同的處理方法。首先先處理偶數行,它們之間有SE方向的連線關係,先連線這個方向。

HexMap學習筆記(二)——單元格顏色混合
連線每行之前由西北到東南方向的單元格

HexMap學習筆記(二)——單元格顏色混合

"z&1"是什麼意思?
大家都知道"&&"是布林運算子裡的"與"運算,"&"就是"按位與"。它們的執行邏輯相同,但後者是對於每一位進行運算。
兩個Bit都是1,與運算的結果就是1。例如:10101010&00001111結果是00001010。
從計算機原理層面上來說,所有的數字都是用二進位制表示的。二進位制中的1、2、3、4寫作1、10、11、100。如你所見,表示是否為偶數的最低有效位是第一位。
我們把二進位制的與運算作為一個遮罩,忽略除了第一位之外的其他位數,如果結果是0,那就表示這是一個偶數。

現在就可以連線SW方向的單元格了。除了每一行的第一個單元格,它們SW方向是空的。

HexMap學習筆記(二)——單元格顏色混合

奇數行遵循著相同的邏輯,除了映象關係。執行完一次這個方法後,網格內所有的單元格都關聯了起來。

HexMap學習筆記(二)——單元格顏色混合

HexMap學習筆記(二)——單元格顏色混合
所有的相鄰單元格都連線上了

當然,不是每個單元格都精準的與六個單元格相鄰。網格邊緣的單元格最少有兩個,最多有5個相鄰單元格,這應該是在之前就意識到的。

HexMap學習筆記(二)——單元格顏色混合
每個單元格所擁有的鄰居個數

2.顏色混合

顏色混合會使三角化每個單元格的過程更加複雜,所以我們把三角化的程式碼單獨分開。因為我們現在能通過方向獲取相鄰單元格了,所以在程式碼裡替換原來用座標獲取的部分。

HexMap學習筆記(二)——單元格顏色混合

獲取角度也換成用方向獲取,這樣應該比用座標獲取好用。

HexMap學習筆記(二)——單元格顏色混合

但在這之前需要在HexMetrics裡新增兩個靜態方法。這樣做還有個額外的好處:我們可以設定corners陣列為私有變數了。

HexMap學習筆記(二)——單元格顏色混合

2.1讓每個三角形有多種顏色

現在HexMesh.AddTriangleColor方法只有一個顏色引數,所以每個三角形只能有一個固定的顏色。我們新增兩個引數讓三角形的每個頂點都有不同的顏色。

HexMap學習筆記(二)——單元格顏色混合

現在可以開始混合顏色了!一開始就簡單的用相鄰單元格的顏色作為其它頂點的顏色。

HexMap學習筆記(二)——單元格顏色混合

又不幸地產生了一個空引用異常,因為不是每個單元格都有六個相鄰單元格。當沒有鄰居時就用自己的顏色代替。

HexMap學習筆記(二)——單元格顏色混合

"??"是什麼意思?
這被稱為空合併運算子,簡單來說"a??b"即"a!=null?a:b"的簡寫。
這裡有個小詭計,因為Unity在把一個東西與元件比較時會自定義,(注:Unity中判斷一個物件不為空時可以直接寫成if(someThings),這在C#原本語法裡是沒有的)而這個運算子會繞過它並直接與null比較,不過這是銷燬物件時才需要考慮的問題。

HexMap學習筆記(二)——單元格顏色混合
錯誤的顏色混合

座標標籤去哪了?

它還在那,不過截圖時把UI層隱藏了。

2.2顏色平均化

顏色混合其效果了,不過顯然現在的效果不能接受。兩個單元格之間的邊界的顏色應該是這兩者的平均值才對。

HexMap學習筆記(二)——單元格顏色混合

HexMap學習筆記(二)——單元格顏色混合
邊緣混合

現在我們混合了兩個邊界點上的顏色,依然得到了顏色分界線的形狀。這是因為六邊形的每個外部頂點總共有3個六邊共享。

HexMap學習筆記(二)——單元格顏色混合
三個相鄰單元格,四種顏色

這表示還得考慮上面和下面的相鄰單元格。最終得到的應該是四種顏色,每組三種。

新增兩個額外的方法到HexDirectionExtension中,讓我們能方便的跳轉到上一個和下一個方向。

HexMap學習筆記(二)——單元格顏色混合

現在我們可以獲得所有的三個相鄰單元格,然後執行兩次三向顏色混合。

HexMap學習筆記(二)——單元格顏色混合

HexMap學習筆記(二)——單元格顏色混合
角落混合

現在除了邊緣上的單元格,其他位置得到了正確的顏色變換。邊界單元格因為與丟失的單元格顏色不一致,所以仍然能看到那裡的邊界顏色。但是總的來說,目前效果仍不理想,需要一個更好的策略。

3.混合區域

混合六邊形的整個表面色導致雜亂的模糊效果,你再也不能清晰的識別出每一個單元格。我們能通過只混合靠近邊界的區域來改善這一點,這可以讓內部的六邊形保持固定的顏色。

HexMap學習筆記(二)——單元格顏色混合
顏色混合區域的固定核心

混合區域相對固定區域的佔比會生成不同的視覺效果,我們把這個區域定於為外徑的一部分。先設定其為75%,這就新增了兩個常量,兩者加起來是100%。

HexMap學習筆記(二)——單元格顏色混合

利用新的要素,建立獲取內部固定顏色的六邊形的方法。

HexMap學習筆記(二)——單元格顏色混合

現在修改HexMesh.Triangulate方法讓它變成獲取內部固定色六邊形的角。先暫時不改變顏色。

HexMap學習筆記(二)——單元格顏色混合

HexMap學習筆記(二)——單元格顏色混合
中間的固定區域六邊形,沒有邊緣

3.1三角化混合區域

現在通過細分三角形來填補空缺,它在每個方向都是一個梯形。我們可以用四邊形來表示,建立一個方法新增這個四邊形和它的顏色。

HexMap學習筆記(二)——單元格顏色混合
梯形邊緣

HexMap學習筆記(二)——單元格顏色混合

重寫HexMesh.Triangulate方法,現在三角形獲取固定的顏色,梯形部分混合這個固定色和兩個角落上的顏色。

HexMap學習筆記(二)——單元格顏色混合

HexMap學習筆記(二)——單元格顏色混合
梯形邊緣顏色混合

3.2邊界連線橋

現在看起來好些了,但是還沒完。兩個邊界的顏色混合受到了相鄰單元格邊界的影響。為了避免這一點,我們要把梯形的兩個角切開變成矩形。現在它變成了連線相鄰單元格的橋,在側面留下了缺口。

HexMap學習筆記(二)——單元格顏色混合
邊緣連線橋

在一開始就能通過v1和v2獲取v3和v4的位置,然後沿著橋移動到邊緣就能計算出座標。v3和v4在邊緣上的偏移量可以通過取相鄰角的中點來獲取,然後乘以混合比例的常量。這些應該是HexMetrics的範疇。

HexMap學習筆記(二)——單元格顏色混合

回到HexMesh裡,把AddQuadColor的顏色引數改為兩個比較合理。

HexMap學習筆記(二)——單元格顏色混合

修改Triangulate,現在它就能在相鄰單元格之間建立正確的混合連線橋。

HexMap學習筆記(二)——單元格顏色混合

HexMap學習筆記(二)——單元格顏色混合
正確的連線橋顏色混合與角落的缺口

3.3填充間隙

現在當三個單元格交匯時,通過切分梯形得到一個三角形的洞,下一步是把這些間隙填充回去。首先考慮與前一個相鄰單元格連線的三角形。它的第一個頂點具有單元格的顏色,第二個頂點的顏色是三色混合,最後一個頂點的顏色和連線橋顏色的一半相同。

HexMap學習筆記(二)——單元格顏色混合

HexMap學習筆記(二)——單元格顏色混合
快完成了

最後,另一個三角形也是一樣的方法,除了它是第二個頂點與連線橋相接,而不是第三個頂點。

HexMap學習筆記(二)——單元格顏色混合

HexMap學習筆記(二)——單元格顏色混合
完全填充

現在我們得到了不錯的混合區域效果,我們可以指定任意大小,由你自己決定模糊還是直接的邊緣顏色過渡。你可能會注意到網格邊界附近的混合仍然是不正確的。還是暫時不管這個問題,把注意力集中在另一件事上。

但是顏色轉換依然很難看?
這是線性顏色混合的極限了。事實上純色的效果不是很好,未來的教程中將會升級到地形材質並做一些更漂亮的混合。

4.邊界合併

觀察一下網格的拓撲結構,有哪些不同的形狀?如果我們忽略邊框,能識別出3種形狀:固定色的六邊形、兩種顏色混合的矩形和三種顏色混合的三角形,無論是否有三個單元格交匯都能看到。

HexMap學習筆記(二)——單元格顏色混合
能看到的三種形狀

所以每兩個六邊形由一個矩形橋連線起來,每三個六邊形由一個三角形連線起來。而我們現在用一種更復雜的方法在進行三角剖分:用兩個四邊形連線一對六邊形,用三個三角形連線3個六邊形。這樣似乎有些過於複雜了,如果我們直接用單個形狀相連,甚至顏色平均都不需要了。這樣就能用更少的複雜性,更少的工作量,更少的三角形實現這個功能。

HexMap學習筆記(二)——單元格顏色混合
比需求更為複雜

我們為什麼不在一開始就這麼做?
你可能會在你的一生中問許多次這個問題,但這是馬後炮。這是一個程式碼以邏輯方式演進的例子,直到獲得了新的見解,從而產生了新的方法。這樣的頓悟常常發生在你認為你已經搞定的時候。

4.1直接的連線橋

邊界的連線橋現在是兩個四邊形組合而成,要直接用一個四邊形連線到相鄰單元格,需要把橋的長度增加一倍。這意味著不再需要平均化HexMetrics.GetBridge裡的兩個角了,而變成直接相加並與混合區域佔比常量相乘。

HexMap學習筆記(二)——單元格顏色混合

HexMap學習筆記(二)——單元格顏色混合
所有的連線橋都交叉重疊

現在一個橋直接連線兩個六邊形,但依然生成了兩個連線處,在每個方向上都有一個橋被覆蓋,所以現在兩個相鄰單元格只有一個需要建立連線橋。

先簡化我們的三角化程式碼,刪除所有邊緣三角形與顏色混合的部分。然後把新增四邊形連線橋的程式碼移動到新方法中。傳遞前兩個頂點到這個方法中,這樣就不需要再推導它們了。

HexMap學習筆記(二)——單元格顏色混合

現在可以很容易的限制三角化連線,處理NE方向的連線時受限只新增一個連線橋。

HexMap學習筆記(二)——單元格顏色混合

HexMap學習筆記(二)——單元格顏色混合
東北方向的連線橋

這樣看起來在只在前三個方向三角化連線橋就能包含所有的連線,這三個方向分別是NE、E和SE。

HexMap學習筆記(二)——單元格顏色混合

HexMap學習筆記(二)——單元格顏色混合
裡面和邊界上的連線橋

相對於兩個單元格的連線就完成了,但是還有一些在網格邊緣無用的連線。讓我們通過修改TriangulateConnection中的程式碼去掉它們。一旦發現沒有相鄰單元格方法就跳出去,所以不用再用當前單元格代替不存在的相鄰單元格了。

HexMap學習筆記(二)——單元格顏色混合
HexMap學習筆記(二)——單元格顏色混合
只有裡面的連線橋

4.2三角形連線處

又來開始填三角形的坑了,依然只需要在相鄰單元格存在的時候才執行。

HexMap學習筆記(二)——單元格顏色混合

第三個頂點位置在哪?暫時用v2當佔位符,但那顯然不正確。因為三角形的每個頂點都連線在橋上,所以可以通過在下一個相鄰六邊形的連線橋上獲得。

HexMap學習筆記(二)——單元格顏色混合

HexMap學習筆記(二)——單元格顏色混合
再一次,顏色過渡完成

全搞定了?還沒,現在要處理三角形重疊的問題。因為是每三個單元格共享一個三角形連線處,所以只需要在兩個方向新增連線三角形:NE和E方向。

HexMap學習筆記(二)——單元格顏色混合

下一期教程內容是HexMap3
本期工程地址:tank1018702/Hex-Map-Learning
有想系統學習遊戲開發的童鞋,歡迎訪問http://levelpp.com/。

專欄地址:https://zhuanlan.zhihu.com/p/55068031

相關文章