V7大神獎專訪|Cocos Creator3.x 程序化大世界地圖!
共 16512字,需瀏覽 34分鐘
·
2024-06-07 16:11
在 Cocos 第 7 期社區(qū)投稿活動中,開發(fā)者 GreenPack 的投稿文章《Hello World: Cocos Creator3.x 程序化地圖》榮獲了“大神獎”。
我們有幸對 GreenPack 進行了專訪,在閱讀大佬的文章之前,我們先一起來看看他在游戲開發(fā)道路上的心得與經(jīng)驗吧。
大佬能不能介紹下自己?
大家好,我是 GreenPack,很榮幸獲得這次 Cocos v7 投稿的”大神獎“。
我是一個普通的游戲客戶端開發(fā)者,為了尋求技術增長,自學和應用了一些渲染相關的技術。這次 v7 投稿活動,我將我自己的過去技術整合,結合 Cocos Creator 引擎,做了一套簡單的程序化大地圖渲染方案。
老實說,這次投稿活動的競爭十分激烈,看著各位大佬的作品,我曾經(jīng)也是一度懷疑自己的投稿的分量到底夠不夠,這次能得大神獎其實也有點意外和驚喜。
大佬得獎有什么感言?
首先非常感謝官方給我這個獎,說實在的,這個稿子投了之后,我后續(xù)自我感覺還是有好多的提升空間,后續(xù)還在考慮繼續(xù)優(yōu)化迭代。
其次要感謝各位開發(fā)者同僚們,最近一段時間我經(jīng)常在論壇發(fā)一些技術貼,不得不說 Cocos 的論壇氛圍是真的好,有問題可以快速得到解答,分享技術經(jīng)驗能獲得成就感。
我從菜雞到一個勉強有一技之長的 Cocos 論壇鍵客,和各位的一聲聲胡吹分不開,尤其是一些群里的大佬,成天說“群除我佬”,說的我自己都差點相信我是大佬了。
最后祝 Cocos 越來越好,各位開發(fā)者們早日實現(xiàn)財富自由!
看完 GreenPack 的采訪,來看看這篇獲得”大神獎“的文章:《Hello World: Cocos Creator3.x 程序化地圖》
前言
坑挖得太大了……
14 號活動帖子發(fā)出來時,我就開始整這個投稿了,當時設想了很多東西,什么日月星辰、風雨雷電、花草樹木、滄海桑田都想加進去。后來實踐起來,工作量遠超自己的想象。真這樣搞起來 10 年都填不完了。
眼看著論壇里大神們一個接一個地拋出好東西,忍不住了,先把當前的成果搬出來了,以后有時間再繼續(xù)加工。
關于噪聲生成算法
大就是強,大就是美!誰會不喜歡大的呢?我第一次接觸到隨機生成無限大世界這個概念,是看到了這一篇文章 《「無人深空」是怎樣生成一個宇宙的?》
“文章鏈接:https://indienova.com/indie-game-news/how-does-no-mans-sky-generate-the-universe/
”
里面提到了用一個自然界存在的擁有著無限量信息的無理數(shù)來構造世界,沒錯,那位大人就是:π!
“文章鏈接:https://qinglite-1253448069.cos.ap-shanghai.myqcloud.com/web/3bc46f1e8e0be128adb98a76294bf65fa13f60ae
”
但是當時我還是個小菜雞,只是隱約理解了程序化生成世界使用了π,但是具體怎么操作,完全沒有頭緒,直到后來學 shader 時看了這本經(jīng)典的電子書:thebookofshaders。
“文章鏈接:https://thebookofshaders.com/?lan=ch
”
這本電子書里有不少內(nèi)容,這里不多贅述,簡單來說,它寫了怎么程序化生成一個噪聲。這里我會用 三個步驟 簡單地告訴大家,程序化生成噪聲的幾個核心要點:
1. 一步計算即可獲得的隨機數(shù)
首先拋開復雜的東西,要想生成一個隨機的內(nèi)容,我們第一步要生成一個隨機數(shù),不,等等,在程序化生成世界的過程中,我們需要大量隨機數(shù),并且這些隨機數(shù)會由它們的坐標唯一確定。
theshaderofbooks 里描述了隨機數(shù)生成的計算,并且用了可以編寫實時查看的網(wǎng)頁元素的方式來呈現(xiàn),非常地高端大氣,但是關于隨機數(shù)這第一步,也許是因為翻譯的問題,我個人覺得書里沒有很好的說清楚。下面我將結合圖片來說說我的理解,先看噪聲那一章節(jié)的圖:
看代碼,輸入值x經(jīng)過了一次 fract 操作,也就是取小數(shù)部分。我們可以理解為它將曲線劃分成了以 1 為周期的無限區(qū)塊。
再回頭看隨機那一章節(jié)的圖:
這里對輸入的x值進行了一次 sin 操作。
好了,大聲告訴我, sin的周期是多少 !
這就是核心操作了,一個周期為 2π 的曲線,我們每隔單位 1 取一個值,取出的數(shù)值構成的數(shù)列,從數(shù)學邏輯上保證了沒有周期性。所以,它是一個隨機數(shù)列。
2. 平滑過渡,一維變二維
第一步我們獲得的是一些離散的隨機數(shù),但是現(xiàn)實中的噪聲,非常接近的兩個坐標點之間的值也是非常接近的,也就是說噪聲是具有連續(xù)性的。
從離散的數(shù)列到具有連續(xù)性的曲線,很簡單,兩個點之間平滑過渡一下即可:
一般情況下,會用常用的 smoothstep 算法來做平滑過渡,thebookofshader 中還提到了 simplexNoise 中使用到的四次 Hermite函數(shù)。
關于這方面我并沒有過多的研究,只有一個小小的個人經(jīng)驗,那就是 smoothstep 函數(shù),在端點處的斜率是 0,也就是說上面那個曲線,每隔單位1就會出現(xiàn)一個“平地”。
平滑過渡之后,再將一維曲線升級一下,變成二維的噪聲圖即可,這里就不多贅述了,網(wǎng)上教程很多。
3. 迭代、分形
噪聲生成相關的技術中,有個聽起來非常牛批的東西, 分形布朗運動(Fractal Brownian Motion) ,簡稱 fbm 。別被它嚇到,這玩意用簡單地白話來說,就是用了一次for循環(huán),對我們上面步驟獲得的波進行了疊加而已。
布朗運動是指懸浮在液體或氣體中的微粒所做的永不停息的無規(guī)則運動。分形則是數(shù)學中的一個概念,大家可能已經(jīng)忘了,一個例子幫你回憶起來:雪花晶體!所謂分形,就是放大了之后看,小的部分和大的形狀相似。
那么怎么整出分形布朗運動呢?簡單,把我們上面步驟獲得的曲線,縮小振幅,加大頻率,經(jīng)過多次迭代即可。
這里還引入了一個術語:** octaves(八度)** 。這是音樂中的一個概念,在這里,fbm 進行了幾次迭代,就稱為幾個八度。
Cocos Creator 代碼生成網(wǎng)格
1.代碼接口
Cocos Creator 提供了代碼生成網(wǎng)格的接口,直接看官方文檔即可,下面是簡單的代碼示例。
let mesh:Mesh = utils.MeshUtils.createMesh({
colors:[1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1],
positions:[0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1],
indices:[0, 3, 2, 0, 2, 1],
minPos:v3(0, 0, 0),
maxPos:v3(1,1,1),
// attributes: [
// new gfx.Attribute(gfx.AttributeName.ATTR_POSITION, gfx.Format.RGB32F),
// ],
});
參數(shù)說明:
-
positions:頂點坐標數(shù)組。長度必定為3的倍數(shù),每三個值代表一個頂點的x、y、z。 -
colors:頂點顏色數(shù)組。長度必定為4的倍數(shù),每四個值代表一個頂點的rgba(這里的顏色值已經(jīng)歸一化)。 -
indices:頂點數(shù)據(jù)是可以復用的,indices中的值是頂點數(shù)據(jù)的索引,每三個值構成一個三角面。 -
minPos & maxPos:包圍盒的起點和終點。也可以缺省,然后在options參數(shù)中設置讓網(wǎng)格自己計算,但是這樣會有較大的計算量。
“程序化生成網(wǎng)格的時候要注意:所有三角面需要從相機方向看過去呈順時針。這是因為一般 3d 引擎中,為了減少無用的渲染,通常會有正面、背面的區(qū)分,并且一般默認開啟背面剔除。而正面背面的區(qū)分方式則是三角面的三個頂點的順逆時針順序。
”
-
attributes:頂點數(shù)據(jù)格式。可以修改默認的頂點數(shù)據(jù)的格式。在 create-mesh.ts中有定義了程序化網(wǎng)格接口默認的頂點數(shù)據(jù)格式:
const _defAttrs: Attribute[] = [
new Attribute(AttributeName.ATTR_POSITION, Format.RGB32F),
new Attribute(AttributeName.ATTR_NORMAL, Format.RGB32F),
new Attribute(AttributeName.ATTR_TEX_COORD, Format.RG32F),
new Attribute(AttributeName.ATTR_TANGENT, Format.RGBA32F),
new Attribute(AttributeName.ATTR_COLOR, Format.RGBA32F),
];
5 個默認的頂點數(shù)據(jù)的數(shù)據(jù)類型都是 rgba32f,如果對某個值精度要求不那么高,可以自己改一下,減少帶寬(最常見的就是把顏色值改掉)。
2.查看網(wǎng)格
我們使用裝飾器executeInEditMode來裝飾腳本類,讓腳本在編輯器中運行,便于我們觀察生成的網(wǎng)格。
為了處理每次修改代碼后,腳本反復執(zhí)行創(chuàng)建網(wǎng)格產(chǎn)生的問題。我們將生成一個子節(jié)點,并且將網(wǎng)格放在子節(jié)點上,在生成之前做一次 removeAllChildren 操作。
@ccclass('procedural1')
@executeInEditMode(true)
export class procedural1 extends Component {
start() {
this.node.removeAllChildren()
let node = new Node()
this.node.addChild(node)
let meshRenderer:MeshRenderer = node.addComponent(MeshRenderer);
let mesh:Mesh = utils.MeshUtils.createMesh({
colors:[1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1],
positions:[0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1],
indices:[0, 3, 2, 0, 2, 1],
minPos:v3(0, 0, 0),
maxPos:v3(1,1,1),
// attributes: [
// new gfx.Attribute(gfx.AttributeName.ATTR_POSITION, gfx.Format.RGB32F),
// ],
});
meshRenderer.mesh = mesh;
meshRenderer.onGeometryChanged();
}
}
可以在編輯器中看到我們的網(wǎng)格了,選中后會有一個正方形的盒子邊框出來,那是我們自己定義的包圍盒。
移動鏡頭到下方看上去,這時網(wǎng)格不見了,因為視角到了網(wǎng)格的背面。有興趣的同學可以自己試試再改下頂點索引的順逆時針,或者在后續(xù)添加了材質(zhì)后,修改正面剔除、背面剔除,加深一下對正面剔除、背面剔除機制的理解。
3.蜂窩網(wǎng)格結構
構造網(wǎng)格時,用兩個三角形拼湊成一個正方形是最簡單的,但是這樣的網(wǎng)格效率低下了一點。在相同頂點數(shù)量下,如何讓面積盡可能得大,很顯然是用等邊三角形。而等邊三角形拼湊在一起,就形成了無數(shù)個正六邊形。很自然地,我就用起來六邊形網(wǎng)格結構。
關于六角網(wǎng)格,可以參考這一篇文章,干貨超多,并且?guī)缀趺總€圖都可以交互:《六角網(wǎng)格大觀》
“文章鏈接:https://indienova.com/indie-game-development/hex-grids-reference/#iah-7
”
文中內(nèi)容超多,不多贅述,請大家自己去看。這里只簡單說下六角網(wǎng)格相關的計算的一個核心要點:立方坐標。
立方坐標,顧名思義,就是具有 xyz 三個軸的坐標系,如何在平面中用上三個軸,大家看這個截圖就行了。
想象一下我們有很多立方形的方塊堆在空間中。這時我們從斜著的方向看去,并且對這些方塊進行一個截面的切割:
六角網(wǎng)格和立方坐標,一目了然。
4.六邊形頂點規(guī)化為中心點
上面那篇文章中幾乎包含了所有六角網(wǎng)格相關的計算的方法,但是我在構造六角網(wǎng)格的時候,還是遇到了一個問題:六角網(wǎng)格的“頂點”(相對于中心點,一個六邊形周圍的六個角上的點)計算坐標容易,但是重用去重時不好處理。
后來,我經(jīng)過思考繪圖,想到了將頂點規(guī)化為中心點:
如圖,一個大六邊形的頂點,可以視為其尺寸 1/3 的小六邊形的中心點。
我們用一個二級 map 來存儲頂點對應的索引值,用立方坐標的x、y來作為鍵值(立方坐標中 x + y + z = 0,所以只需要 xy 兩個值,詳情可以看六角網(wǎng)格大觀里的說明)。
為了顯示出六角網(wǎng)格的形狀,我們將所有六角網(wǎng)格的中心點頂點色用黑色,邊角頂點用白色。
addPoint(aid:Vec3, a:number, isCenter:boolean = false) {
let map = this.idxMap.get(aid.x);
if (!map) {
map = new Map()
this.idxMap.set(aid.x, map)
}
if (map.has(aid.y)) {
return map.get(aid.y)
}
let pos = HexUtils.getCellPositionWithAxisID(aid, a);
this.positions.push(pos.x)
this.positions.push(0)
this.positions.push(pos.y)
if (isCenter) {
this.colors.push(0)
this.colors.push(0)
this.colors.push(0)
this.colors.push(0)
} else {
this.colors.push(1)
this.colors.push(1)
this.colors.push(1)
this.colors.push(1)
}
let idx = this.positions.length / 3 - 1;
map.set(aid.y, idx);
return idx;
}
5.擴圈
定義一個包圍圈數(shù)值 N,做循環(huán)構造六角網(wǎng)格:
let N = 5;
let a = 1;
for (let cx = -N; cx <= N; cx++) {
for (let cy = Math.max(-N - cx, -N); cy <= Math.min(N, -cx + N); cy++) {
let cz = -cx - cy
let center = v3(cx * 3, cy * 3, cz * 3)
let idx0 = this.addPoint(center, a / 3, true)
let offset = v3(1, 1, -2)
let v1 = v3()
Vec3.add(v1, center, offset)
let idx1 = this.addPoint(v1, a / 3)
let v2 = v3()
for (let i = 0; i < 6; i++) {
// 旋轉(zhuǎn)60度
offset.set(-offset.z, -offset.x, -offset.y)
Vec3.add(v2, center, offset)
let idx2 = this.addPoint(v2, a / 3)
this.indices.push(idx0)
this.indices.push(idx1)
this.indices.push(idx2)
v1.set(v2)
idx1 = idx2
}
}
}
6.旋轉(zhuǎn)
六邊形中心點規(guī)化成小六邊形直接坐標乘以 3 即可,邊角頂點我們先找到一個點,這里找的是中心點偏移(1, 1,-1)后的點,然后用六角網(wǎng)格大觀中提到的一個非常好用的技巧,通過立方坐標轉(zhuǎn)置來獲得旋轉(zhuǎn) 60 度后的格子的立方坐標:
創(chuàng)建一個材質(zhì)和 shader 用上,shader 里的輸出乘以一下頂點顏色,效果出來了:
好了,你已經(jīng)學會了噪聲和程序化網(wǎng)格了,接下來只要加億點點細節(jié),就能構造出程序化地形了,前提是你的機子具有理論上無限強大的性能。
億點點細節(jié)
1.lod
lod(level of details) 是一個 3d 渲染常見的技術。一般我們會在兩個地方看到這個詞,一個是模型 lod。大致就是將制作高模、中模、低模三個模型(或者更多),然后通過模型和相機的距離,動態(tài)地替換不同精度地模型。保證近距離下能夠較高的視覺體驗,遠距離時能有較低的性能消耗。
另一個常見的地方就是某些 shader 中會定義一個 lod 值,用于在不同性能設備上,使用不同的 shader,來兼容性能差異。
這里我們也用上大致的一個 lod 技術概念,不過我們不需要做的很動態(tài),由于這個 demo 設定是從第一人稱視角看過去的,所以,只要以角色中心,附近使用高精度網(wǎng)格,距離遠的地方使用低精度網(wǎng)格即可。
我們也用下 fbm 里的術語,用八度 octaves 表示一個分級,每一級的六邊形邊長是上一級的 2 倍,用這樣的方式,可以用較少的網(wǎng)格數(shù)量構造非常大的地圖。
let octaves = 5
let N = 5;
let n = N / 2
let a = 1;
for (let oct = 0; oct < octaves; oct++) {
let mul = Math.pow(2, oct)
for (let dx = -N; dx <= N; dx++) {
for (let dy = Math.max(-N - dx, -N); dy <= Math.min(N, -dx + N); dy++) {
let dz = -dx - dy
// oct>0的六角網(wǎng)格,內(nèi)部一半部分可以不要,由小網(wǎng)格填充
if (oct > 0 && Math.abs(dx) < n && Math.abs(dy) < n && Math.abs(dz) < n) {
continue
}
// 略 大體同上
}
}
}
2.靜態(tài)網(wǎng)格&動態(tài)網(wǎng)格
在進行程序化地形的嘗試的過程中,我試過用 ccc 的動態(tài)網(wǎng)格,官方有個示例可以參考。
一個程序化生成的無邊大地圖,必定時要時刻修改更新地形的,所以理論上動態(tài)網(wǎng)格會更高效。但是使用時,當我將網(wǎng)格的量增大后,遇到了一個報錯:
個人猜測是網(wǎng)格的數(shù)據(jù)量達到上限了。
并且,即時不考慮網(wǎng)格上限的問題,一個非常大的網(wǎng)格,更新時的這么多頂點的計算量也肯定非常夸張。
然后,我經(jīng)過了幾次嘗試:
方法一:使用shader進行地點坐標偏移
整個網(wǎng)格的y坐標全都設置為 0 即可,實際的高度,在 shader 中進行計算偏移。這樣的好處是,完全不需要更新網(wǎng)格。你只要將一個扁平的網(wǎng)格移動,shader 會幫你顯示出該有的高度。
缺點1:只適合單純顯示一個地形。當我們需要有單位在地形上移動時,需要獲取地形高度,此時還需要在 cpu 中進行計算高度。并且,經(jīng)過我的測試,shader 中實時算出的高度由于精度問題,會和 cpu 端算出的值有較大的誤差,這個誤差放大到一個非常大的地圖中,會非常致命。
缺點2:整個高度全有 shader 計算,對于超大地圖來說,fbm 的 octaves 需要很大,計算量會變得極大。
方法二:使用“瓦片”拼湊
參考 2d 的瓦片地圖,我們可以用大量瓦片來拼湊成一個大地圖。瓦片可以通過 gpu instancing 技術進行合批渲染。在 cpu 端對單個瓦片計算出各自的高度,然后在 shader 中加上頂點偏移矯正。
拼湊的問題:我以六邊形為單位瓦片,將計算好各個六邊形的坐標,高度按六邊形中心點的噪聲值來算。然后算出六個邊角頂點的高度和法線,通過 gpu 示例化屬性的方式傳遞給 gpu,用 shader 來偏移對齊。但是這時卻遇到了一個報錯,經(jīng)過幾番嘗試得出結論:gpu 實例化屬性的數(shù)據(jù)量上限為 4 個 vec4 ,也就是一個 mat4 矩陣。
因此我不得不將所有六邊形拆分成 3 個四邊形(mesh 只要構建一個即可,通過旋轉(zhuǎn)來用三個拼湊成一個六邊形)。
3.使用噪聲圖shader 中的 fbm 計算直接換成一個噪聲圖采樣,減少計算量。
通過頂點法線和采樣到的 fbm 計算出的法線疊加,用一個簡單的半蘭伯特模型計算明暗。
4.著色
程序化地形的著色一般有兩種方式,一種是 三向紋理采樣 ,即從 x、y、z 三個方向去采樣紋理,然后根據(jù)當前點的 法線 來對三個采樣值進行融合。
如果不用三向紋理采樣,單純用一個方向,比如從俯視方向,用xz向量來采樣紋理,那么在一些比較陡峭的斜坡處,會呈現(xiàn)出明顯的拉伸現(xiàn)象。
另一種方式則簡單了,使用單純的顏色,不過為了展現(xiàn)出不同的地形風貌,我們不該用單純的一個顏色,而是應該用一個色帶。
這里推薦一下 shadertoy 作者們常用的一個函數(shù):
vec3 palette(in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) {
return a + b * cos(6.28318 * (c * t + d));
}
6.28318 是2π;t 是一個 0~1 之間的參數(shù)(實際上超出了也沒關系,反正取色會用 cos);abcd 參數(shù)控制了 rgb 三個顏色的曲線,可以去這個網(wǎng)頁調(diào)節(jié)自己想要的色帶,然后把參數(shù)拿來用即可。
“文章鏈接:http://dev.thi.ng/gradients/
”
著色后,在將高處的部分加上一些積雪。積雪用簡單地法線和豎直方向的夾角來處理計算即可,越是平坦的地方越容易積雪。
然后用高度值來定義一個雪線,在一定高度之上才會有雪。
在給雪線通過我們的噪聲圖加上一點隨機。
float snowHeightValue = smoothstep(40.0, 50.0, v_position.y + texture(mainTexture, p * 0.1).r * 10.0);
float s1 = clamp(1.0 - snowHeightValue, 0.0, 1.0);
float snowValue = smoothstep(s1, s1 + 0.002 / (s1 + 0.1), dot(vec3(0.0, 1.0, 0.0), n)) * 0.5 + 0.5;
5.移動更新
我們需要在跟著主角移動時更新我們視野內(nèi)的地形。由于地形很大,所以,我們要盡可能地使用少的變動來實現(xiàn)效果。
由于我們已經(jīng)使用 lod 的概念,將網(wǎng)格進行了分級,所以我們只要在相應級別的網(wǎng)格需要變更時才即可。
這樣說起來可能有點繞,我用一個簡單的例子來說明:主角移動,時刻判定主角所在街道;主角所在街道變了,更新街道網(wǎng)格;更新街道網(wǎng)格時,如果城市變了,更新城市網(wǎng)格;更新城市網(wǎng)格時,如果省份變了,更新省份網(wǎng)格;更新省份網(wǎng)格時,如果國家變了,更新國家網(wǎng)格。
這樣,我們按我們的分級 octaves 來進行 update 更新。
onCenterChanged() {
for (let i = 0; i < HexConfig.octaves; i++) {
if (!this.checkCellChange(i)) {
break;
}
}
}
在加上一個簡單的方向控制 ui 來控制主角移動,這些代碼比較基礎,就略過了。
這時,我們已經(jīng)可以愉快地在我們的程序世界里逛街了。不過在完成這一步之后,我還實現(xiàn)了一個比較重要的優(yōu)化功能。
6.共享節(jié)點
雖然我們使用 lod,但是由于每個六邊形就有 3 個節(jié)點存在,地圖上的網(wǎng)格數(shù)量相當多。導致我們在初始化的時候卡了比較多的時間。這時,我想起了論壇上有一篇關于共享節(jié)點的帖子:
“文章鏈接:https://forum.cocos.org/t/topic/144350
”
個人概括理解:共享節(jié)點就是不創(chuàng)建實際的節(jié)點,而是創(chuàng)建了 1 個節(jié)點加上 n-1 個自己定義的對象,這些對象里具備了渲染時的所有差異信息。
用這個方式,我們能創(chuàng)建大量節(jié)點時的時間消耗。不過那篇文章是基于 2.x 的,跟我們 3.x 的模型的共享不能說是毫不搭邊,只能說是毛兒關系沒有。
于是,我花時間啃了三天三夜的源碼,終于整出了一個大致的 3.x 模型的共享節(jié)點方案出來。代碼就不發(fā)了,沒啥特別的,并且我也沒有好好封裝,丑陋地一批。大致原理就是,ccc 的 meshrender 會在賦值了 mesh 后自動創(chuàng)建一個 model。這個 model 就是渲染時的目標對象。
model 有個 node 和 transform 變量都指向當前的節(jié)點,在渲染時會用到node的矩陣信息。
做了一個實驗場景,構造一萬個節(jié)點和使用共享節(jié)點時,編輯器中的耗時減少及其明顯。
pc 網(wǎng)頁上也有一定提升:
尚未進行的構想
由于篇幅和時間的限制,本文到此為止。但是關于程序化生成地形,其實還有許多許多的東西可以探究。最后,我想將一些我沒有時間和精力去繼續(xù)探究,但是有了一些思路和線索的東西羅列一下,以后有時間優(yōu)化,也歡迎大佬們?nèi)L試:
-
使用高度+濕度定義區(qū)域類型:詳情可以參考以下文章:
“文章鏈接:https://indienova.com/indie-game-development/polygonal-map-generation-for-games-2/
”
-
根據(jù)主角視角方向,減少至少一半的網(wǎng)格
這個其實挺好做的,只是我現(xiàn)在實在沒時間,倒騰這篇文章已經(jīng)肝了兩三個星期了。
-
程序化天空
這是個大課題,做得好的話可以整出極其夢幻的效果,大家可以看看 shadertoy 上各種天體、星空的效果,如果把這些整到一個程序化世界的天空中,成品應該是會很震撼的。
-
地圖生物、物品
按噪聲圖的計算方式,去給當前點獲得一個隨機值,然后按這個隨機值去確定當前點有什么生物或者物品即可。技術上沒有什么難度,并且是一個在實際游戲中很有使用性的方向。
-
滄海桑田
thebookofshaders 中提到了用 fbm 來扭曲 fbm,構造出一種夢境版的扭動霧氣效果。根據(jù)類似的原理,其實我們可以給地形加上一個時間參數(shù),讓地形隨著時間慢慢變化。一個會變化的無限世界!
-
更好的噪聲圖
啃了兩天iq大佬的文章,其實早就想好好看看了,只是一直沒有時間(絕對不是害怕讀英文)。目前有了一點點小收獲,但是還沒有實際產(chǎn)出,希望有一天能整出和大佬一個級別的地形表現(xiàn):
-
風格化
我做的這個例子里的地形的渲染是按寫實的方向去的,但是由于技術力有限,并沒有多少真實感。其實很多時候,程序化生成的地形,也可以避開那些難點,不用一味朝著物理寫實的方向,反而能做出更好看的效果,這是我查資料的過程中看到的一個程序化地形的圖:
寫在最后
過幾天整理一下,打算把 demo 放到 Cocos Store 上,希望對有興趣的同學有所幫助。
