Panduan Utama untuk Bahasa Pemrosesan Bagian II: Membangun Game Sederhana
Diterbitkan: 2022-03-11Ini adalah bagian kedua dari panduan utama untuk bahasa Pemrosesan. Di bagian pertama, saya memberikan panduan bahasa Pemrosesan dasar. Langkah selanjutnya bagi Anda untuk mempelajari Pemrosesan adalah pemrograman yang lebih praktis.
Pada artikel ini, saya akan menunjukkan cara menggunakan Processing untuk mengimplementasikan game Anda sendiri, langkah demi langkah. Setiap langkah akan dijelaskan secara rinci. Kemudian, kami akan port game ke web.
Sebelum kita memulai tutorial Processing, berikut adalah kode latihan logo DVD dari bagian sebelumnya. Jika Anda memiliki pertanyaan, pastikan untuk meninggalkan komentar.
Tutorial Pemrosesan: Game Sederhana
Game yang akan kita buat dalam tutorial Processing ini adalah semacam kombinasi dari Flappy Bird, Pong dan Brick Breaker. Alasan saya memilih game seperti ini adalah karena sebagian besar konsepnya sulit dipahami oleh pemula saat mempelajari pengembangan game. Ini berdasarkan pengalaman saya sejak saya menjadi asisten pengajar, membantu programmer baru mempelajari cara menggunakan Processing. Konsep-konsep ini termasuk gravitasi, tabrakan, menjaga skor, menangani layar yang berbeda dan interaksi keyboard/mouse. Flappy Pong memiliki semuanya di dalamnya.
Mainkan Game Sekarang!
Tanpa menggunakan konsep pemrograman berorientasi objek (OOP), tidak mudah untuk membangun game yang kompleks, seperti game platform dengan banyak level, pemain, entitas, dll. Saat kita melanjutkan, Anda akan melihat bagaimana kode menjadi rumit dengan sangat cepat. Saya melakukan yang terbaik untuk menjaga tutorial Pemrosesan ini tetap teratur dan sederhana.
Saya menyarankan Anda untuk mengikuti artikelnya, ambil kode lengkapnya, mainkan sendiri, mulailah memikirkan permainan Anda sendiri secepat mungkin, dan mulai menerapkannya.
Jadi mari kita mulai.
Membangun Flappy Pong
Tutorial Pemrosesan Langkah #1: Inisialisasi & Tangani Layar Berbeda
Langkah pertama adalah menginisialisasi proyek kita. Sebagai permulaan, kami akan menulis pengaturan kami dan menggambar blok seperti biasa, tidak ada yang mewah atau baru. Kemudian, kami akan menangani layar yang berbeda (layar awal, layar game, layar game over, dll.). Jadi timbul pertanyaan, bagaimana caranya agar Processing menampilkan halaman yang benar pada waktu yang tepat?
Menyelesaikan tugas ini cukup sederhana. Kami akan memiliki variabel global yang menyimpan informasi dari layar yang sedang aktif. Kami kemudian menggambar isi layar yang benar tergantung pada variabel. Di blok draw, kita akan memiliki pernyataan if yang memeriksa variabel dan menampilkan isi layar yang sesuai. Kapan pun kita ingin mengubah layar, kita akan mengubah variabel itu menjadi pengidentifikasi layar yang ingin kita tampilkan. Dengan itu, inilah tampilan kode kerangka kami:
/********* 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; }
Ini mungkin terlihat menakutkan pada awalnya, tetapi yang kami lakukan hanyalah membangun struktur dasar dan memisahkan bagian-bagian yang berbeda dengan blok komentar.
Seperti yang Anda lihat, kami mendefinisikan metode yang berbeda untuk setiap layar yang akan ditampilkan. Di blok draw kami, kami cukup memeriksa nilai variabel gameScreen
kami, dan memanggil metode yang sesuai.
Di bagian void mousePressed(){...}
, kita mendengarkan klik mouse dan jika layar aktif adalah 0, layar awal, kita memanggil metode startGame()
yang memulai permainan seperti yang Anda harapkan. Baris pertama dari metode ini mengubah variabel gameScreen
menjadi 1, layar game.
Jika sudah dipahami, langkah selanjutnya adalah mengimplementasikan layar awal kita. Untuk melakukan itu, kita akan mengedit metode initScreen()
. Ini dia:
void initScreen() { background(0); textAlign(CENTER); text("Click to start", height/2, width/2); }
Sekarang layar awal kami memiliki latar belakang hitam dan teks sederhana, "Klik untuk memulai", terletak di tengah dan sejajar dengan tengah. Tapi ketika kita klik, tidak ada yang terjadi. Kami belum menentukan konten apa pun untuk layar game kami. Metode gameScreen()
tidak memiliki apa pun di dalamnya, jadi kami tidak menutupi konten sebelumnya yang diambil dari layar terakhir (teks) dengan memiliki background()
sebagai baris pertama undian. Itu sebabnya teksnya tetap ada, meskipun baris text()
tidak dipanggil lagi (seperti contoh bola bergerak dari bagian terakhir yang meninggalkan jejak) . Latar belakang masih hitam untuk alasan yang sama. Jadi mari kita lanjutkan dan mulai mengimplementasikan layar game.
void gameScreen() { background(255); }
Setelah perubahan ini, Anda akan melihat bahwa latar belakang berubah menjadi putih & teks menghilang.
Tutorial Pemrosesan Langkah #2: Membuat Bola & Menerapkan Gravitasi
Sekarang, kita akan mulai mengerjakan layar game. Pertama-tama kita akan membuat bola kita. Kita harus mendefinisikan variabel untuk koordinat, warna, dan ukurannya karena kita mungkin ingin mengubah nilai tersebut nanti. Misalnya, jika kita ingin memperbesar ukuran bola karena skor pemain lebih tinggi sehingga permainan akan lebih sulit. Kita perlu mengubah ukurannya, jadi itu harus berupa variabel. Kami akan menentukan kecepatan bola juga, setelah kami menerapkan gravitasi.
Pertama, mari tambahkan yang berikut ini:
... 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); }
Kami mendefinisikan koordinat sebagai variabel global, membuat metode yang menarik bola, yang disebut dari metode gameScreen . Satu-satunya hal yang perlu diperhatikan di sini adalah kita menginisialisasi koordinat, tetapi kita mendefinisikannya di setup()
. Alasan kami melakukan itu adalah kami ingin bola dimulai pada seperempat dari kiri dan seperlima dari atas. Tidak ada alasan khusus kami menginginkan itu, tetapi itu adalah poin yang bagus untuk bola dimulai. Jadi kita perlu mendapatkan width
dan height
sketsa secara dinamis. Ukuran sketsa ditentukan dalam setup()
, setelah baris pertama. width
dan height
tidak disetel sebelum setup()
berjalan, itu sebabnya kami tidak dapat mencapai ini jika kami mendefinisikan variabel di atas.
Gravitasi
Sekarang menerapkan gravitasi sebenarnya adalah bagian yang mudah. Hanya ada beberapa trik. Berikut implementasinya terlebih dahulu:
... 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); } }
Dan hasilnya adalah:
Pegang kudamu, fisikawan. Saya tahu itu bukan cara kerja gravitasi dalam kehidupan nyata. Sebaliknya, ini lebih merupakan proses animasi daripada apa pun. Variabel yang kita definisikan sebagai gravity
hanyalah nilai numerik— float
sehingga kita dapat menggunakan nilai desimal, bukan hanya bilangan bulat—yang kita tambahkan ke ballSpeedVert
di setiap loop. Dan ballSpeedVert
adalah kecepatan vertikal bola, yang ditambahkan ke koordinat Y bola ( ballY
) pada setiap loop. Kami mengamati koordinat bola dan memastikannya tetap berada di layar. Jika tidak, bola akan jatuh hingga tak terhingga. Untuk saat ini, bola kami hanya bergerak vertikal. Jadi kami melihat batas lantai dan langit-langit layar. Dengan metode keepInScreen()
, kami memeriksa apakah ballY
( + radius) kurang dari height
, dan juga ballY
( - radius) lebih dari 0
. Jika kondisi tidak terpenuhi, kami membuat bola memantul (dari bawah atau atas) dengan makeBounceBottom()
dan makeBounceTop()
. Untuk membuat bola memantul, kita cukup memindahkan bola ke lokasi yang tepat di mana bola harus memantul dan mengalikan kecepatan vertikal ( ballSpeedVert
) dengan -1
( mengalikan dengan -1 akan mengubah tanda). Ketika nilai kecepatan memiliki tanda minus, penambahan koordinat Y kecepatan menjadi ballY + (-ballSpeedVert)
, yaitu ballY - ballSpeedVert
. Jadi bola segera berubah arah dengan kecepatan yang sama. Kemudian, saat kita menambahkan gravity
ke ballSpeedVert
dan ballSpeedVert
memiliki nilai negatif, ia mulai mendekati 0
, akhirnya menjadi 0
, dan mulai meningkat lagi. Itu membuat bola naik, naik lebih lambat, berhenti dan mulai jatuh.
Namun, ada masalah dengan proses animasi kami—bola terus memantul. Jika ini adalah skenario dunia nyata, bola akan menghadapi hambatan udara dan gesekan setiap kali menyentuh permukaan. Itulah perilaku yang kami inginkan untuk proses animasi game kami, jadi menerapkan ini mudah. Kami menambahkan yang berikut ini:
... 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); }
Dan sekarang proses animasi kami menghasilkan ini:
Seperti namanya, friction
adalah gesekan permukaan dan airfriction
adalah gesekan udara. Jadi jelas, friction
harus diterapkan setiap kali bola menyentuh permukaan apa pun. airfriction
namun harus diterapkan terus-menerus. Jadi itulah yang kami lakukan. metode applyGravity()
berjalan pada setiap loop, jadi kami mengambil 0.0001
persen dari nilainya saat ini dari ballSpeedVert
pada setiap loop. makeBounceBottom()
dan makeBounceTop()
dijalankan saat bola menyentuh permukaan apa pun. Jadi dalam metode itu, kami melakukan hal yang sama, hanya kali ini dengan friction
.
Tutorial Pemrosesan Langkah #3: Membuat Raket
Sekarang kami membutuhkan raket untuk memantulkan bola. Kita harus mengendalikan raket. Mari kita membuatnya dapat dikontrol dengan mouse. Berikut kodenya:
... color racketColor = color(0); float racketWidth = 100; float racketHeight = 10; ... void gameScreen() { ... drawRacket(); ... } ... void drawRacket(){ fill(racketColor); rectMode(CENTER); rect(mouseX, mouseY, racketWidth, racketHeight); }
Kami mendefinisikan warna, lebar dan tinggi raket sebagai variabel global, kami mungkin ingin mereka berubah selama bermain game. Kami menerapkan metode drawRacket()
yang melakukan apa yang disarankan namanya. Kami mengatur rectMode
ke tengah, sehingga raket kami sejajar dengan pusat kursor kami.
Sekarang setelah kita membuat raket, kita harus membuat bola memantul di atasnya.
... 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; } } } }
Dan inilah hasilnya:
Jadi yang dilakukan watchRacketBounce()
adalah memastikan bahwa raket dan bola bertabrakan. Ada dua hal yang harus diperiksa di sini, yaitu apakah bola dan raket berjajar baik secara vertikal maupun horizontal. Pernyataan if pertama memeriksa apakah koordinat X sisi kanan bola lebih besar dari koordinat X sisi kiri raket (dan sebaliknya). Jika ya, pernyataan kedua memeriksa apakah jarak antara bola dan raket lebih kecil dari atau sama dengan jari-jari bola (yang berarti keduanya bertabrakan) . Jadi jika kondisi ini terpenuhi, metode makeBounceBottom()
dipanggil dan bola memantul di raket kita (di mouseY
, tempat raket berada).
Pernahkah Anda memperhatikan variabel overhead
yang dihitung dengan mouseY - pmouseY
? pmouseX
dan pmouseY
menyimpan koordinat mouse pada frame sebelumnya. Karena mouse dapat bergerak sangat cepat, ada kemungkinan besar kita tidak dapat mendeteksi jarak antara bola dan raket dengan benar di antara frame jika mouse bergerak ke arah bola dengan cukup cepat. Jadi, kami memperhitungkan perbedaan koordinat mouse di antara bingkai dan memperhitungkannya saat mendeteksi jarak. Semakin cepat mouse bergerak, semakin jauh jarak yang dapat diterima.
Kami juga menggunakan overhead
untuk alasan lain. Kami mendeteksi ke arah mana mouse bergerak dengan memeriksa tanda di overhead
. Jika overhead negatif, mouse berada di suatu tempat di bawah frame sebelumnya sehingga mouse (raket) kita bergerak ke atas. Dalam hal ini, kami ingin menambahkan kecepatan ekstra pada bola dan memindahkannya sedikit lebih jauh dari pantulan biasa untuk mensimulasikan efek memukul bola dengan raket. Jika overhead
kurang dari 0
, kami menambahkannya ke ballY
dan ballSpeedVert
untuk membuat bola lebih tinggi dan lebih cepat. Jadi semakin cepat raket memukul bola, semakin tinggi dan cepat ia akan bergerak ke atas.
Tutorial Pemrosesan Langkah #4: Gerakan Horizontal & Mengontrol Bola
Pada bagian ini, kita akan menambahkan gerakan horizontal pada bola. Kemudian, kami akan memungkinkan untuk mengontrol bola secara horizontal dengan raket kami. Ini dia:
... // 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); } }
Dan hasilnya adalah:
Idenya di sini sama dengan apa yang kami lakukan untuk gerakan vertikal. Kami membuat variabel kecepatan horizontal, ballSpeedHorizon
. Kami menciptakan metode untuk menerapkan kecepatan horizontal ke ballX
dan menghilangkan gesekan udara. Kami menambahkan dua pernyataan if lagi ke metode keepInScreen()
yang akan mengawasi bola untuk memukul tepi kiri dan kanan layar. Akhirnya kami membuat makeBounceLeft()
dan makeBounceRight()
untuk menangani pantulan dari kiri dan kanan.

Sekarang setelah kami menambahkan kecepatan horizontal ke permainan, kami ingin mengontrol bola dengan raket. Seperti dalam game Atari Breakout yang terkenal dan di semua game memecahkan batu bata lainnya, bola harus bergerak ke kiri atau ke kanan sesuai dengan titik pada raket yang dipukul. Tepi raket harus memberikan kecepatan bola yang lebih horizontal sedangkan bagian tengah seharusnya tidak memiliki efek apa pun. Kode terlebih dahulu:
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; ... } } }
Hasilnya adalah:
Menambahkan baris sederhana itu ke watchRacketBounce()
berhasil. Apa yang kami lakukan adalah kami menentukan jarak titik yang ditendang bola dari pusat raket dengan ballX - mouseX
. Kemudian, kita membuatnya menjadi kecepatan horizontal. Perbedaan sebenarnya terlalu banyak, jadi saya mencobanya beberapa kali dan berpikir bahwa sepersepuluh dari nilai terasa paling alami.
Tutorial Pemrosesan Langkah #5: Membuat Dinding
Sketsa kami mulai terlihat lebih seperti permainan dengan setiap langkah. Pada langkah ini, kita akan menambahkan dinding yang bergerak ke arah kiri, seperti pada 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); } }
Dan ini mengakibatkan:
Meskipun kodenya terlihat panjang dan menakutkan, saya berjanji tidak ada yang sulit untuk dipahami. Hal pertama yang harus diperhatikan adalah ArrayList
. Bagi Anda yang belum tahu apa itu ArrayList
, ini hanyalah implementasi dari daftar yang berfungsi seperti Array, tetapi memiliki beberapa kelebihan. Ini dapat diubah ukurannya, memiliki metode yang berguna seperti list.add(index)
, list.get(index)
dan list.remove(index)
. Kami menyimpan data dinding sebagai array integer dalam daftar array. Data yang kami simpan dalam array adalah untuk celah antara dua dinding. Array berisi nilai-nilai berikut:
[gap wall X, gap wall Y, gap wall width, gap wall height]
Dinding sebenarnya digambar berdasarkan nilai celah dinding. Perhatikan bahwa semua ini dapat ditangani dengan lebih baik dan lebih bersih menggunakan kelas, tetapi karena penggunaan Pemrograman Berorientasi Objek (OOP) tidak dalam cakupan tutorial Pemrosesan ini, inilah cara kami akan menanganinya. Kami memiliki dua metode dasar untuk mengelola dinding, wallAdder()
dan wallHandler
.
wallAdder()
metode hanya menambahkan dinding baru di setiap milidetik wallInterval
ke daftar array. Kami memiliki variabel global lastAddTime
yang menyimpan waktu ketika dinding terakhir ditambahkan (dalam milidetik) . Jika milidetik saat ini millis()
dikurangi milidetik terakhir yang ditambahkan lastAddTime
lebih besar dari nilai interval wallInterval
, itu berarti sekarang saatnya untuk menambahkan dinding baru. Variabel gap acak kemudian dihasilkan berdasarkan variabel global yang didefinisikan di bagian paling atas. Kemudian dinding baru (array bilangan bulat yang menyimpan data dinding celah) ditambahkan ke dalam daftar array dan lastAddTime
diatur ke milidetik saat ini millis()
.
wallHandler()
loop melalui dinding saat ini yang ada di daftar array. Dan untuk setiap item di setiap loop, ia memanggil wallRemover(i)
, wallMover(i)
dan wallDrawer(i)
dengan nilai indeks daftar array. Metode ini melakukan apa yang disarankan oleh namanya. wallDrawer()
menggambar dinding sebenarnya berdasarkan data dinding celah. Ini mengambil array data dinding dari daftar array, dan memanggil metode rect()
untuk menggambar dinding ke tempat yang seharusnya. metode wallMover()
mengambil elemen dari daftar array, mengubah lokasi X-nya berdasarkan variabel global wallSpeed
. Terakhir, wallRemover()
menghapus dinding dari daftar array yang berada di luar layar. Jika kami tidak melakukan itu, Processing akan memperlakukan mereka seperti mereka masih di layar. Dan itu akan menjadi kerugian besar dalam kinerja. Jadi ketika sebuah dinding dihapus dari daftar array, itu tidak tergambar pada loop berikutnya.
Tantangan terakhir yang harus dilakukan adalah mendeteksi benturan antara bola dan dinding.
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()
metode dipanggil untuk setiap dinding di setiap loop. Kami mengambil koordinat dinding celah, menghitung koordinat dinding yang sebenarnya (atas dan bawah) dan kami memeriksa apakah koordinat bola bertabrakan dengan dinding.
Tutorial Pemrosesan Langkah #6: Kesehatan dan Skor
Sekarang setelah kita dapat mendeteksi tabrakan bola dan dinding, kita dapat memutuskan mekanisme permainannya. Setelah beberapa penyetelan ke permainan, saya berhasil membuat permainan agak bisa dimainkan. Tapi tetap saja, itu sangat sulit. Pikiran pertama saya tentang game ini adalah membuatnya seperti Flappy Bird, ketika bola menyentuh dinding, permainan berakhir. Tapi kemudian saya menyadari itu tidak mungkin untuk dimainkan. Jadi inilah yang saya pikirkan:
Harus ada bar kesehatan di atas bola. Bola harus kehilangan kesehatan saat menyentuh dinding. Dengan logika ini, tidak masuk akal untuk membuat bola memantul dari dinding. Jadi ketika kesehatannya 0, game harus berakhir dan kita harus beralih ke layar game over. Jadi di sini kita pergi:
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(); } }
Dan ini adalah lari sederhana:
Kami menciptakan health
variabel global untuk menjaga kesehatan bola. Dan kemudian buat metode drawHealthBar()
yang menggambar dua persegi panjang di atas bola. Yang pertama adalah bar kesehatan dasar, yang lain adalah yang aktif yang menunjukkan kesehatan saat ini. Lebar yang kedua adalah dinamis, dan dihitung dengan healthBarWidth*(health/maxHealth)
, rasio kesehatan kita saat ini sehubungan dengan lebar bilah kesehatan. Terakhir, warna isian diatur sesuai dengan nilai kesehatan. Terakhir, skor :
... 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); }
Kami harus mencetak gol saat bola melewati tembok. Tapi kita perlu menambahkan maksimal 1 skor per dinding. Artinya, jika bola melewati tembok daripada kembali dan mengopernya lagi, skor lain tidak boleh ditambahkan. Untuk mencapai ini, kami menambahkan variabel lain ke array dinding celah di dalam daftar array. Variabel baru menyimpan 0
jika bola belum melewati tembok itu dan 1
jika berhasil. Kemudian, kami memodifikasi metode watchWallCollision()
. Kami menambahkan kondisi yang menembakkan metode score()
dan menandai tembok yang dilewati ketika bola melewati tembok yang belum pernah dilewati sebelumnya.
Kami sekarang sangat dekat dengan akhir. Hal terakhir yang harus dilakukan adalah menerapkan click to restart
pada layar game over. Kita perlu mengatur semua variabel yang kita gunakan ke nilai awalnya, dan memulai kembali permainan. Ini dia:
... 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; }
Mari tambahkan beberapa warna lagi.
Voila! Kami memiliki Flappy Pong!
Kode game Processing lengkap dapat ditemukan di sini.
Memindahkan Kode Game Pemrosesan ke Web Menggunakan p5.js
p5.js adalah library JavaScript dengan sintaks yang sangat mirip dengan bahasa pemrograman Processing. Ini bukan perpustakaan yang hanya mampu menjalankan kode Pemrosesan yang ada; sebagai gantinya, p5.js memerlukan penulisan kode JavaScript yang sebenarnya—mirip dengan port JavaScript Pemrosesan yang dikenal sebagai Processing.js. Tugas kita adalah mengubah kode Pemrosesan menjadi JavaScript menggunakan p5.js API. Pustaka memiliki serangkaian fungsi dan sintaks yang mirip dengan Pemrosesan, dan kami harus melakukan perubahan tertentu pada kode kami untuk membuatnya berfungsi di JavaScript—namun karena Pemrosesan dan JavaScript memiliki kesamaan dengan Java, ini tidak terlalu jauh dari kedengarannya. . Bahkan jika Anda bukan pengembang JavaScript, perubahannya sangat sepele dan Anda harus dapat mengikutinya dengan baik.
Pertama-tama, kita perlu membuat index.html
sederhana dan menambahkan p5.min.js
ke header kita. Kita juga perlu membuat file lain bernama flappy_pong.js
yang akan menampung kode konversi kita.
<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>
Strategi kami saat mengonversi kode harus menyalin dan menempelkan semua kode kami ke flappy_pong.js
dan kemudian membuat semua perubahan. Dan itulah yang saya lakukan. Dan berikut adalah langkah-langkah yang saya ambil untuk memperbarui kode:
Javascript adalah bahasa yang tidak diketik (tidak ada deklarasi tipe seperti
int
danfloat
). Jadi kita perlu mengubah semua deklarasi tipe menjadivar
.Tidak ada
void
dalam Javascript. Kita harus mengubah semuafunction
.Kita perlu menghapus deklarasi tipe argumen dari tanda tangan fungsi. (mis.
void wallMover(var index) {
function wallMover(index) {
)Tidak ada
ArrayList
di JavaScript. Tapi kita bisa mencapai hal yang sama menggunakan array JavaScript. Kami membuat perubahan berikut:-
ArrayList<int[]> walls = new ArrayList<int[]>();
kevar walls = [];
-
walls.clear();
kewalls = [];
-
walls.add(randWall);
kewalls.push(randWall);
-
walls.remove(index);
kewalls.splice(index,1);
-
walls.get(index);
kewalls[index]
-
walls.size()
kewalls.length
-
Ubah deklarasi array
var randWall = {width, randY, wallWidth, randHeight, 0};
tovar randWall = [width, randY, wallWidth, randHeight, 0];
Hapus semua kata kunci
public
.Pindahkan semua deklarasi
color(0)
ke dalamfunction setup()
karenacolor()
tidak akan didefinisikan sebelum panggilansetup()
.Ubah
size(500, 500);
untukcreateCanvas(500, 500);
Ganti nama
function gameScreen(){
menjadi sesuatu yang lain sepertifunction gamePlayScreen(){
karena kita sudah memiliki variabel global bernamagameScreen
. Ketika kami bekerja dengan Processing, satu adalah fungsi dan yang lainnya adalah variabelint
. Tetapi JavaScript membingungkan ini karena tidak diketik.Hal yang sama berlaku untuk
score()
. Saya menamainya menjadiaddScore()
.
Kode JavaScript lengkap yang mencakup semuanya dalam tutorial Pemrosesan ini dapat ditemukan di sini.
Memproses Kode Game: Anda Juga Bisa
Dalam tutorial Processing ini, saya mencoba menjelaskan bagaimana membangun sebuah game yang sangat sederhana. Namun, apa yang kami lakukan dalam artikel ini hanyalah puncak gunung es. Dengan bahasa pemrograman Processing, apa saja bisa dicapai. Menurut pendapat saya, ini adalah alat terbaik untuk memprogram apa yang Anda bayangkan. Niat saya yang sebenarnya dengan tutorial Pemrosesan ini adalah daripada mengajar Pemrosesan dan membuat game, untuk membuktikan bahwa pemrograman tidak terlalu sulit. Membangun game Anda sendiri bukan hanya mimpi. Saya ingin menunjukkan kepada Anda bahwa dengan sedikit usaha dan antusiasme, Anda bisa melakukannya. Saya sangat berharap kedua artikel ini menginspirasi semua orang untuk mencoba pemrograman.