นี่คือส่วนที่สองของคู่มือขั้นสูงสุดสำหรับภาษาประมวลผล ใน ส่วนแรก ฉันให้คำแนะนำแบบการประมวลผลภาษาพื้นฐาน ขั้นตอนต่อไปสำหรับคุณในการเรียนรู้การประมวลผลเป็นเพียงการเขียนโปรแกรมแบบลงมือปฏิบัติ
ในบทความนี้ฉันจะแสดงวิธีใช้การประมวลผลเพื่อใช้เกมของคุณเองทีละขั้นตอน แต่ละขั้นตอนจะอธิบายโดยละเอียด จากนั้นเราจะพอร์ตเกมไปยังเว็บ
ก่อนที่เราจะเริ่มการสอนการประมวลผล ที่นี่ คือรหัสของแบบฝึกหัดโลโก้ DVD จากส่วนก่อนหน้า หากคุณมีคำถามใด ๆ อย่าลืมแสดงความคิดเห็น
เกม เราจะสร้างในบทช่วยสอนการประมวลผลนี้เป็นการผสมผสานระหว่าง Flappy Bird, Pong และ Brick Breaker เหตุผลที่ฉันเลือกเกมแบบนี้ก็คือมันมีแนวคิดส่วนใหญ่ที่ผู้เริ่มต้นต่อสู้ด้วยเมื่อเรียนรู้การพัฒนาเกม สิ่งนี้มาจากประสบการณ์ของฉันตั้งแต่ตอนที่ฉันเป็นผู้ช่วยสอนซึ่งช่วยให้โปรแกรมเมอร์มือใหม่เรียนรู้วิธีใช้การประมวลผล แนวคิดเหล่านี้รวมถึงแรงโน้มถ่วงการชนการรักษาคะแนนการจัดการหน้าจอที่แตกต่างกันและการโต้ตอบของแป้นพิมพ์ / เมาส์ Flappy Pong มีทั้งหมดอยู่ในนั้น
หากไม่ใช้แนวคิดการเขียนโปรแกรมเชิงวัตถุ (OOP) จึงไม่ใช่เรื่องง่ายที่จะสร้างเกมที่ซับซ้อนเช่นเกมแพลตฟอร์มที่มีหลายระดับผู้เล่นเอนทิตี ฯลฯ ในขณะที่เราดำเนินการต่อไปคุณจะเห็นว่าโค้ดมีความซับซ้อนรวดเร็วเพียงใด ฉันพยายามอย่างเต็มที่เพื่อให้บทเรียนการประมวลผลนี้เป็นระเบียบและเรียบง่าย
ฉันแนะนำให้คุณทำตามบทความคว้าโค้ดทั้งหมดเล่นด้วยตัวคุณเองเริ่มคิดถึงเกมของคุณเองโดยเร็วที่สุดและเริ่มใช้งานได้
มาเริ่มกันเลย
ขั้นตอนแรกคือการเริ่มต้นโครงการของเรา สำหรับผู้เริ่มต้นเราจะเขียนการตั้งค่าและวาดบล็อกตามปกติไม่มีอะไรแปลกใหม่หรือแปลกใหม่ จากนั้นเราจะจัดการหน้าจอต่างๆ (หน้าจอเริ่มต้นหน้าจอเกมเกมบนหน้าจอ ฯลฯ ) จึงเกิดคำถามว่าเราจะให้ 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); }
หลังจากการเปลี่ยนแปลงนี้คุณจะสังเกตเห็นว่าพื้นหลังเปลี่ยนเป็นสีขาวและข้อความจะหายไป
ตอนนี้เราจะเริ่มทำงานบนหน้าจอเกม ก่อนอื่นเราจะสร้างลูกบอลของเรา เราควรกำหนดตัวแปรสำหรับพิกัดสีและขนาดเพราะเราอาจต้องการเปลี่ยนค่าเหล่านั้นในภายหลัง ตัวอย่างเช่นหากเราต้องการเพิ่มขนาดของลูกบอลเมื่อผู้เล่นทำคะแนนได้สูงขึ้นเพื่อที่เกมจะยากขึ้น เราจะต้องเปลี่ยนขนาดจึงควรเป็นตัวแปร เราจะกำหนดความเร็วของลูกบอลด้วยหลังจากที่เราใช้แรงโน้มถ่วง
ขั้นแรกให้เพิ่มสิ่งต่อไปนี้:
วิธีรับ m3u8 url
... 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()
หลังจากบรรทัดแรก width
และ height
ไม่ได้ตั้งค่าไว้ก่อน setup()
นั่นคือสาเหตุที่เราไม่สามารถบรรลุเป้าหมายนี้ได้หากเรากำหนดตัวแปรไว้ด้านบน
ตอนนี้การใช้แรงโน้มถ่วงเป็นส่วนที่ง่าย มีเพียงเทคนิคเล็กน้อย นี่คือการนำไปใช้ก่อน:
... 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
เป็นเพียงค่าตัวเลข - a 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()
วิธีการทำงานในแต่ละลูปดังนั้นเราจึงนำ 0.0001
เปอร์เซ็นต์ของมูลค่าปัจจุบันจาก ballSpeedVert
ในทุกวง makeBounceBottom()
และ makeBounceTop()
วิธีการทำงานเมื่อลูกบอลสัมผัสพื้นผิวใด ๆ ดังนั้นในวิธีการเหล่านั้นเราได้ทำสิ่งเดียวกันในครั้งนี้ด้วย friction
ตอนนี้เราต้องการไม้เพื่อให้ลูกบอลกระเด้ง เราควรจะควบคุมแร็กเกต มาทำให้มันควบคุมได้ด้วยเมาส์ นี่คือรหัส:
... 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
ไปที่กึ่งกลางดังนั้นแร็กเกตของเราจึงถูกจัดให้อยู่ตรงกลางเคอร์เซอร์ของเรา
ตอนนี้เราสร้างแร็กเกตแล้วเราต้องทำให้ลูกบอลเด้งขึ้นมา
... int racketBounceRate = 20; ... void gameScreen() { ... watchRacketBounce(); ... } ... void watchRacketBounce() { float overhead = mouseY - pmouseY; if ((ballX+(ballSize/2) > mouseX-(racketWidth/2)) && (ballX-(ballSize/2) และนี่คือผลลัพธ์:

แล้วอะไรล่ะ watchRacketBounce()
ตรวจสอบให้แน่ใจหรือไม่ว่าแร็กเก็ตและลูกบอลชนกัน มีสองสิ่งที่ต้องตรวจสอบที่นี่ซึ่งก็คือถ้าลูกบอลและแร็กเกตเรียงกันทั้งแนวตั้งและแนวนอน คำสั่ง if แรกจะตรวจสอบว่าพิกัด X ของด้านขวาของลูกมากกว่าพิกัด X ของด้านซ้ายของไม้ (และอีกทางหนึ่ง) ถ้าเป็นเช่นนั้นคำสั่งที่สองจะตรวจสอบว่าระยะห่างระหว่างลูกบอลกับแร็กเกตน้อยกว่าหรือเท่ากับรัศมีของลูกบอลหรือไม่ (ซึ่งหมายความว่าพวกเขากำลังชนกัน) . ดังนั้นหากตรงตามเงื่อนไขเหล่านี้ makeBounceBottom()
วิธีการเรียกและลูกบอลจะกระเด้งบนแร็กเกตของเรา (ที่ mouseY
โดยที่แร็กเก็ตอยู่)
คุณสังเกตเห็นตัวแปร overhead
ซึ่งคำนวณโดย mouseY - pmouseY
? 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) width){ makeBounceRight(width); } }
และผลลัพธ์คือ:

แนวคิดตรงนี้เหมือนกับสิ่งที่เราทำสำหรับการเคลื่อนที่ในแนวตั้ง เราสร้างตัวแปรความเร็วแนวนอน ballSpeedHorizon
เราได้สร้างวิธีการใช้ความเร็วแนวนอนกับ ballX
และกำจัดแรงเสียดทานของอากาศ เราได้เพิ่มอีกสองคำสั่ง if ใน keepInScreen()
วิธีการที่จะดูบอลสำหรับการตีขอบซ้ายและขวาของหน้าจอ ในที่สุดเราก็สร้าง makeBounceLeft()
และ makeBounceRight()
วิธีจัดการกับการตีกลับจากซ้ายและขวา
ตอนนี้เราได้เพิ่มความเร็วในแนวนอนให้กับเกมแล้วเราต้องการควบคุมลูกบอลด้วยไม้ เช่นเดียวกับในเกม Atari ที่มีชื่อเสียง ฝ่าวงล้อม และในเกมทำลายอิฐอื่น ๆ ทั้งหมดลูกบอลควรไปทางซ้ายหรือขวาตามจุดบนแร็กเกตที่ตี ขอบของแร็กเกตควรให้ความเร็วในแนวนอนของลูกมากขึ้นในขณะที่ตรงกลางไม่ควรมีผลใด ๆ รหัสแรก:
void watchRacketBounce() { ... if ((ballX+(ballSize/2) > mouseX-(racketWidth/2)) && (ballX-(ballSize/2) ผลลัพธ์คือ:

การเพิ่มบรรทัดง่ายๆลงใน 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 walls = new ArrayList(); ... 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 และสิ่งนี้ส่งผลให้:

แม้ว่ารหัสจะดูยาวและน่ากลัว แต่ฉันสัญญาว่าไม่มีอะไรยากที่จะเข้าใจ สิ่งแรกที่ต้องสังเกตคือ 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]
ผนังจริงวาดตามค่าผนังช่องว่าง โปรดทราบว่าสิ่งเหล่านี้สามารถจัดการได้ดีกว่าและสะอาดกว่าโดยใช้คลาส แต่เนื่องจากการใช้ Object Oriented Programming (OOP) ไม่ได้อยู่ในขอบเขตของบทช่วยสอนการประมวลผลนี้เราจึงจะจัดการกับมัน เรามีสองวิธีพื้นฐานในการจัดการกำแพง wallAdder()
และ wallHandler
.
wallAdder()
เพียงแค่เพิ่มกำแพงใหม่ในทุกๆ wallInterval
มิลลิวินาทีไปยังรายการอาร์เรย์ เรามีตัวแปรทั่วโลก lastAddTime
ซึ่งเก็บเวลาที่เพิ่มกำแพงสุดท้าย (เป็นมิลลิวินาที) . หากปัจจุบันเป็นมิลลิวินาที millis()
ลบมิลลิวินาทีที่เพิ่มล่าสุด lastAddTime
มีขนาดใหญ่กว่าค่าช่วงเวลาของเรา wallInterval
หมายความว่าถึงเวลาเพิ่มกำแพงใหม่แล้ว จากนั้นตัวแปรช่องว่างแบบสุ่มจะถูกสร้างขึ้นตามตัวแปรส่วนกลางที่กำหนดไว้ที่ด้านบนสุด จากนั้นวอลล์ใหม่ (อาร์เรย์จำนวนเต็มที่เก็บข้อมูล gap wall) จะถูกเพิ่มเข้าไปในรายการอาร์เรย์และ lastAddTime
ถูกตั้งค่าเป็นมิลลิวินาทีปัจจุบัน millis()
ในการจัดทำคำชี้แจงปัญหา ผู้วิจัยจะพูดถึงแต่ละข้อต่อไปนี้ ยกเว้น
wallHandler()
วนผ่านผนังปัจจุบันที่อยู่ในรายการอาร์เรย์ และสำหรับแต่ละรายการในแต่ละลูปจะเรียก wallRemover(i)
, wallMover(i)
และ wallDrawer(i)
ตามค่าดัชนีของรายการอาร์เรย์ วิธีการเหล่านี้ทำในสิ่งที่ชื่อแนะนำ wallDrawer()
วาดผนังจริงตามข้อมูลผนังช่องว่าง มันจับอาร์เรย์ข้อมูลผนังจากรายการอาร์เรย์และเรียก rect()
วิธีการวาดกำแพงตามที่ควรจะเป็น wallMover()
วิธีการจับองค์ประกอบจากรายการอาร์เรย์เปลี่ยนตำแหน่ง X ตาม wallSpeed
ตัวแปรส่วนกลาง สุดท้าย wallRemover()
ลบกำแพงออกจากรายการอาร์เรย์ซึ่งอยู่นอกหน้าจอ หากเราไม่ทำเช่นนั้นการประมวลผลจะถือว่าพวกเขายังคงอยู่ในหน้าจอ และนั่นจะเป็นการสูญเสียประสิทธิภาพอย่างมาก ดังนั้นเมื่อนำกำแพงออกจากรายการอาร์เรย์ผนังจะไม่ถูกดึงออกจากลูปที่ตามมา
สิ่งที่ท้าทายสุดท้ายที่ต้องทำคือการตรวจจับการชนกันระหว่างลูกบอลกับกำแพง
void wallHandler() { for (int i = 0; i wallTopX) && (ballX-(ballSize/2)wallTopY) && (ballY-(ballSize/2)wallBottomX) && (ballX-(ballSize/2)wallBottomY) && (ballY-(ballSize/2)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 คะแนนต่อผนัง หมายความว่าหากลูกบอลผ่านกำแพงมากกว่าย้อนกลับและผ่านอีกครั้งไม่ควรเพิ่มคะแนนอื่น เพื่อให้บรรลุเป้าหมายนี้เราได้เพิ่มตัวแปรอื่นในอาร์เรย์ผนังช่องว่างภายในรายการอาร์เรย์ ตัวแปรใหม่เก็บ 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
p5.js เป็นไลบรารี JavaScript ที่มีไฟล์ คล้ายกันมาก ไวยากรณ์ของภาษาโปรแกรมการประมวลผล ไม่ใช่ไลบรารีที่สามารถเรียกใช้โค้ดการประมวลผลที่มีอยู่ได้ แทน p5.js ต้องการการเขียนโค้ด JavaScript จริงซึ่งคล้ายกับพอร์ต JavaScript ของการประมวลผลที่เรียกว่า Processing.js . หน้าที่ของเราคือการแปลงรหัสการประมวลผลเป็น JavaScript โดยใช้ p5.js API ไลบรารีมีชุดของฟังก์ชันและไวยากรณ์ที่คล้ายกับการประมวลผลและเราต้องทำการเปลี่ยนแปลงบางอย่างกับโค้ดของเราเพื่อให้ทำงานใน JavaScript ได้ แต่เนื่องจากทั้งการประมวลผลและ JavaScript มีความคล้ายคลึงกันกับ Java จึงเป็นการก้าวกระโดดน้อยกว่าที่คิด . แม้ว่าคุณจะไม่ได้เป็น นักพัฒนา JavaScript การเปลี่ยนแปลงเป็นเรื่องเล็กน้อยมากและคุณควรจะทำตามได้ดี
ก่อนอื่นเราต้องสร้าง index.html
ง่ายๆ และเพิ่ม p5.min.js
ไปยังส่วนหัวของเรา เราต้องสร้างไฟล์อื่นชื่อ flappy_pong.js
ซึ่งจะเป็นที่ตั้งของรหัสที่แปลงแล้วของเรา
Flappy Pong canvas { box-shadow: 0 0 20px lightgray; }
กลยุทธ์ของเราในขณะแปลงรหัสควรคัดลอกและวางโค้ดทั้งหมดของเราลงใน flappy_pong.js
แล้วทำการเปลี่ยนแปลงทั้งหมด และนั่นคือสิ่งที่ฉันทำ และนี่คือขั้นตอนที่ฉันดำเนินการเพื่ออัปเดตโค้ด:
-
Javascript เป็นภาษาที่ไม่ได้พิมพ์ (ไม่มีการประกาศประเภทเช่น int
และ float
) ดังนั้นเราจำเป็นต้องเปลี่ยนการประกาศประเภททั้งหมดเป็น var
-
ไม่มี void
ใน Javascript เราควรเปลี่ยนทั้งหมดเป็น function
.
-
เราจำเป็นต้องลบการประกาศประเภทของอาร์กิวเมนต์ออกจากลายเซ็นของฟังก์ชัน (เช่น. void wallMover(var index) {
ถึง function wallMover(index) {
)
-
ไม่มี ArrayList
ใน JavaScript แต่เราสามารถบรรลุสิ่งเดียวกันได้โดยใช้อาร์เรย์ JavaScript เราทำการเปลี่ยนแปลงต่อไปนี้:
ArrayList walls = new ArrayList();
ถึง 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()
เพราะ color()
จะไม่ถูกกำหนดไว้ก่อน setup()
โทร.
-
เปลี่ยน size(500, 500);
ถึง createCanvas(500, 500);
-
เปลี่ยนชื่อ function gameScreen(){
เป็นอย่างอื่นเช่น function gamePlayScreen(){
เพราะเรามีตัวแปร global ชื่อ gameScreen
แล้ว เมื่อเราทำงานกับการประมวลผลหนึ่งคือฟังก์ชันและอีกฟังก์ชันหนึ่งคือ int
ตัวแปร. แต่ JavaScript ทำให้สิ่งเหล่านี้สับสนเนื่องจากไม่ได้พิมพ์
-
สิ่งเดียวกันสำหรับ score()
. ฉันเปลี่ยนชื่อเป็น addScore()
.
คุณสามารถดูโค้ด JavaScript ที่ครอบคลุมทุกอย่างในบทช่วยสอนการประมวลผลนี้ได้ ที่นี่ .
ประมวลผลรหัสเกม: คุณก็ทำได้เช่นกัน
ในบทช่วยสอนการประมวลผลนี้ฉันพยายามอธิบายวิธีสร้างเกมที่ง่ายมาก อย่างไรก็ตามสิ่งที่เราทำในบทความนี้เป็นเพียงส่วนเล็ก ๆ ของภูเขาน้ำแข็ง ด้วยภาษาโปรแกรมการประมวลผล เกี่ยวกับอะไรก็ได้ สามารถทำได้. ในความคิดของฉันเป็นเครื่องมือที่ดีที่สุดในการเขียนโปรแกรมสิ่งที่คุณกำลังจินตนาการ ความตั้งใจจริงของฉันกับบทช่วยสอนการประมวลผลนี้ไม่ใช่การสอนการประมวลผลและการสร้างเกมเพื่อพิสูจน์ว่าการเขียนโปรแกรมไม่ใช่เรื่องยาก การสร้างเกมของคุณเองไม่ใช่แค่ความฝัน ฉันอยากจะแสดงให้คุณเห็นว่าคุณสามารถทำได้ด้วยความพยายามและความกระตือรือร้นเพียงเล็กน้อย ฉันหวังเป็นอย่างยิ่งว่าบทความทั้งสองนี้จะเป็นแรงบันดาลใจให้ทุกคนอยากเขียนโปรแกรม
ทำความเข้าใจพื้นฐาน
ภาษาประมวลผลมีพื้นฐานมาจากอะไร?
การประมวลผลขึ้นอยู่กับ Java เนื่องจาก JavaScript มีลักษณะคล้ายกับ Java บนพื้นผิวการพอร์ตการประมวลผลโค้ดไปยังเว็บจึงค่อนข้างตรงไปตรงมาโดยใช้ไลบรารี JavaScript p5.js
'โรคหลอดเลือดสมอง' ในการประมวลผลคืออะไร?
เส้นขีดหมายถึงเส้นขอบที่วาดรอบรูปร่าง
ลอยทำอะไรในการประมวลผล?
float ถือค่าทศนิยม ในทางตรงกันข้าม int สามารถเก็บค่าจำนวนเต็มได้เท่านั้น (จำนวนเต็มบวกหรือลบ)