處理語言終極指南第二部分:構建一個簡單的遊戲

已發表: 2022-03-11

這是處理語言終極指南的第二部分。 在第一部分,我給出了基本的處理語言演練。 學習Processing 的下一步就是更多的動手編程。

在本文中,我將逐步向您展示如何使用 Processing 來實現您自己的遊戲。 將詳細解釋每個步驟。 然後,我們將游戲移植到網絡上。

使用處理語言構建一個簡單的遊戲。

在我們開始處理教程之前,這裡是上一部分的 DVD 徽標練習的代碼。 如果您有任何問題,請務必發表評論。

處理教程:一個簡單的遊戲

我們將在本處理教程中構建的遊戲是 Flappy Bird、Pong 和 Brick Breaker 的組合。 我之所以選擇這樣一款遊戲,是因為它包含了初學者在學習遊戲開發時難以理解的大部分概念。 這是基於我當助教時的經驗,幫助新程序員學習如何使用 Processing。 這些概念包括重力、碰撞、記分、處理不同的屏幕和鍵盤/鼠標交互。 Flappy Pong 裡面都有。

現在玩遊戲!

如果不使用面向對象編程 (OOP) 的概念,構建複雜的遊戲並不容易,例如具有多個關卡、玩家、實體等的平台遊戲。隨著我們的深入,您將看到代碼是如何迅速變得複雜的。 我盡我所能保持這個處理教程的組織和簡單。

我建議你按照這篇文章,獲取完整的代碼,自己玩,盡快開始思考你自己的遊戲,然後開始實現它。

那麼讓我們開始吧。

建造 Flappy Pong

處理教程步驟#1:初始化和處理不同的屏幕

第一步是初始化我們的項目。 對於初學者,我們將像往常一樣編寫設置和繪製塊,沒有什麼花哨的或新的。 然後,我們將處理不同的屏幕(初始屏幕、遊戲屏幕、遊戲結束屏幕等)。 那麼問題來了,我們如何讓 Processing 在正確的時間顯示正確的頁面呢?

完成這項任務相當簡單。 我們將有一個全局變量來存儲當前活動屏幕的信息。 然後我們根據變量繪製正確屏幕的內容。 在繪圖塊中,我們將有一個 if 語句來檢查變量並相應地顯示屏幕的內容。 每當我們想要更改屏幕時,我們都會將該變量更改為我們希望它顯示的屏幕標識符。 話雖如此,我們的骨架代碼如下所示:

 /********* VARIABLES *********/ // We control which screen is active by settings / updating // gameScreen variable. We display the correct screen according // to the value of this variable. // // 0: Initial Screen // 1: Game Screen // 2: Game-over Screen int gameScreen = 0; /********* SETUP BLOCK *********/ void setup() { size(500, 500); } /********* DRAW BLOCK *********/ void draw() { // Display the contents of the current screen if (gameScreen == 0) { initScreen(); } else if (gameScreen == 1) { gameScreen(); } else if (gameScreen == 2) { gameOverScreen(); } } /********* SCREEN CONTENTS *********/ void initScreen() { // codes of initial screen } void gameScreen() { // codes of game screen } void gameOverScreen() { // codes for game over screen } /********* INPUTS *********/ public void mousePressed() { // if we are on the initial screen when clicked, start the game if (gameScreen==0) { startGame(); } } /********* OTHER FUNCTIONS *********/ // This method sets the necessary variables to start the game void startGame() { gameScreen=1; }

乍一看這可能看起來很可怕,但我們所做的只是構建基本結構並用註釋塊分隔不同的部分。

如您所見,我們為每個屏幕定義了不同的顯示方法。 在我們的繪圖塊中,我們只需檢查gameScreen變量的值,然後調用相應的方法。

void mousePressed(){...}部分,我們正在監聽鼠標點擊,如果活動屏幕為 0,即初始屏幕,我們調用startGame()方法,它會按您的預期開始遊戲。 該方法的第一行將gameScreen變量更改為 1,即遊戲畫面。

如果理解了這一點,下一步就是實現我們的初始屏幕。 為此,我們將編輯initScreen()方法。 它是這樣的:

 void initScreen() { background(0); textAlign(CENTER); text("Click to start", height/2, width/2); }

現在我們的初始屏幕有一個黑色背景和一個簡單的文本“點擊開始”,位於中間並與中心對齊。 但是當我們點擊時,什麼也沒有發生。 我們還沒有為我們的遊戲畫面指定任何內容。 gameScreen()方法中沒有任何內容,因此我們不會通過將background()作為第一行繪製來覆蓋從最後一個屏幕(文本)繪製的先前內容。 這就是為什麼文本仍然存在的原因,即使不再調用text()(就像上一部分中留下痕蹟的移動球示例一樣) 。 出於同樣的原因,背景仍然是黑色的。 所以讓我們繼續並開始實現遊戲畫面。

 void gameScreen() { background(255); }

進行此更改後,您會注意到背景變為白色並且文本消失了。

處理教程步驟#2:創建球和實施重力

現在,我們將開始在遊戲屏幕上工作。 我們將首先創建我們的球。 我們應該為其坐標、顏色和大小定義變量,因為我們可能希望稍後更改這些值。 例如,如果我們想在球員得分越高時增加球的大小,從而使比賽變得更難。 我們需要改變它的大小,所以它應該是一個變量。 在我們實現重力之後,我們還將定義球的速度。

首先,讓我們添加以下內容:

 ... int ballX, ballY; int ballSize = 20; int ballColor = color(0); ... void setup() { ... ballX=width/4; ballY=height/5; } ... void gameScreen() { ... drawBall(); } ... void drawBall() { fill(ballColor); ellipse(ballX, ballY, ballSize, ballSize); }

我們將坐標定義為全局變量,創建了一個繪製球的方法,從gameScreen方法中調用。 這裡唯一需要注意的是我們初始化了坐標,但是我們在setup()中定義了它們。 我們這樣做的原因是我們希望球從左四分之一和上五分之一開始。 我們沒有特別想要這樣的理由,但這是一個很好的開始點。 所以我們需要動態獲取草圖的widthheight 。 草圖大小在setup()中定義,在第一行之後。 在setup()運行之前沒有設置widthheight ,這就是為什麼我們在頂部定義變量時無法實現這一點的原因。

重力

現在實現重力實際上是容易的部分。 只有幾個技巧。 首先是實現:

 ... float gravity = 1; float ballSpeedVert = 0; ... void gameScreen() { ... applyGravity(); keepInScreen(); } ... void applyGravity() { ballSpeedVert += gravity; ballY += ballSpeedVert; } void makeBounceBottom(float surface) { ballY = surface-(ballSize/2); ballSpeedVert*=-1; } void makeBounceTop(float surface) { ballY = surface+(ballSize/2); ballSpeedVert*=-1; } // keep ball in the screen void keepInScreen() { // ball hits floor if (ballY+(ballSize/2) > height) { makeBounceBottom(height); } // ball hits ceiling if (ballY-(ballSize/2) < 0) { makeBounceTop(0); } }

結果是:

一個在偽重力下無限彈跳的球。

抓住你的馬,物理學家。 我知道這不是重力在現實生活中的作用方式。 相反,這更像是一個動畫過程。 我們定義為gravity的變量只是一個數值——一個float ,以便我們可以使用十進制值,而不僅僅是整數——我們在每個循環中添加到ballSpeedVert 。 而ballSpeedVert是小球的垂直速度,在每個循環中都與小球的Y 坐標( ballY )相加。 我們觀察球的坐標並確保它停留在屏幕上。 如果我們不這樣做,球就會落到無窮遠。 目前,我們的球只能垂直移動。 所以我們觀察屏幕的地板和天花板邊界。 使用keepInScreen()方法,我們檢查ballY ( +半徑) 是否小於height ,類似地ballY ( -半徑) 是否大於0 。 如果條件不滿足,我們使用makeBounceBottom()makeBounceTop()方法讓球彈起(從底部或頂部)。 為了讓球反彈,我們只需將球移動到它必須反彈的確切位置,並將垂直速度 ( ballSpeedVert ) 乘以-1 (乘以 -1 會改變符號)。 當速度值有負號時,加上 Y 坐標,速度變為ballY + (-ballSpeedVert) ,即ballY - ballSpeedVert 。 所以球立即以相同的速度改變方向。 然後,當我們將gravity添加到ballSpeedVert並且ballSpeedVert具有負值時,它開始接近0 ,最終變為0 ,並再次開始增加。 這使球上升,上升緩慢,停止並開始下降。

球在球拍上無限彈跳。

但是,我們的動畫過程存在一個問題——球一直在彈跳。 如果這是真實世界的場景,球每次接觸表面時都會面臨空氣阻力和摩擦。 這就是我們想要的遊戲動畫過程的行為,所以實現它很容易。 我們添加以下內容:

 ... float airfriction = 0.0001; float friction = 0.1; ... void applyGravity() { ... ballSpeedVert -= (ballSpeedVert * airfriction); } void makeBounceBottom(int surface) { ... ballSpeedVert -= (ballSpeedVert * friction); } void makeBounceTop(int surface) { ... ballSpeedVert -= (ballSpeedVert * friction); }

現在我們的動畫過程產生了這個:

球彈跳但由於摩擦而停止。

顧名思義, friction是表面摩擦,空氣airfriction是空氣的摩擦。 所以很明顯,每次球接觸任何表面時都必須施加friction 。 然而, airfriction必須不斷地應用。 這就是我們所做的。 applyGravity()方法在每個循環上運行,因此我們在每個循環上從ballSpeedVert中取出其當前值的0.0001 %。 makeBounceBottom()makeBounceTop()方法在球接觸任何表面時運行。 所以在那些方法中,我們做了同樣的事情,只是這次是friction

處理教程步驟#3:創建球拍

現在我們需要一個球拍讓球彈起來。 我們應該控制球拍。 讓我們用鼠標控制它。 這是代碼:

 ... color racketColor = color(0); float racketWidth = 100; float racketHeight = 10; ... void gameScreen() { ... drawRacket(); ... } ... void drawRacket(){ fill(racketColor); rectMode(CENTER); rect(mouseX, mouseY, racketWidth, racketHeight); }

我們將球拍的顏色、寬度和高度定義為全局變量,我們可能希望它們在遊戲過程中發生變化。 我們實現了一個方法drawRacket() ,正如它的名字所暗示的那樣。 我們將rectMode設置為 center,因此我們的球拍與光標的中心對齊。

現在我們創建了球拍,我們必須讓球在其上彈跳。

 ... int racketBounceRate = 20; ... void gameScreen() { ... watchRacketBounce(); ... } ... void watchRacketBounce() { float overhead = mouseY - pmouseY; if ((ballX+(ballSize/2) > mouseX-(racketWidth/2)) && (ballX-(ballSize/2) < mouseX+(racketWidth/2))) { if (dist(ballX, ballY, ballX, mouseY)<=(ballSize/2)+abs(overhead)) { makeBounceBottom(mouseY); // racket moving up if (overhead<0) { ballY+=overhead; ballSpeedVert+=overhead; } } } }

結果如下:

球在球拍上彈跳,但由於摩擦而停止。

所以watchRacketBounce()所做的是確保球拍和球發生碰撞。 這裡有兩件事要檢查,那就是球和球拍是否垂直和水平排列。 第一個 if 語句檢查球右側的 X 坐標是否大於球拍左側的 X 坐標(反之亦然)。 如果是,第二個語句檢查球和球拍之間的距離是否小於或等於球的半徑(這意味著它們正在碰撞) 。 因此,如果滿足這些條件,將調用makeBounceBottom()方法並且球在我們的球拍上彈跳(在mouseY ,球拍所在的位置)。

您是否注意到由mouseY - pmouseY計算的可變overheadpmouseXpmouseY變量存儲鼠標在前一幀的坐標。 由於鼠標的移動速度非常快,如果鼠標向球移動的速度足夠快,我們很有可能無法在幀之間正確檢測到球和球拍之間的距離。 因此,我們會考慮幀之間鼠標坐標的差異,並在檢測距離時將其考慮在內。 鼠標移動得越快,可接受的距離就越大。

我們還出於另一個原因使用overhead 。 我們通過檢查overhead的符號來檢測鼠標移動的方向。 如果開銷為負,則鼠標位於前一幀下方的某個位置,因此我們的鼠標(球拍)正在向上移動。 在這種情況下,我們希望為球增加額外的速度,並使其比常規彈跳更遠一些,以模擬用球拍擊球的效果。 如果overhead小於0 ,我們將其添加到ballYballSpeedVert以使球飛得更高更快。 所以球拍擊球的速度越快,它向上移動的速度就越高。

處理教程步驟#4:水平運動和控制球

在本節中,我們將為球添加水平運動。 然後,我們將可以用我們的球拍水平控制球。 開始了:

 ... // we will start with 0, but for we give 10 just for testing float ballSpeedHorizon = 10; ... void gameScreen() { ... applyHorizontalSpeed(); ... } ... void applyHorizontalSpeed(){ ballX += ballSpeedHorizon; ballSpeedHorizon -= (ballSpeedHorizon * airfriction); } void makeBounceLeft(float surface){ ballX = surface+(ballSize/2); ballSpeedHorizon*=-1; ballSpeedHorizon -= (ballSpeedHorizon * friction); } void makeBounceRight(float surface){ ballX = surface-(ballSize/2); ballSpeedHorizon*=-1; ballSpeedHorizon -= (ballSpeedHorizon * friction); } ... void keepInScreen() { ... if (ballX-(ballSize/2) < 0){ makeBounceLeft(0); } if (ballX+(ballSize/2) > width){ makeBounceRight(width); } }

結果是:

一個球現在也水平彈跳。

這裡的想法與我們為垂直移動所做的相同。 我們創建了一個水平速度變量ballSpeedHorizon 。 我們創建了一種將水平速度應用於ballX並消除空氣摩擦的方法。 我們在keepInScreen()方法中添加了另外兩個 if 語句,它們將觀察球撞擊屏幕的左右邊緣。 最後,我們創建了makeBounceLeft()makeBounceRight()方法來處理左右反彈。

現在我們在遊戲中添加了水平速度,我們想用球拍控制球。 與著名的 Atari 遊戲 Breakout 和所有其他破磚遊戲一樣,球應根據其擊中球拍上的點向左或向右移動。 球拍的邊緣應該給球更多的水平速度,而中間不應該有任何影響。 先上代碼:

 void watchRacketBounce() { ... if ((ballX+(ballSize/2) > mouseX-(racketWidth/2)) && (ballX-(ballSize/2) < mouseX+(racketWidth/2))) { if (dist(ballX, ballY, ballX, mouseY)<=(ballSize/2)+abs(overhead)) { ... ballSpeedHorizon = (ballX - mouseX)/5; ... } } }

結果是:

突破式水平物理。

將這條簡單的行添加到watchRacketBounce()就可以了。 我們所做的是我們用ballX - mouseX確定了球擊中點到球拍中心的距離。 然後,我們將其設為水平速度。 實際差異太大,所以我試了幾次,覺得十分之一的值感覺最自然。

處理教程步驟#5:創建牆壁

我們的草圖開始看起來更像是每一步的遊戲。 在這一步中,我們將添加向左移動的牆壁,就像在 Flappy Bird 中一樣:

 ... int wallSpeed = 5; int wallInterval = 1000; float lastAddTime = 0; int minGapHeight = 200; int maxGapHeight = 300; int wallWidth = 80; color wallColors = color(0); // This arraylist stores data of the gaps between the walls. Actuals walls are drawn accordingly. // [gapWallX, gapWallY, gapWallWidth, gapWallHeight] ArrayList<int[]> walls = new ArrayList<int[]>(); ... void gameScreen() { ... wallAdder(); wallHandler(); } ... void wallAdder() { if (millis()-lastAddTime > wallInterval) { int randHeight = round(random(minGapHeight, maxGapHeight)); int randY = round(random(0, height-randHeight)); // {gapWallX, gapWallY, gapWallWidth, gapWallHeight} int[] randWall = {width, randY, wallWidth, randHeight}; walls.add(randWall); lastAddTime = millis(); } } void wallHandler() { for (int i = 0; i < walls.size(); i++) { wallRemover(i); wallMover(i); wallDrawer(i); } } void wallDrawer(int index) { int[] wall = walls.get(index); // get gap wall settings int gapWallX = wall[0]; int gapWallY = wall[1]; int gapWallWidth = wall[2]; int gapWallHeight = wall[3]; // draw actual walls rectMode(CORNER); fill(wallColors); rect(gapWallX, 0, gapWallWidth, gapWallY); rect(gapWallX, gapWallY+gapWallHeight, gapWallWidth, height-(gapWallY+gapWallHeight)); } void wallMover(int index) { int[] wall = walls.get(index); wall[0] -= wallSpeed; } void wallRemover(int index) { int[] wall = walls.get(index); if (wall[0]+wall[2] <= 0) { walls.remove(index); } }

這導致:

一個球在有牆壁的水平面上彈跳。

儘管代碼看起來很長而且令人生畏,但我保證沒有什麼難以理解的。 首先要注意的是ArrayList 。 對於那些不知道ArrayList是什麼的人來說,它只是一個 list 的實現,就像一個 Array,但它比它有一些優勢。 它是可調整大小的,它有一些有用的方法,比如list.add(index)list.get(index)list.remove(index) 。 我們將牆壁數據作為整數數組保存在數組列表中。 我們保存在數組中的數據是關於兩堵牆之間的差距。 數組包含以下值:

 [gap wall X, gap wall Y, gap wall width, gap wall height]

實際牆是根據間隙牆值繪製的。 請注意,使用類可以更好、更簡潔地處理所有這些問題,但由於面向對象編程 (OOP) 的使用不在本處理教程的範圍內,因此我們將採用這種方式處理它。 我們有兩個管理牆壁的基本方法, wallAdder()wallHandler

wallAdder()方法只是在每個wallInterval毫秒內將新牆添加到數組列表中。 我們有一個全局變量lastAddTime ,它存儲添加最後一面牆的時間(以毫秒為單位) 。 如果當前毫秒millis()減去最後添加的毫秒lastAddTime大於我們的間隔值wallInterval ,則意味著現在是時候添加新牆了。 然後根據最頂部定義的全局變量生成隨機間隙變量。 然後將新牆(存儲間隙牆數據的整數數組)添加到數組列表中,並將lastAddTime設置為當前毫秒millis()

wallHandler()循環遍歷數組列表中的當前牆。 對於每個循環中的每個項目,它通過數組列表的索引值調用wallRemover(i)wallMover(i)wallDrawer(i) 。 這些方法正如其名稱所暗示的那樣。 wallDrawer()根據間隙牆壁數據繪製實際牆壁。 它從 arraylist 中獲取牆壁數據數組,並調用rect()方法將牆壁繪製到它們實際應該在的位置。 wallMover()方法從數組列表中獲取元素,根據wallSpeed全局變量更改其 X 位置。 最後, wallRemover()從數組列表中移除屏幕外的牆壁。 如果我們不這樣做,Processing 就會將它們視為仍在屏幕上。 這將是性能的巨大損失。 因此,當從數組列表中刪除一堵牆時,它不會在後續循環中繪製。

剩下要做的最後一項具有挑戰性的事情是檢測球和牆壁之間的碰撞。

 void wallHandler() { for (int i = 0; i < walls.size(); i++) { ... watchWallCollision(i); } } ... void watchWallCollision(int index) { int[] wall = walls.get(index); // get gap wall settings int gapWallX = wall[0]; int gapWallY = wall[1]; int gapWallWidth = wall[2]; int gapWallHeight = wall[3]; int wallTopX = gapWallX; int wallTopY = 0; int wallTopWidth = gapWallWidth; int wallTopHeight = gapWallY; int wallBottomX = gapWallX; int wallBottomY = gapWallY+gapWallHeight; int wallBottomWidth = gapWallWidth; int wallBottomHeight = height-(gapWallY+gapWallHeight); if ( (ballX+(ballSize/2)>wallTopX) && (ballX-(ballSize/2)<wallTopX+wallTopWidth) && (ballY+(ballSize/2)>wallTopY) && (ballY-(ballSize/2)<wallTopY+wallTopHeight) ) { // collides with upper wall } if ( (ballX+(ballSize/2)>wallBottomX) && (ballX-(ballSize/2)<wallBottomX+wallBottomWidth) && (ballY+(ballSize/2)>wallBottomY) && (ballY-(ballSize/2)<wallBottomY+wallBottomHeight) ) { // collides with lower wall } }

watchWallCollision()方法在每個循環中為每個牆調用。 我們抓取縫隙牆壁的坐標,計算實際牆壁(頂部和底部)的坐標,然後檢查球的坐標是否與牆壁發生碰撞。

處理教程步驟#6:健康和分數

現在我們可以檢測到球和牆壁的碰撞,我們可以決定遊戲機制。 在對遊戲進行了一些調整後,我設法使遊戲變得有些可玩。 但是,這仍然非常困難。 我對遊戲的第一個想法是讓它像 Flappy Bird 一樣,當球碰到牆壁時,遊戲結束。 但後來我意識到這是不可能玩的。 所以這就是我的想法:

球的頂部應該有一個健康欄。 球在接觸牆壁時應該失去健康。 按照這個邏輯,讓球從牆上反彈回來是沒有意義的。 所以當生命值為0時,遊戲應該結束,我們應該切換到遊戲結束屏幕。 所以我們開始:

 int maxHealth = 100; float health = 100; float healthDecrease = 1; int healthBarWidth = 60; ... void gameScreen() { ... drawHealthBar(); ... } ... void drawHealthBar() { // Make it borderless: noStroke(); fill(236, 240, 241); rectMode(CORNER); rect(ballX-(healthBarWidth/2), ballY - 30, healthBarWidth, 5); if (health > 60) { fill(46, 204, 113); } else if (health > 30) { fill(230, 126, 34); } else { fill(231, 76, 60); } rectMode(CORNER); rect(ballX-(healthBarWidth/2), ballY - 30, healthBarWidth*(health/maxHealth), 5); } void decreaseHealth(){ health -= healthDecrease; if (health <= 0){ gameOver(); } }

這是一個簡單的運行:

一個帶有健康條的球在關卡中彈跳,每當它與牆壁碰撞時都會失去健康。

我們創建了一個全局變量health來保持球的健康。 然後創建了一個方法drawHealthBar()在球的頂部繪製兩個矩形。 第一個是基礎生命值條,另一個是顯示當前生命值的活動條。 第二個的寬度是動態的,使用healthBarWidth*(health/maxHealth)計算,即我們當前的健康狀況與健康欄寬度的比率。 最後,根據健康值設置填充顏色。 最後但並非最不重要的,分數

 ... void gameOverScreen() { background(0); textAlign(CENTER); fill(255); textSize(30); text("Game Over", height/2, width/2 - 20); textSize(15); text("Click to Restart", height/2, width/2 + 10); } ... void wallAdder() { if (millis()-lastAddTime > wallInterval) { ... // added another value at the end of the array int[] randWall = {width, randY, wallWidth, randHeight, 0}; ... } } void watchWallCollision(int index) { ... int wallScored = wall[4]; ... if (ballX > gapWallX+(gapWallWidth/2) && wallScored==0) { wallScored=1; wall[4]=1; score(); } } void score() { score++; } void printScore(){ textAlign(CENTER); fill(0); textSize(30); text(score, height/2, 50); }

當球越過牆壁時,我們需要得分。 但是我們需要為每面牆添加最多 1 分。 意思是,如果球通過牆壁而不是返回並再次傳遞,則不應添加另一個分數。 為了實現這一點,我們在arraylist 中的gap wall 數組中添加了另一個變量。 如果球還沒有通過那堵牆,則新變量存儲0 ,如果已經通過,則存儲1 。 然後,我們修改了watchWallCollision()方法。 我們添加了一個觸發score()方法的條件,並在球通過之前未通過的牆時將牆標記為通過。

我們現在非常接近尾聲。 最後要做的是在遊戲結束屏幕上實現click to restart 。 我們需要將我們使用的所有變量設置為初始值,然後重新開始遊戲。 這裡是:

 ... public void mousePressed() { ... if (gameScreen==2){ restart(); } } ... void restart() { score = 0; health = maxHealth; ballX=width/4; ballY=height/5; lastAddTime = 0; walls.clear(); gameScreen = 0; }

讓我們添加更多顏色。

完整的 Flappy Pong 全彩。

瞧! 我們有 Flappy Pong!

完整的處理遊戲代碼可以在這裡找到。

使用 p5.js 將處理遊戲代碼移植到 Web

p5.j​​s 是一個 JavaScript 庫,其語法與 Processing 編程語言的語法非常相似。 它不是一個能夠簡單地運行現有處理代碼的庫; 相反,p5.js 需要編寫實際的 JavaScript 代碼——類似於被稱為 Processing.js 的 Processing 的 JavaScript 端口。 我們的任務是使用 p5.js API 將處理代碼轉換為 JavaScript。 該庫有一組函數和類似於 Processing 的語法,我們必須對我們的代碼進行某些更改以使它們在 JavaScript 中工作——但由於 Processing 和 JavaScript 與 Java 有相似之處,它不像聽起來那麼簡單. 即使您不是 JavaScript 開發人員,這些更改也是非常微不足道的,您應該能夠很好地跟進。

首先,我們需要創建一個簡單的index.html並將p5.min.js添加到我們的標題中。 我們還需要創建另一個名為flappy_pong.js的文件,它將存放我們轉換後的代碼。

 <html> <head> <title>Flappy Pong</title> <script tyle="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.19/p5.min.js"></script> <script tyle="text/javascript" src="flappy_pong.js"></script> <style> canvas { box-shadow: 0 0 20px lightgray; } </style> </head> <body> </body> </html>

我們在轉換代碼時的策略應該是將所有代碼複製並粘貼到flappy_pong.js ,然後進行所有更改。 這就是我所做的。 以下是我更新代碼的步驟:

  • Javascript 是一種無類型語言(沒有像intfloat這樣的類型聲明)。 所以我們需要將所有類型聲明更改為var

  • Javascript 中沒有void 。 我們應該改變所有的function

  • 我們需要從函數簽名中刪除參數的類型聲明。 (即void wallMover(var index) { to function wallMover(index) { )

  • JavaScript 中沒有ArrayList 。 但是我們可以使用 JavaScript 數組來實現同樣的目的。 我們進行以下更改:

    • ArrayList<int[]> walls = new ArrayList<int[]>();var walls = [];
    • walls.clear();walls = [];
    • walls.add(randWall);walls.push(randWall);
    • walls.remove(index);walls.splice(index,1);
    • walls.get(index);walls[index]
    • walls.size()walls.length
  • 更改數組的聲明var randWall = {width, randY, wallWidth, randHeight, 0};var randWall = [width, randY, wallWidth, randHeight, 0];

  • 刪除所有public關鍵字。

  • 將所有color(0)聲明移動到function setup()中,因為在setup()調用之前不會定義color()

  • 改變size(500, 500); createCanvas(500, 500);

  • function gameScreen(){ function gamePlayScreen(){因為我們已經有一個名為gameScreen的全局變量。 當我們使用 Processing 時,一個是函數,另一個是int變量。 但是 JavaScript 混淆了這些,因為它們是無類型的。

  • score()也是如此。 我將其重命名為addScore()

可以在此處找到涵蓋本處理教程中所有內容的完整 JavaScript 代碼。

處理遊戲代碼:你也可以

在這個處理教程中,我試圖解釋如何構建一個非常簡單的遊戲。 然而,我們在本文中所做的只是冰山一角。 使用 Processing 編程語言,幾乎可以實現任何目標。 在我看來,它是對您的想像進行編程的最佳工具。 我對這個處理教程的實際意圖不是教處理和構建遊戲,而是證明編程並不難。 建立自己的遊戲不僅僅是一個夢想。 我想告訴你,只要付出一點努力和熱情,你就可以做到。 我真的希望這兩篇文章能激勵大家嘗試編程。