譯文:程序控制生成2D平臺(tái)游戲 Jack Benoit

Procedural Level Generation for a 2D Platformer

程序控制生成2D平臺(tái)游戲(2D平臺(tái)游戲舉例:《超級(jí)馬里奧》《索尼克》)

By Fabien Benoit-Koch

Jack Benoit is my latest mobile game, a not-so-original 2D platformer for Android. My goal was to make a fast, responsive game for mobiles, with the best possible controls, and to have complete procedural generation for the levels. By complete, I mean not based on manually crafted level pieces, assembled randomly, but truly randomised to the tile granularity, something that is often advised against. But hey, it was fun to try and the results are not that bad. I’ll describe the whole process in this article.

Jack Benoit是我的一款,不算完全自主創(chuàng)意的安卓2D平臺(tái)游戲。我的目標(biāo)就是在短時(shí)間內(nèi)做出一款手機(jī)游戲,操作簡單,所有地圖利用程序生成。不用人工設(shè)計(jì),完全隨機(jī)生成瓦塊數(shù)量,盡管大家都勸我不要這么做。但是我喜歡,勇于嘗試,而且結(jié)果也不賴。

Description of the game ?游戲介紹

You control a character able to jump and climb ladders, and your goal is to simply reach the exit of a level, which are made of various sets of platforms, ladders, and hazard zones (spikes). Jack Benoit uses 4 layered tile maps:

The parrallax background,

The platforms,

The ladders,

The sprites (collectable items, decorations, etc).

All of the layers are constructed procedurally. The background is simply made out of Perlin noise, filtered and smoothed using transition tiles, I won’t talk about it, the subject has been covered to death by better authors than me. This article will focus the architecture (platforms, blocks, and ladders).

在游戲中,玩家控制一個(gè)小人,通過梯子,跳躍,危險(xiǎn)區(qū)域,來回來去尋找每關(guān)的出口。 游戲用了四層tile地圖:

1、視差背景(Parallax,視差,是指從不同的點(diǎn)看一個(gè)物體時(shí)形成的視覺差異)

2、platforms

3、ladders

4、sprites?

每一層面都由程序生成。背景用Perlin噪聲(Perlin噪聲可以用來模擬自然界中的噪聲現(xiàn)象。由于它的連續(xù)性,如果將二維噪聲中的一個(gè)軸作為時(shí)間軸,得到的就是一個(gè)連續(xù)變化的一維函數(shù)。同樣的也可以得到連續(xù)變化的二維圖像。該噪聲可以用來模擬人體的隨機(jī)運(yùn)動(dòng),螞蟻行進(jìn)的線路等。另外,還可以通過計(jì)算分形和模擬云朵,火焰等非常復(fù)雜的紋理。)算法生成,用過渡tile進(jìn)行過濾并整平。我在這里不多說了,有更好的文章。本文主要闡述游戲中建筑(platform,blocks和ladders)

Step 1. Generating a level layout

第一步:生成一個(gè)關(guān)卡布局

Levels are composed by a random set of discretely connected “rooms” (rectangles of 20×16 tiles). Each room can have up to 3 “walls”, at its own edges. Two rooms are connected if their shared edge doesn’t contain a wall. The structure becomes quite clear when you see a whole level. This one is made of 15 rooms:

關(guān)卡是由隨機(jī)一組不相關(guān)聯(lián)的room組成(room是一個(gè)20x16 瓦塊的長方形)。每個(gè)room最多有3面墻,每面墻占據(jù)長方形四個(gè)邊的其中之一。兩個(gè)能連起來的room,連接的那一面,各自本來都是沒有墻的。下面我們來看一張完整的關(guān)卡結(jié)構(gòu)圖,它由15個(gè)room組成。

The first step is to create this random path of rooms. The goal is to get a data structure describing something like this:

第一步是要隨機(jī)創(chuàng)造出一個(gè)rooms組合,得到一個(gè)如下所示的數(shù)據(jù)結(jié)構(gòu):

We simply represent this using a 2D array of Room objects. The algorithm is a simple recursive graph exploration, with backtracking.

我們用一個(gè)2D array of Room objects來描述。算法是一個(gè)簡單的遞歸地圖探索(recursive graph exploration),不用倒退。

function findPath(x, y, minDistance):

if (x,y is goal and minDistance == 0) return true

if (x,y not open) return false

mark x,y as part of layout path

switch(random number 1 out of 4):

case 1: if (findPath(North of x,y, minDistance - 1) == true) return true

case 2: if (findPath(East of x,y, minDistance - 1) == true) return true

case 3: if (findPath(South of x,y, minDistance - 1) == true) return true

case 4: if (findPath(West of x,y, minDistance - 1) == true) return true

unmark x,y as part of solution path

return false

Once this is done, we make sure the structure is easily iterable, and each Room knows about the location of next and the previous ones.

這樣做完,可以保證整個(gè)結(jié)構(gòu)是可以遍歷的,每個(gè)房間都能獲悉前一個(gè)和下一個(gè)房間的位置。

Step 2. Generating a solution path

第二步:生成一條最優(yōu)通關(guān)路徑

The second step is the most critical to ensure the correctness. It’s very easy, if you’re not careful, to produce impossible levels! In our example, the player must always be able to navigate through all these rooms, to reach the last one. Given that the player movement is constrained by physics (he can jump 4-5 tiles high), we had to make sure that the vertical parts (two or more rooms vertically connected) were always in reach.

第二步對(duì)于保證路徑的準(zhǔn)確性很重要。不小心的話很容易做出一條無解路徑!我們要讓玩家通過所有的房間再到最后一間。玩家單次最高可以跳過4-5個(gè)tile,我們要確保垂直的部分,玩家是都可以抵達(dá)的。

That part proved to be tricky. I finally chose to create a solution path, i.e. a set of platforms and ladders that leads the player directly to the level exit, without interruption.

最終,我選擇創(chuàng)建一條最優(yōu)通關(guān)路徑,通過platform和ladder引領(lǐng)玩家順利通關(guān)

At first, it may seem too straightforward, but once the generation is complete, this path is “hidden” in the middle of the other platforms, and is not evident at all to the player. In fact, it is so camouflaged that I had to put sign posts indicating the direction to follow. The player often gets off the path, finding alternative ones, but at least a solution is guaranteed to exist (no impossible levels).

起初,這么做看起來太直接了,但所有內(nèi)容完成后,這條路就隱蔽在其它platform中了,玩家不容易發(fā)現(xiàn),我有時(shí)還要放一些引導(dǎo)。玩家有些時(shí)候,明明走對(duì)了卻又跳了下來去找別的。不過最后都應(yīng)該是可以出來的。

The process is quite simple. Generate a random position in each room, then connect all of them using one ladder, and one platform.

生成過程很簡單。在每個(gè)room里生成一個(gè)隨機(jī)位置,然后每個(gè)點(diǎn)之間都用一個(gè)ladder和一個(gè)platform連起來。

for each room in the layout:

select a random point P1(X1,Y1) in the room

select a random point P2(X2,Y2) in the next room of the layout

set cursor C(Xc, Yc) to P1

while P2 is not reached by cursor:

if (selectionFunction):

create a platform between (Xc, Yc) and (X2, Yc)

move cursor to (X2, Yc)

else:

create a ladder between (Xc, Yc) and (Xc, Y2)

move cursor to (Xc, Y2)

The selectionFunction is used to determine if we start by a ladder or a platform. It’s randomised, however in order to generate well-designed levels, it will also take some heuristics into account, like the minimum length of a ladder or a platform.

這個(gè)selectionFunction用來決定,是從ladder還是platform開始。這個(gè)是隨機(jī)的,但要想把關(guān)卡設(shè)計(jì)的更好,還是應(yīng)該有些大膽的探索,比如給ladder和platform一個(gè)長度限制。

Step 3. Fill the rooms with platforms

第三步:在rooms中填充platforms

Now that the path is secured, we must actually fill the rooms. I tried some top-down approaches (generating perlin noise to spread platforms homogeneously) but the simplest ones (pure randomness guided with some ad-hoc heuristics) often produced the best results.

現(xiàn)在路徑做好了,我們要把rooms充實(shí)起來。我嘗試了很多top-down 方法(用Perlin噪聲生成platform然后均勻散開),往往最簡單的方法給出最佳結(jié)果。

I simply on each empty tile of the room, starting in the top-left corner, and for each empty tile (in the platforms layer), there is a chance to generate a platform of some random length. We also make sure that this platform, if generated, does not completely block the level (horizontally or vertically).

我先簡單地,從每個(gè)room的左上角開始,把所有值為0的tile遍歷一遍,這些0值tile(platform層的)都有可能生成隨機(jī)長度的platform。不過我們要保證這些platform,在水平或者垂直方向,不會(huì)將路徑堵住。

Step 4. Generate ladders

第四步:生成ladders

On each the generated platforms, we place a ladder top at a random X position on the platform. We then “grow” them (like a plant, which is fortunate, because some of the ladders are actually plants) downward until it reaches a platform below, or the ground.

在每個(gè)生成的platform上,我任選一點(diǎn)作為一個(gè)ladder的起點(diǎn),然后讓ladder向下生長(游戲中的ladder就是植物的樣子),直到觸及到下面的platform或者地面

As you can see, while the solution path is quite clear before this step, it is eventually neatly disguised.

如圖所示,生成這些ladders后,就可以將之前生成的最優(yōu)解決路徑混淆掉。

Conclusion

結(jié)論

This simple method produces a lot of variability, which is nice, yet the gameplay remains relatively consistent. After some runs, the player learns to “guess” what the solution path is. It has some drawbacks though: some (rare) platforms remain sometimes unreachable, because putting a ladder on 100% of all the platform produces too many of them.

簡單的方法可以制造出很多的可能性,大致玩法是不變的。玩了幾次之后,玩家能猜到這里面藏有一條最優(yōu)路徑。缺點(diǎn)是:少數(shù)platform,在有些情況下到不了,給所有platform放一個(gè)ladder,就有了太多的可走的路。

A more complex graph exploration, based on the physical characteristics of the player would be necessary to detect and correct these cases, but it is costly, and felt unnecessary given the frequency and the gravity of the problem.

更復(fù)雜的地圖探索,要有樂于發(fā)現(xiàn)并糾正問題的玩家?guī)椭@就需要你花費(fèi)更多的精力去應(yīng)對(duì)他們提出的問題。

Thanks a lot for reading this. If you have any questions, feel free to ask!

謝謝閱讀。

Note: This post was originally published on Fabien's blog and is republished here with kind permission.

About the Author(s)

After a decade in "corporate" software engineering, I decided to jump back to my first love, game development, hoping to

recreate the arcade feel of classics on modern platforms. With the help of my partner in crime, Marcin JP, we founded Arcade Pulp,

and we're currently busy with "Turbo Smash Blade", an old school brawler for mobiles phones.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容