最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

HexMap學(xué)習(xí)筆記(三)——海拔高度與階梯連接

2019-02-22 18:10 作者:皮皮關(guān)做游戲  | 我要投稿

作者:沈琰


從這篇開始難度陡然提高了不少,不過雖然在三角剖分這塊略微復(fù)雜一些,但也不算很難,主要就是計算繁瑣。大家可以在紙上畫一下方便理解。

本篇原文地址:https://catlikecoding.com/unity/tutorials/hex-map/part-3/


本篇難度:★★☆☆☆

海拔高度與階梯連接

此教程是HexMap的第三部分,這次會添加一個方法讓單元格處于不同的海拔高度,并用一個特殊的方法去連接它們。

單元格的高度與階梯化的連接

1.單元格的高度

我們之前在平坦的區(qū)域把地圖分成了不同的六邊形單元格,現(xiàn)在要給每個單元格加上高度變化。我們將用高度等級來表示,在HexCell里新建一個整數(shù)字段表示這個值。

高度等級每一級可以是任何值,在HexMetrics里另外定義一個常量表示。這里用5標準單位作為每層的高度值,變化看起來會比較明顯,如果是實際游戲中可以適當(dāng)減小每層高度。

1.1編輯單元格

之前我們只能編輯單元格的顏色,而現(xiàn)在要添加編輯高度的功能,所以現(xiàn)在HexGrid.ColorCell方法已經(jīng)不夠用了。而且考慮到以后可能給每個單元格添加更多的可編輯選項,這需要一個新的編輯方法。

把ColorCell重命名為GetCell,并把編輯顏色的功能變?yōu)榉祷匾粋€指定位置的單元格?,F(xiàn)在不再作任何改變操作,也不再直接三角化每個單元格。

現(xiàn)在由編輯器自己來修改單元格,之后在重新三角化一次。為此添加一個公共方法HexGrid.Refresh()去處理。

修改HexMapEditor里的代碼讓其調(diào)用新方法,添加一個EditCell的新方法負責(zé)單元格的編輯,然后刷新網(wǎng)格。

可以簡單地為正在編輯的單元格分配一個選定的高度等級來調(diào)整單元格的海拔高度。

和顏色一樣,需要添加一個方法去設(shè)置有效的高度等級,并與UI相關(guān)聯(lián)。在這里用UI里的Slider組件去調(diào)整高度等級。由于Slider組件接收的是float類型的值,所以我們的方法也需要一個float類型的參數(shù),然后再轉(zhuǎn)換為整數(shù)。

在Canvas上添加一個Slider組件(GameObject / Create / Slider)并調(diào)整位置到調(diào)色板的下面。設(shè)置其為豎直方向,視覺上比較吻合調(diào)整高度的組件。限制它的取值上下范圍在一個合理的區(qū)間,比如說0-6。接著把OnValueChanged事件與HexMapEditor里的SetElevation方法相連。

編輯高度的滑動條組件

1.2高度可視化

現(xiàn)在編輯單元格可以同時改變顏色和高度等級了,你可以在Insperctor里看到高度確實改變了,但是三角化時并未應(yīng)用。

現(xiàn)在需要在高度變化時修改垂直方向的本地坐標,為了讓這一步更簡化一些,把HexCell.elevation設(shè)為私有類型同時添加一個HexCell.Elevation公共字段。

現(xiàn)在就能在編輯高度時修改垂直坐標。

當(dāng)然別忘了修改HexMapEditor.EditCell里的賦值。

不同高度的單元格
MeshCollider是否能吻合新的高度地圖?
舊版本的Unity需要在設(shè)置相同的Mesh信息之前先把MeshCollider設(shè)為null,它只是假設(shè)Mesh的數(shù)據(jù)不會改改變,所以只有不同的Mesh(或者null)能觸發(fā)碰撞器的刷新,現(xiàn)在已經(jīng)沒這個必要了。所以我們現(xiàn)在的方法:在三角化之后重新分配碰撞器的Mesh信息,這是可行的。

現(xiàn)在單元格的高度就明顯可見了,但是還有兩個問題:

第一:單元格顯示坐標的標簽在被升高的單元格下面消失了。

第二:單元格的連接沒有考慮到高度。

現(xiàn)在我們就來解決這兩個問題。

1.3 單元格坐標標簽復(fù)位

就當(dāng)前情況下,單元格的坐標UI標簽只有在開始的時候創(chuàng)建和定位了.然后就沒有管了。要更新標簽的垂直坐標就得獲取引用,所以給每個HexCell一個自己UI標簽的RectTransform的引用,以便接下來去更新它。

在HexGrid.CreateCell結(jié)尾時給它們賦值。

現(xiàn)在可以擴展HexCell.Elevation的set方法,讓其同時校準坐標UI標簽的位置。因為Canvas組件之前我們旋轉(zhuǎn)過,所以應(yīng)該是在Z軸的負方向移動而不是Y軸。

坐標標簽與單元格的高度同步


1.4 創(chuàng)建傾斜連接

下面把平坦的連接變?yōu)閮A斜的連接,這一步的方法已經(jīng)在HexMesh.TriangulateConnection里完成了。在邊界相連的情況下需要覆蓋高度坐標到連接終點兩端。

在角相連的情況下則需要對每個相連接的單元格做一樣的操作。

不同高度單元格之間的連接

這樣不同海拔高度之間的連接就完成了,可以看到所有的傾斜面都用正確的方式連接在一起,但這還不算完,繼續(xù)讓連接方式變得更有意思一些。

2.階梯狀的邊界連接

筆直的傾斜連接看起來沒什么意思,我們可以讓它分成階梯狀的好幾段?!稛o盡傳說》就是其中一個這么做的游戲。

例如我們可以在一個傾斜面上插入兩段階梯,結(jié)果就是一個完整的大斜坡被分為三個小斜坡,中間用平坦的部分相連,這需要我們把連接分為五個部分來看。

一個斜坡中插入兩段梯形

我們可以在HexMetrics里定義每個斜坡中插入梯形的數(shù)量,并由此計算出分成部分的數(shù)量。

理想情況下,我們可以簡單地沿著斜率對每一步進行插值。這并不完全是不重要的,因為Y坐標只能在奇數(shù)步上變化,而不能在偶數(shù)步上變化。否則我們就得不到平坦的類似梯田的形狀。讓我們在HexMetrics中添加一個特殊的插值方法來解決這個問題。

水平方向的插值是筆直向前的,只要我們知道插值的步長就行。

同樣添加顏色的階梯插值計算方法,僅當(dāng)連接處是平的時候計算插值。

2.1 三角剖分

因為邊界連接處的三角剖分更復(fù)雜了,所以把HexMesh.TriangulateConnection中的相關(guān)代碼提取出來放到一個新方法中,把原先代碼注釋掉以便等會作為參考。

先處理第一步,用我們的特殊插值方法創(chuàng)建第一個四邊形,會得到一個比原來斜率更陡的短斜坡。

階梯化的第一步

現(xiàn)在直接跳過其他步驟到最后一步,盡管此時還不是正確形狀,但先完成邊界連接。

階梯化的最后一步

中間跳過的步驟可以放在一個循環(huán)中,每一步中上一次計算的最后兩個頂點作為這一步開始的兩個頂點,顏色賦值也是同樣的方法。這樣一來新的向量和顏色就計算出來了,另外幾個四邊形也添加進去了。

單元格之間的所有階梯化步驟

現(xiàn)在每個單元格的邊界連接都有兩次階梯變換,或者你也可以在HexMetrice.TerracesPerSlope中修改變換的次數(shù)。當(dāng)然,我們還沒有對角落連接進行階梯化處理,這部分放到后面來完成。

階梯化連接所有的邊界


3 連接類型

把所有的邊界連接都進行階梯化處理似乎不太妥當(dāng),當(dāng)兩個相鄰單元格之間的高度相差不大的情況下看起來還行,但差距較大時這樣處理會產(chǎn)生一個狹長的階梯狀大跳躍,這不太好看。另外還有平坦連接情況下就更不需要進行階梯化處理了。

我們把不同高度的連接情況抽象為三種類型:Flat(平坦),Slope(傾斜),Cliff(陡峭),并為此創(chuàng)建一個新的枚舉類型。

為了確認是哪種連接類型,可以在HexMetrice里添加一個方法,基于連個高度等級去獲取連接類型。

如果高度是一樣的,那自然就是平坦的。

如果高度等級的差值剛好為1,就是傾斜類型。除此之外,無論是大于1還是小于-1,就只能是陡峭類型了。

同樣也在HexCell里添加一個便于獲取邊界類型的方法。

是否需要在所有方向上都要檢查存不存在相鄰單元格?
你可能最終會在恰好位于地圖邊界上的方向獲取邊緣類型。在那種情況下是沒有相鄰單元格的,然后我們也會得到一個空引用異常。當(dāng)然可以在方法內(nèi)就檢查這個,然后如果發(fā)生這種情況拋出某種異常。但是那種情況下,異常就已經(jīng)發(fā)生了,所以這是多此一舉,除非你想拋出一個自定義的異常類型。
需要明確的是,我們只會在我們知道不是處理地圖邊界的時候才會使用這個方法,如果在某些地方出錯了,我們肯定會得到一個空引用異常。

3.1限制只讓傾斜類型階梯化

現(xiàn)在已經(jīng)能確認,哪種連接類型才需要階梯化。修改一下HexMesh.TriangulateConnection讓它只在處理傾斜類型時候階梯化。

這里可以重新啟用之前注釋的代碼,負責(zé)平坦和陡峭類型的連接情況。

只有傾斜類型會進行階梯化處理


4.角落連接的階梯化

角落連接比邊界連接更為復(fù)雜,因為它連接了三個不同單元格,而不是僅僅兩個。每個角連接的三個邊界類型可能是任意一種情況,所以可能性很多。因此最好另外添加一個角落專用的階梯化方法到HexMesh里。

新的方法需要角落三角形的三個頂點和連接的單元格。為了便于管理,我們按順序整理找到最低高度的單元格,然后從底部開始從左往右階梯化。

單元格角落上的連接

現(xiàn)在TriangulateConnection里需要確認哪一個是高度最低的單元格。首先檢查將被三角化的單元格是否高度低于其相鄰單元格,或者就是最低的單元格。在這種情況下我們可以把它當(dāng)做底部單元格使用。

如果最里面的檢測是false, 說明另一個相鄰單元格是最低高度的單元格。這時需要逆時針轉(zhuǎn)動三角形的參數(shù)到正確的方向。

如果第一次高度檢測就是false,情況就變成了分辨出兩個相鄰單元格誰才是最低的。如果邊界上的相鄰單元格是最低的,就要順時針旋轉(zhuǎn)參數(shù)順序,否則逆時針旋轉(zhuǎn)。

注:這里刪去底下原本直接添加三角形的代碼
逆時針旋轉(zhuǎn),不旋轉(zhuǎn),和順時針旋轉(zhuǎn)

4.1傾斜連接的三角剖分

在去想如何三角化角落連接區(qū)域時,先需要知道處理的是哪種邊界類型。為簡化這一步,在HexCell里添加一個新方法,獲取任意兩個單元格連接方式。

在HexMesh.TriangulateCorner里使用新方法去確認左右的邊界類型。

如果兩邊都是傾斜類型,就需要在左右兩邊都進行階梯化。因為底部單元格是最低的,所以這些傾斜類型都是向上的。此外這也意味著左右兩邊單元格有相同的高度,所以上端的邊界連接類型是平坦的。我們可以定義這種情況叫slope-slope-flat,或者簡稱SSF。

兩個傾斜和一個陡峭類型,SSF

檢測是否是這種情況,如果是就調(diào)用新方法TriangulateCornerTerraces,然后直接跳出方法。把檢測代碼放到默認的三角化代碼之前,這樣就會替代默認的的三角化。

只要沒在TriangulateCornerTerraces()里做些什么,這個連接角就會變成一個空洞。是否會變成洞取決于哪個單元格最終成為底部單元格。

出現(xiàn)了一個洞

要填充這個洞我們得穿過缺口連接左右的階梯,這與邊界連接基本是一樣的,除了內(nèi)部添加的雙色四邊形變成了三色三角形。我們還是首先從第一個三角形開始。

第一步的三角形

再一次直接跳到最后一步,這是一個梯形狀的四邊形。與邊界連接最后一步不同的是,這里四個頂點都是不同的顏色。

最后一步的四邊形

在此之間的步驟添加的也是四邊形。

所有的步驟

4.2 雙傾斜連接類型的變體

雙傾斜連接類型在不同的朝向上有兩個變體,取決于哪一個是底部單元格。我們可以通過檢查單元格到左右兩個相鄰單元格的連接類型是不是傾斜-平坦,和平坦-傾斜類型來找到。

如果右邊的邊界連是平坦類型,我們就從左邊開始階梯化處理而不是底部。如果左邊是平坦類型,就從右邊開始。


這樣一來階梯就能沒有中斷的環(huán)繞每個單元格,除非碰到陡峭連接或者地圖邊界。

連續(xù)環(huán)繞的階梯化連接

5.傾斜和陡峭類型的融合

所以當(dāng)傾斜類型和陡峭類型之間該怎么連接起來?如果我們知道左邊的邊界連接類型是傾斜類型而右邊是陡峭類型,那最上端的單元格的邊界連接是什么類型?它肯定不可能是平坦的,但有可能是傾斜和陡峭類型中的任意一個。

SCS和SCC類型

添加一個新方法同時處理這兩種情況。

它應(yīng)當(dāng)在左邊邊界連接是傾斜類型的情況下,在右邊連接最后一個可能選項中被調(diào)用。

所以這塊該如何三角化?這個問題要分成兩個部分:底部和頂部。

5.1 底部部分

底部部分的左邊已經(jīng)階梯化了,而右邊是直接連接的陡峭類型。我們要合并它們最簡單的方法就是把階梯化部分向右上角折疊,這樣會讓階梯部分向上越來越細。

折疊階梯化部分

但事實上并不想這樣讓它在右邊的角落交匯,因為這樣可能會干擾可能存在的頂部到其他單元格的階梯化部分。并且在處理非常高的陡峭連接時,這么做還會產(chǎn)生非常高和薄的三角形,這樣會很不好看。所以我們把它折疊到陡峭到傾斜連接的分界點上。

折疊到分界點上

把分界點定位在比底部單元格高一級的海拔高度上,我們可以根據(jù)海拔高度差的插值得到。

驗證一下邊界點是否正確,用一個三角形覆蓋它們。

較低的三角形

分界點定位準確時就可以開始三角化階梯部分了。還是從第一個三角形開始。

折疊的第一個步驟

這一次最后一個步驟也是一個三角形。

折疊的最后一個步驟

之間的步驟也是三角形。

階梯化的折疊
不能保持階梯的高度不變么?
通過在起始點和分界點之間插值計算我們的確能保持階梯部分平坦,而不是總是使用邊界點。這就需要使用傾斜四邊形填充這個部分。但是這些傾斜四邊形并不是在一個平坦的平面上,因為它們左右相連的邊的斜率并不一樣。最后結(jié)果看起來會很亂。

5.2 完成相連角

底部部分就完成了,現(xiàn)在來看看頂部部分怎么處理。如果頂部單元格與相鄰單元格的邊緣連接時傾斜類型,我們就又得去連接階梯化部分和陡峭部分。為了代碼能復(fù)用,這里單獨抽成一個方法,并把先前的代碼移動到它自己的方法中。

現(xiàn)在完成頂部連接就簡單許多了,如果頂部邊緣連接時傾斜的,就改變參數(shù)順序,添加旋轉(zhuǎn)過的分界點三角形,否則就直接一個簡單的三角形就足夠了。

完成這兩種情況的三角化

5.3 鏡像情況

現(xiàn)在我們已經(jīng)解決了傾斜-陡峭情況的階梯化合并.還有兩種鏡像的情況,這時它們陡峭連接的部分在左邊。

方法和之前是一樣的,只在方向問題上有些許不同。復(fù)制TriangulateCornerTerracesCliff并修改相應(yīng)的部分,這里只標注了不同的地方。

在TriangulateCorner里去處理這些情況。

CSS和CSC的三角化也完成了

5.4 兩個陡峭連接類型的情況

剩余的沒有平坦連接的情況就是底部單元格兩邊的邊緣連接都是陡峭類型,這樣一來頂部的兩個相鄰單元格的連接有可能是平坦,傾斜,陡峭中的任意情況。我們只對頂部連接是傾斜時這種情況感興趣(C-C-S),只有這種情況下要處理連接角的階梯化。

實際上有兩個不同的CCS版本,根據(jù)哪邊更高來決定,它們互為鏡像。把這兩種情況定義為CCSR和CCSL。

CCSR和CCSL

我們可以通過不同的參數(shù)順序調(diào)用TriangulateCorner里先前寫好的TriangulateCornerCliffTerraces和TriangulateCornerTerracesCliff這兩個方法去解決這兩種情況。

然而這會產(chǎn)生一個奇怪的三角剖分。發(fā)生這種情況是因為我們現(xiàn)在是從上至下在進行三角化,這樣分界點的插值計算就得到了一個負值。解決方案是保證插值計算的正確性。

CCSR和CCSL三角化完成

5.5 清理

現(xiàn)在能處理所有特殊的連接情況,并且所有的階梯都有正確的三角剖分。

所有情況下的三角化都完成了

我們可以清理一下TriangulateCorner里的代碼,用else來替代return。

最后一個else里包含了所有之前沒提到的連接情況,分別是FFF,CCF,CCCR,CCCL。它們的角連接都可以用一個三角形表示。

所有一個三角形能表示的情況

下一篇教程是:https://catlikecoding.com/unity/tutorials/hex-map/part-4/

本期工程地址:https://github.com/tank1018702/Hex-Map-Learning/tree/Elevation


想系統(tǒng)學(xué)習(xí)游戲開發(fā)的童鞋,歡迎訪問?http://levelpp.com/???????????

游戲開發(fā)攪基QQ群:869551769???

HexMap學(xué)習(xí)筆記(三)——海拔高度與階梯連接的評論 (共 條)

分享到微博請遵守國家法律
九寨沟县| 福泉市| 长顺县| 岳阳县| 和林格尔县| 岑溪市| 临高县| 衡山县| 深州市| 城步| 林口县| 伽师县| 易门县| 堆龙德庆县| 萨嘎县| 汉阴县| 怀安县| 开鲁县| 石首市| 绥中县| 安平县| 策勒县| 金溪县| 定结县| 剑阁县| 饶河县| 株洲县| 南昌县| 昂仁县| 黄龙县| 正蓝旗| 麻江县| 桃园县| 徐州市| 高阳县| 凤凰县| 宁陕县| 泸水县| 达拉特旗| 新安县| 仪陇县|