處理語言終極指南第二部分:構建一個簡單的遊戲
已發表: 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()
中定義了它們。 我們這樣做的原因是我們希望球從左四分之一和上五分之一開始。 我們沒有特別想要這樣的理由,但這是一個很好的開始點。 所以我們需要動態獲取草圖的width
和height
。 草圖大小在setup()
中定義,在第一行之後。 在setup()
運行之前沒有設置width
和height
,這就是為什麼我們在頂部定義變量時無法實現這一點的原因。
重力
現在實現重力實際上是容易的部分。 只有幾個技巧。 首先是實現:
... 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
計算的可變overhead
? pmouseX
和pmouseY
變量存儲鼠標在前一幀的坐標。 由於鼠標的移動速度非常快,如果鼠標向球移動的速度足夠快,我們很有可能無法在幀之間正確檢測到球和球拍之間的距離。 因此,我們會考慮幀之間鼠標坐標的差異,並在檢測距離時將其考慮在內。 鼠標移動得越快,可接受的距離就越大。
我們還出於另一個原因使用overhead
。 我們通過檢查overhead
的符號來檢測鼠標移動的方向。 如果開銷為負,則鼠標位於前一幀下方的某個位置,因此我們的鼠標(球拍)正在向上移動。 在這種情況下,我們希望為球增加額外的速度,並使其比常規彈跳更遠一些,以模擬用球拍擊球的效果。 如果overhead
小於0
,我們將其添加到ballY
和ballSpeedVert
以使球飛得更高更快。 所以球拍擊球的速度越快,它向上移動的速度就越高。
處理教程步驟#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!
完整的處理遊戲代碼可以在這裡找到。
使用 p5.js 將處理遊戲代碼移植到 Web
p5.js 是一個 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 是一種無類型語言(沒有像
int
和float
這樣的類型聲明)。 所以我們需要將所有類型聲明更改為var
。Javascript 中沒有
void
。 我們應該改變所有的function
。我們需要從函數簽名中刪除參數的類型聲明。 (即
void wallMover(var index) {
tofunction 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 編程語言,幾乎可以實現任何目標。 在我看來,它是對您的想像進行編程的最佳工具。 我對這個處理教程的實際意圖不是教處理和構建遊戲,而是證明編程並不難。 建立自己的遊戲不僅僅是一個夢想。 我想告訴你,只要付出一點努力和熱情,你就可以做到。 我真的希望這兩篇文章能激勵大家嘗試編程。