Search.....

Saturday, December 22, 2018

Tutorial Game Mario dengan Processing

Tutorial Game Mario dengan Processing


HI SOBAT.. KALI INI ANE AKAN POSTING MELANJUTKAN TUTORIAL MEMBUAT GAME MARIO DIMANA PART 1 DAN 2 NYA BISA DILIHAT DI SINI :

SHINY, PICKUP MENGKILAP

Di perpustakaan permainan, harta adalah bentuk "pikap". Pickup adalah hal-hal yang bisa diambil oleh pemain atau NPC (karakter non-pemain) dengan berjalan ke dalamnya, dan ketika mereka melakukannya, "sesuatu" dapat terjadi. Tentu saja, kita memegang kendali, jadi yang terjadi tergantung pada kita. Sangat berguna.

Jika Anda pernah memainkan permainan mario, Anda mungkin tahu bahwa mario suka mengumpulkan koin, dari semua ukuran dan warna. Dalam permainan kami, kami akan memberinya dua jenis koin berbeda untuk diambil: koin normal, dan koin naga misterius:

Untuk membuat pickup koin, kita mulai dengan membuat master "Mario pickup", yang dapat kita gunakan sebagai superclass untuk setiap pickup yang akan kita buat dalam permainan kita:

Meskipun kode ini tidak banyak membantu, kami akan mengelompokkan hal-hal sebagai "Mario Pickup", dan itu akan berguna jika kami pernah memperkenalkan kelas pickup yang bukan untuk Mario. 
Jadi, mari kita tulis koin pikap kami. Mereka cukup lugas, jadi begini :

Itu saja, hanya itu yang harus kita definisikan. Sekarang jika kita ingin menggunakannya dalam permainan kita, kita cukup membuat new Coin(...,...) atau new DragonCoin(...,...) dan kemudian menambahkannya ke daftar pickup kami, untuk pemain hanya: 
class MarioLayer extends LevelLayer {  [...]  MarioLayer(Level owner) {    [...]
    // add coints above the horizontal platforms    addCoins(928,height-236,96);    addCoins(912,height-140,128);    addCoins(1442,height-140,128);
    // add a dragon coin at the start    addForPlayerOnly(new DragonCoin(352,height-164));  }
  // a handy function for placing lots of coins  void addCoins(float x, float y, float w) {    float step = 16, i = 0, last = w/step;    for(i=0; i<last; i++) {      addForPlayerOnly(new Coin(x+8+i*step,y));    }  }}  
Fungsi addCoins adalah fungsi yang nyaman yang memungkinkan kita menambahkan string koin mulai dari posisi x/y dan mencakup lebar w . Kami menggunakan nilai stepuntuk menghemat koin kami, menggunakan 16 piksel sebagai jarak dari satu pusat koin ke pusat koin berikutnya, dan kemudian kami mulai menambahkan koin "untuk pemain saja", seperti yang jelas dari addForPlayerOnly(...) nama fungsi. Hasil? Mengapa, mari kita mainkan game terbaru kami dan lihat sendiri:


Seperti yang Anda lihat, kita dapat mengambil koin dengan berlari ke dalamnya. Jika kita mau, kita dapat membuat skor Mario naik setiap kali dia mengambil koin, tetapi kita akan melakukannya nanti karena itu membutuhkan bekerja dengan sesuatu yang disebut "HUD", yang merupakan informasi yang Anda dapatkan tentang permainan Anda seperti skor, kesehatan, sisa waktu, peralatan saat ini, dan semua hal semacam itu. 

MUSUH!

Ya, saatnya menambah musuh ke permainan kami.Berlari saja mengumpulkan koin akan jauh lebih menantang jika Anda mencoba untuk tidak menyerang musuh pada saat yang bersamaan. Jadi mari tambahkan favorit Mario lama: Koopa Trooper.

Pasukan Koopa sangat mirip Mario: Mereka memiliki negara dan perlu beralih di antara mereka ketika mereka melakukan sesuatu, jadi mungkin tidak mengherankan, mendefinisikan seorang polisi Koopa sangat mirip dengan mendefinisikan Mario. Kecuali kita tidak perlu khawatir tentang memasukkan penanganan input, karena kita tidak akan mengontrol.
class Koopa extends Interactor {  // we construct a Koopa trooper pretty much the same way we did Mario:  Koopa(float x, float y) {    super("Koopa Trooper");    setStates();    setForces(-0.25, DOWN_FORCE);        setImpulseCoefficients(DAMPENING, DAMPENING);    setPosition(x,y);  }
  // And we use states.  void setStates() {    // walking state    State walking = new State("idle", "graphics/enemies/Red-koopa-walking.gif", 1, 2);    walking.setAnimationSpeed(0.12);    addState(walking);  }}
Lalu kami menambahkannya seperti kami menambahkan yang lain: 


class MarioLayer extends LevelLayer {  [...]  MarioLayer(Level owner) {    [...]      Koopa koopa = new Koopa(264, height-178);    addInteractor(koopa);  }}
Itu lumayan bagus, tapi agak tidak lengkap. Mari kita coba mainkan:

Kami tidak terluka! Musuh ini tidak berbahaya! Dan dia agak bodoh, setelah berjalan ke dinding di sebelah kiri, dia hanya memilih untuk terus berjalan ke kiri. Jadi jelas, kita perlu mengajarkannya beberapa hal lagi. Pertama, mari kita perbaiki masalah "Saya tidak tahu apa itu tembok". Ketika aktor (pemain atau NPC) memukul sesuatu, fungsi khusus disebutgotBlockeddisebut oleh perpustakaan game. Biasanya, fungsi ini tidak "tidak ada", jadi ketika Anda menabrak dinding, tidak ada yang benar-benar terjadi, selain itu Anda berhenti. Tapi, untuk pasukan Koopa kami, kami ingin menyadari bahwa itu tidak bisa berjalan lebih jauh, dan harus berbalik. Tidak masalah:


class Koopa extends Interactor {  [...]    void gotBlocked(Boundary b, float[] intersection) {    // is the boundary vertical?    if (b.x == b.xw) {      // yes it is. Reverse direction!      fx = -fx;      setHorizontalFlip(fx > 0);    }  }}
Di sana, bagus dan sederhana. The if (bx == b.xw)kode memeriksa apakah batas vertikal atau tidak: untuk garis vertikal, yang xawal dan akhir koordinat nilai yang sama, dengan hanya ketinggian di awal dan akhir menjadi berbeda, jadi kami hanya memeriksa "adalah mulai x nilai ( bx) yang sama dengan nilai x akhir b.xw)? Jika demikian, arah sebaliknya ". Dan kami membalik arah dengan mengubah kekuatan seragam yang bertindak pada pasukan Koopa kami. Untuk membuatnya terdorong ke kiri, kita gunakan -0,25 sebagai gaya horizontal, sehingga untuk membuat pasukan Koopa 'terdorong' ke kanan, kita dapat mengembalikannya menjadi 0,25, dan membalik sprite Koopa trooper. Pekerjaan selesai!

Dan sekarang, mari kita membuat musuh kita menjadi berbahaya. Ini membutuhkan penambahan dalam interaksi antara Mario dan pasukan Koopa: Mario harus bisa menekan mereka, dan pasukan Koopa harus mampu mengalahkan Mario. Jadi kita akan mengubah kedua kelas.

MEMBUAT PASUKAN KOOPA BERBAHAYA UNTUK MARIO

Untuk membuat pasukan Koopa berbahaya bagi Mario, kita perlu memastikan "sesuatu" harus terjadi ketika Mario dan seorang Koopa menyentuh polisi. Untungnya, perpustakaan game sudah memiliki fungsi untuk itu, kita hanya perlu mengisinya.
class Mario extends Player {  [...]
  void setupStates() {    [...]
    State dead = new State("dead", "graphics/mario/small/Dead-mario.gif", 1, 2);    dead.setAnimationSpeed(0.25);    dead.setDuration(15);    addState(dead);        [...]  }
  // what happens when we touch another player or NPC?  void overlapOccurredWith(Actor other, float[] direction) {    // for the moment, we only care about what happens    // when we touch Koopa troopers    if (other instanceof Koopa) {      // if we touch a koopa trooper, we die! O_O      die();    }  }
  void die() {    // switch to dead state    setCurrentState("dead");    // turn off interaction, so we don't flag more touching koopas or pickups or walls, etc.    setInteracting(false);    // make up jump up in an "oh no!" fashion    addImpulse(0,-30);    // and turn up gravity so we fall down quicker than usual.    setForces(0,3);  }}
Ini adalah awal dari perubahan yang harus kita buat, tetapi yang bagus: jika kita menyentuh seorang polisi Koopa, kita mati. 

Memastikan kami memulai ulang game

Tentu saja sementara pasukan Koopa sekarat dan meninggalkan permainan bukan masalah besar, jika kita mati kita ingin memulai kembali permainan. Saat ini, itu tidak terjadi, jadi kita perlu menambahkan satu kode lagi untuk membuat semuanya berjalan sesuai keinginan kita:
[...]void reset() {  clearScreens();  addScreen("level", new MarioLevel(4*width, height));}[...]class Mario extends Player {  [...]  void handleStateFinished(State which) {    if(which.name == "dead") {      removeActor();      reset();    } else {      setCurrentState("idle");    }  }  [...]}
Kami memperkenalkan fungsi global baru ("dapat dipanggil dari mana saja dalam kode") yang disebut reset(), yang pada dasarnya menyeka segalanya, dan kemudian membangun kembali level kami, dengan mario baru, koopa trooper, koin, dan semua yang kami katakan harus dibangun saat kami membuat MarioLevel kami.

Jadi sekarang jika kita menyentuh pasukan Koopa, kita akan kalah, dan kemudian permainan dimulai lagi. Mari tingkatkan arena permainan!

Membuat Mario berbahaya bagi pasukan Koopa
Dalam game Mario klasik, sebagian besar musuh dapat dikalahkan dengan melompat di atas mereka. Jadi ini membutuhkan dua perubahan: Mario harus tahu dari arah mana dia memukul pasukan Koopa, dan pasukan Koopa harus dapat diperas. Mari kita memecahkannya:
class Mario extends Player {  [...]  void overlapOccurredWith(Actor other, float[] direction) {    if (other instanceof Koopa) {      // get a reference to this Koopa trooper      Koopa koopa = (Koopa) other;    // get the angle at which we've impacted with this koopa trooper      float angle = direction[2];
  // Now to find out whether we bopped a koopa on the head!      float tolerance = radians(75);if (PI/2 - tolerance &lt;= angle &amp;&amp; angle &lt;= PI/2 + tolerance) {        // we hit it from above!        // 1) squish the koopa trooper        koopa.squish();    // Stop moving in whichever direction we were moving in        stop(0,0);        // instead, jump up!        setImpulse(0, -30);        setCurrentState("jumping");      }
      // if we didn't hit it at the correct angle, we still die =(      else { die(); }    }  }
  [...]}
Di sini kita melihat beberapa matematika. Alih-alih hanya memberi tahu kami mana dari empat arah kiri, kanan, atas, atau bawah yang kami pukul, perpustakaan permainan memberi kami sudut pandang yang sebenarnya saat kami memukul aktor lain. Itu benar-benar berguna, tetapi juga membuatnya sedikit sulit untuk mengetahui apa arah "nyata" yang akan kita masuki. Jika Anda tidak mengerti apa yang terjadi di kode di atas, tidak apa-apa. Saya akan coba menjelaskannya di sini:

Untuk kebanyakan hal, sudut tidak berubah dari nol derajat ke 360 ​​derajat, seperti yang mungkin Anda terbiasa, tetapi mereka menggunakan "radian". Menggunakan derajat, ada 360 derajat ke lingkaran penuh. Menggunakan Radian, ada 2 × π "derajat". π sebenarnya adalah angka, tetapi tidak mungkin untuk menulis tanpa menggunakan satu juta milyar angka desimal, jadi kami menggunakan huruf untuk itu sebagai gantinya. Nilainya kira-kira 3,14, dan adalah nilai yang Anda dapatkan jika Anda menggambar lingkaran dengan diameter setengah. (satu setengah meter, satu setengah kaki, tidak terlalu penting). Jika Anda mengukur berapa lama lingkaran sebenarnya, jawabannya adalah π. Untuk pengukuran sudut, kita sebenarnya menggunakan lingkaran khusus yang disebut "lingkaran unit", yang memiliki diameter dua. (lagi, dua meter atau dua kaki, atau dua inci, tidak masalah). Lingkaran ini memiliki diameter dua kali π.

Jika kita melihat lingkaran unit ini, dan menandai nilai di sekitar garis lingkaran, lalu di ujung kanan, nilainya adalah 0. Bergerak searah jarum jam sepanjang lingkaran, nilai paling bawah adalah setengah π, paling kiri adalah π , bagian atas adalah satu setengah π, dan kemudian ketika kita kembali ke tempat kita mulai, kita mendapatkan dua π, yang sama dengan nol. Ini mungkin terdengar aneh, tetapi anggap itu sama dengan jam. Setelah kita melakukan perjalanan sepanjang angka jam, kita berakhir di awal dan angka "reset". Itu juga terjadi di sini.
Jadi, jika kita ingin melompat dari "puncak", diekspresikan dalam gagasan radian ini, kita ingin datang dengan sudut antara π dan dua π (di suatu tempat antara "kiri" dan "kanan"). Itulah yang dilakukan kode di atas.
class Koopa extends Interactor {  [...]
  void setStates() {    // walking state  State walking = new State("idle", "graphics/enemies/Red-koopa-walking.gif", 1, 2);    walking.setAnimationSpeed(0.12);    addState(walking);
    // if we get squished, we first lose our shell.State noshell = new State("noshell", "graphics/enemies/Naked-koopa-walking.gif", 1, 2);    noshell.setAnimationSpeed(0.12);    addState(noshell);
    setCurrentState("idle");  }
  [...]
  void squish() {    // do we have our shell? Then we only get half-squished.    if (active.name != "noshell") {      setCurrentState("noshell");      return;    }    // no shell... this koopa is toast.    removeActor();  }}
Mendapatkan squished akan melakukan dua hal. Pertama, jika kita adalah pasukan Koopa yang normal, cangkang kita melindungi kita. Alih-alih dikotori mati, kita hanya kehilangan cangkang kita. Tidak ada masalah besar.Tapi, jika Mario mempermainkan kita lagi, kita bersulang. Jadi kode melakukan ini juga:

if (active.name != "noshell") periksa apakah kita sudah kehilangan cangkang kita atau tidak. The !=berarti "tidak", jadi jika keadaan aktif kami bukanlah "tidak shell" negara, kita set sederhana negara saat ini untuk "tidak shell", dan keluar fungsi squish. Kami tidak perlu melakukan lebih banyak.
Namun, jika kita berada dalam keadaan "tidak ada cangkang", kita mati. Sebagai aktor dalam game, itu berarti kita harus dikeluarkan dari permainan, jadi itulah removeActor()fungsi yang dilakukan oleh kita. Setiap aktor (pemain, NPC, pickup, dll) yang memanggil fungsi ini akan dikeluarkan dari permainan, dan untuk semua maksud dan tujuan tidak ada lagi. Itu yang saya sebut mati!


Sejauh ini kita begitu terperangkap dalam menambahkan hal-hal keren yang benar-benar kita lupakan bahwa tujuan dari sebuah permainan adalah untuk memenangkannya. Jadi bagaimana kita menang di game Mario ini? Permainan Mario yang berbeda memiliki cara yang berbeda untuk menang atau keluar dari level, jadi mari gunakan solusi sekolah lama: pos sasaran yang harus kita jalankan, dengan tali garis gawang yang naik dan turun.
Untuk mewujudkan hal ini, kami akan menggabungkan beberapa hal yang sudah kami miliki untuk membuat game kami:

1. pos sasaran "dekat dengan layar",
2. sebuah posting tujuan "di kejauhan", dan
3. tali yang naik dan turun di antara mereka.

Kami akan menggunakan tiga gambar untuk ini:


Seperti yang Anda tebak, kami akan menambahkan pos tujuan kembali ke daftar banyak gambar latar belakang yang sudah kami gunakan, dan mudah-mudahan dapat diprediksi, kami dapat menambahkan posting frong ke daftar gambar "latar depan":
class MarioLayer extends LevelLayer {  [...]  MarioLayer(Level owner) {    [...]    // and let's add the thing that makes us win!    addGoal(1920, height-48);  }  void addGoal(float xpos, float hpos) {    // add the back post to the background images    Sprite back_post = new Sprite("graphics/assorted/Goal-back.gif");    back_post.align(CENTER, BOTTOM);    back_post.setPosition(xpos, hpos);    addBackgroundSprite(back_post);    // and add the front post ot the foreground images    Sprite front_post = new Sprite("graphics/assorted/Goal-front.gif");    front_post.align(CENTER, BOTTOM);    front_post.setPosition(xpos+32, hpos);    addForegroundSprite(front_post);    // the finish line rope    addForPlayerOnly(new Rope(xpos, hpos-16));  }}
Sejauh ini bagus, kecuali untuk baris terakhir itu. Ia mengatakan kami menambahkan Ropeke daftar hal-hal yang hanya untuk pemain ... apakah itu Pickup? Nah, jika Anda ingat deskripsi pickup, mereka adalah hal-hal yang pemain bisa masuk ke, untuk membuat "sesuatu" terjadi, dan itulah yang kami inginkan! Ketika Mario berlari ke tali, kami ingin dia memenangkan pertandingan, jadi kami akan membuat pickup baru yang terlihat seperti tali kami:
class Rope extends MarioPickup {  Rope(float x, float y) {    // use the right picture for our rope    super("Finish line", "graphics/assorted/Goal-slider.gif", 1, 1, x, y, true);
    // now, set up the "up and down" animation for this pickup    Sprite rope_sprite = getState("Finish line").sprite;    rope_sprite.align(LEFT, TOP);
    // there are two paths: up, and down. First, up: we move 116 pixels up, over 50 frames    rope_sprite.addPathLine(0, 0,      1,1,0,   0, -116,   1,1,0,    50);
    // and then down: we move the same 116 pixels down, over 50 frames    rope_sprite.addPathLine(0, -116,   1,1,0,   0, 0,      1,1,0,    50);
    // then, make sure that we keep "playing" this    // animation, and make sure we don't try to     // "drive along the path", because that would    // look really weird.    rope_sprite.setNoRotation(true);    rope_sprite.setLooping(true);  }}
Anda mungkin telah memperhatikan bahwa ini adalah pekerjaan yang sedikit lebih banyak daripada pickup lainnya, karena kami ingin tali ini bergerak naik dan turun. Kami melakukan ini dengan memberikan "jalur jalur" sprite yang dapat dilewati. Kode di atas menunjukkan cara membuatnya bergerak naik dan turun, tetapi kami juga dapat mengukur dan memutar di atas jalur. "1,1,0" itu, yang Anda lihat empat kali (dua kali untuk naik, dan dua kali ke bawah) berarti "skala secara horizontal sebesar 1,0, skala vertikal sebesar 1,0, dan putar sebesar 0 derajat", sehingga sprite kami akan terlihat sama di awal jalurnya seperti di ujung jalurnya, untuk jalur naik dan turun.

Jadi bagian terakhir dari teka-teki ini adalah: apa yang terjadi ketika Mario mengambil tali ini? Yah ... dia harus memenangkan pertandingan! Jadi, mari tambahkan sedikit terakhir ke kode Mario dan lihat seperti apa permainan kami sekarang sehingga kami bisa menang:
class Mario extends Player {  [...]
  void setupStates() {    [...]
    // add a winning state for Mario. We'll make his victory last 15 frames.    State won = new State("won", "graphics/mario/small/Standing-mario.gif");    won.setDuration(15);    addState(won);
    setCurrentState("idle");      }
  void handleStateFinished(State which) {    // we reset the game not just if Mario dies, but    // also if he's won and his winning state is "done":    if(which.name == "dead" || which.name == "won") {      removeActor();      reset();    } else {      setCurrentState("idle");    }  }
  [...]
  void pickedUp(Pickup pickup) {    // when we pick up a rope, win!    if (pickup.name=="Finish line") {      setCurrentState("won");    }  }
  [...]}
Cukup lurus ke depan pada saat ini, bukan? Tambahkan status kemenangan, pastikan bahwa kami menyetel ulang gim agar kami dapat bermain lagi ketika kami selesai dengan status kemenangan kami, dan pastikan untuk mengubah status kemenangan kami jika kami mengambil alih talinya.

Menyelesaikan permainan kami

Pada titik ini kita memiliki semua elemen utama di tempat. Kami memiliki pahlawan, kami memiliki orang jahat, kami memiliki harta, kami bisa mati atau kami bisa menang. Ini semua sangat penting untuk permainan yang menyenangkan untuk dimainkan. Namun, masih ada beberapa pekerjaan yang harus dilakukan.

LEBIH BANYAK MUSUH
Pertama, kita butuh lebih banyak musuh. Jika kita melompati polisi Koopa itu, atau menghimpitnya, sisa levelnya bebas dari bahaya. Itu tidak terlalu menarik atau menggairahkan, jadi mari kita mulai menempatkan pasukan Koopa lagi ke level kita.

Kami hanya dapat menambahkan lebih banyak Koopas dengan cara yang sama seperti yang kami lakukan sebelumnya, tetapi ini adalah saat yang tepat untuk berbicara tentang "pemicu". Bayangkan berjalan melalui tingkat, dan tidak ada pasukan Koopa sama sekali. Namun, ketika Anda berjalan di atas tanah tertentu, seorang polisi Koopa ditambahkan di luar layar. Untuk pemain, seolah-olah levelnya diisi dengan pasukan Koopa, tetapi di belakang layar, kami hanya menjatuhkan mereka pada saat-saat terakhir.

Kenapa kita melakukan ini? Mengapa tidak menambahkan semuanya di awal? Nah, jika kita melakukan itu, mereka juga akan berjalan di sekitar level dari detik pertama. Itu membuatnya sangat sulit untuk mengontrol apa yang dilihat oleh Mario ketika mereka muncul, karena kita perlu memperhitungkan setiap saat bahwa Mario tidak melihatnya. Jika kita menjatuhkan mereka pada saat-saat terakhir sebelum Mario "harus" melihat mereka, kita bisa lebih mengontrol di mana mereka ketika mereka muncul.

Ini juga berarti kita dapat menambahkan musuh yang tampaknya lebih dari biasanya. Jika kita menambahkan pasukan Koopa seratus di awal, dan Mario hanya melihat tiga atau empat sekaligus, kita akan membuang-buang banyak waktu komputer membuatnya menghitung di mana untuk menempatkan orang-orang 96 atau 97 pasukan Koopa lainnya karena mereka jalan-jalan. Untuk sebagian besar dari mereka, itu akan menjadi detik atau bahkan beberapa menit sebelum mereka muncul ... mengapa membuang waktu komputer ketika kita dapat menjatuhkan musuh pada saat-saat terakhir, bukan?

Pemicu
Untuk membuat "pemicu koopa trooper", kita dapat menggunakan perpustakaan permainan yang dibangun di "Trigger", dan sama seperti sebelumnya ketika kita membuat aktor atau pickup, memperluas dari kelas Trigger ini: 
class KoopaTrigger extends Trigger {  // these values will let us position a new Koopa trooper when triggered   float koopa_x, koopa_y;    // our constructor is pretty straight forward. The first four values  // are where the trigger is in the left, the last two where a new koopa  // should be made, relative to trigger.  KoopaTrigger(float x, float y, float w, float h, float _kx, float _ky) {    super("koopa", x, y, w, h);    koopa_x = _kx;    koopa_y = _ky;  }
  // when Mario runs through the trigger, this function is automatically  // called by the game engine, and the instructions in it are run.  void run(LevelLayer layer, Actor actor, float[] intersection) {    // make a new Koopa trooper, relative to the trigger's position    Koopa k = new Koopa(x+koopa_x, koopa_y);    // add this Koopa trooper to the list of enemies for the layer the trigger is in    layer.addInteractor(k);    // and remove this trigger so that it's not repeated. Unless we want infinite Koopas!    removeTrigger();  }}
Jadi pemicu khusus ini relatif sederhana: ia menempati beberapa ruang persegi panjang di lapisan tingkat, dan ketika seorang aktor berjalan melaluinya, kode dalam run(...,...,...)fungsi dijalankan. Dalam hal ini, buat koopa, tambahkan ke layer, dan kemudian mematikan pemicu sehingga tidak memicu waktu kedua (atau ketiga, keempat, dll.).

Tentu saja, kita masih perlu menambahkan pemicu ini ke permainan kami, jadi mari lakukan:
class MarioLayer extends LevelLayer {  [...]
  MarioLayer(Level owner) {    [...]
    addTrigger(new KoopaTrigger(412,0, 5,height, 350, height-64));    addTrigger(new KoopaTrigger(562,0, 5,height, 350, height-64));    addTrigger(new KoopaTrigger(916,0, 5,height, 350, height-64));
    // temporarily show triggers in our game:    showTriggers = true;  }}
Kami menempatkan tiga pemicu untuk koopas, semuanya tinggi di seluruh layar, yang pertama di 412 piksel dari kiri tingkat, yang kedua di 562 dan yang ketiga pada 916 piksel dari kiri tingkat. Pemicu ini memiliki lebar 5 piksel, dan semuanya akan memunculkan polisi Koopa baru ke tingkat 350 piksel di sebelah kanan tempat pemicu berada, pada ketinggian lapisan tanah tingkat itu, 64 piksel di atas bagian bawah layar.

Biasanya, pemicu tidak terlihat, tetapi karena kami sedang membuat game kami saat ini, dan kami ingin melihat di mana mereka berada, kami dapat menunjukkan di mana mereka dengan secara eksplisit menyalakan gambar untuk memicu, menggunakan showTriggers = true.


Sekarang game ini sedikit lebih menantang. Tetapi kita bisa melakukan lebih baik

BAHKAN MEMBUAT LEVEL ITU SENDIRI TANTANGAN!

Mari kita buat dunia sedikit lebih berbahaya dengan memperkenalkan kutukan dari banyak pemain Mario: muncher! Di dalam lubang!

Munchers adalah tanaman kecil yang mengganggu yang mencoba mengunyah Anda ketika Anda menyentuh mereka:

Apa yang membuat tanaman ini sangat berbahaya adalah bahwa tidak ada arah aman dari mana kita bisa mendekati mereka. Jika kita menyentuh mereka, kita mati. Untuk membuat segalanya lebih mudah, mari kita masukkan ke dalam lubang, sehingga jika kita melompati pit, kita aman dari muncher, dan jika kita jatuh ke dalam lubang, maka apakah kita memiliki muncher atau tidak, kita akan mati juga. Dengan cara itu, muncher terlihat sangat menakutkan bagi orang-orang yang memainkan permainan kami, tetapi mereka sebenarnya tidak lebih atau kurang berbahaya daripada pit mereka.

Kita dapat membuat lubang dengan memutar tanah dari satu hamparan terus menerus ke dua peregangan, dengan celah di antaranya:
class MarioLayer extends LevelLayer {  [...]
  MarioLayer(Level owner) {    [...]
    mario = new Mario(32, height-64);    addPlayer(mario);
    // add one half of the ground, rather than all of it in one go:    addGround("ground", -32,height-48, -32 + 17*32,height);
    // make sure there's a boundary on the "exposed" side!    addBoundary(new Boundary(-32 + 17*32,height-48,-32 + 17*32,height));
    // and then add the second half, so that there's a gap in between    addGround("ground", -32 + 19*32,height-48, width+32,height);
    // make sure there's a boundary on the "exposed" side here, too!    addBoundary(new Boundary(-32 + 19*32,height,-32 + 19*32,height-48));
    [...]  }
  [...]}
Dan kemudian mari kita isi lubang itu dengan muncher!
class Muncher extends Interactor {  // constructing a Muncher is the same as Mario or Koopas  Muncher(float x, float y) {    super("Muncher");    setPosition(x,y);    setupStates();  }
  void setupStates() {    // Munchers only do one thing... munch!    State munch = new State("munch","graphics/enemies/Muncher.gif", 1, 2);    munch.setAnimationSpeed(0.20);    addState(munch);  }
  void overlapOccurredWith(Actor other, float[] overlap) {    // If someone runs into us... and that someone is Mario... he dies! O_O    if (other instanceof Mario) {      ((Mario)other).die();    }  }}
Disin kami membuat Muncher yang bertanggung jawab untuk membuat Mario mati. Jika dia bertemu dengan seorang Muncher, hal-hal buruk akan terjadi. Tapi, perpustakaan game tidak tahu tentang "Mario", hanya tahu tentang Aktor, jadi kita harus memeriksa dulu apakah Mario terlibat, dan jika demikian, kita perlu memberi tahu perpustakaan permainan bahwa kita akan memanggil fungsi yang hanya ada untuk Aktor dari "Mario" jenis, itulah yang ((Mario)other).die()tentang. The (Mario)other namun mengatakan perpustakaan untuk mempertimbangkan 'lain' semacam Mario, dan (...).die()berarti "memanggil fungsi die () pada apa pun di dalam mereka kurung". 

Jadi sekarang yang tersisa adalah menambahkan tanaman Muncher kami yang lucu, sedikit, mengerikan, dan menghebohkan ke dalam lubang keputusasaan kami ~~!
class MarioLayer extends LevelLayer {  [...]
  MarioLayer(Level owner) {    [...]
    addGround("ground", -32,height-48, -32 + 17*32,height);    addBoundary(new Boundary(-32 + 17*32,height-48,-32 + 17*32,height));
    addInteractor(new Muncher(521, height-8));    addInteractor(new Muncher(536, height-8));    addInteractor(new Muncher(552, height-8));    addInteractor(new Muncher(567, height-8));
    addBoundary(new Boundary(-32 + 19*32,height,-32 + 19*32,height-48));    addGround("ground", -32 + 19*32,height-48, width+32,height);
    [...]  }   [...]}
Keputusasaan selesai! Mari matikan pemicu dan gambar batas, dan mainkan permainan baru kami yang lebih menantang!

Kami sudah cukup jauh, tetapi ada sesuatu yang masih hilang. Sesuatu yang besar. Suara!

MENAMBAHKAN MUSIK DAN EFEK SUARA KE PERMAINAN KAMI

Hingga saat ini, kami bermain tanpa suara. Namun, saya tidak tahu permainan apa pun yang tidak memiliki setidaknya trek musik latar belakang, atau efek suara untuk hal-hal tertentu. Dan permainan kami kehilangan hal-hal itu! Karena kita hampir selesai, saya pikir sudah waktunya kita menambahkan itu, karena itu agak konyol untuk bermain dengan suara dimatikan, bukan?

The SoundManager: memuat, memutar, dan berhenti
Perpustakaan permainan menawarkan kita satu objek untuk menangani semua kebutuhan audio kita, SoundManagerobjek. Objek ini dapat memuat, memutar, menjeda, dan menghentikan file audio untuk kami, selama kami dapat mengikat file-file itu ke berbagai hal. Jadi mari kita lihat bagaimana kita dapat menggunakan SoundManager untuk memainkan musik latar belakang. Biasanya Anda ingin mengaitkan satu trek musik latar dengan setiap level, jadi mari kita berikan beberapa musik MarioLevel kami:
class MainLevel extends Level {  MainLevel(float levelWidth, float levelHeight) {    super(levelWidth, levelHeight);    addLevelLayer("background layer", new MainBackgroundLayer(this));    addLevelLayer("main layer", new MainLevelLayer(this));    setViewBox(0,0,screenWidth,screenHeight);    // Add some background music!    SoundManager.load(this, "audio/bg/Overworld.mp3");    SoundManager.play(this);  }}
Semuanya masih sama seperti sebelumnya kecuali untuk SoundManagerkode: Pertama kita memuat file, dan pastikan untuk "mengikat" ke level itu sendiri (ingat thiskata kunci dari tutorial terakhir?). Kemudian, kami segera mulai memainkannya.
Di mana saja, idenya hampir sama: kita mengaitkan file suara dengan "hal", menggunakan SoundManager.load(thing, "path to sound file"), dan kemudian ketika kita perlu memainkannya, kita sebut SoundManager.play(thing). Jadi, misalnya, mari berikan Mario semua efek suaranya:
class Mario extends Player {  [...]
  void setStates() {    [...]
    State dead = new State("dead", "graphics/mario/small/Dead-mario.gif", 1, 2);    dead.setAnimationSpeed(0.25);    dead.setDuration(100);    addState(dead);       // sound effect associated with this state:    SoundManager.load(dead, "audio/Dead mario.mp3");
    State jumping = new State("jumping", "graphics/mario/small/Jumping-mario.gif");    jumping.setDuration(15);    addState(jumping);    // sound effect associated with this state:    SoundManager.load(jumping, "audio/Jump.mp3");
    [...]  }
  void handleInput() {    [...]
    if(isKeyDown('W') &amp;&amp; active.name!="jumping" &amp;&amp; boundaries.size()&gt;0) {      addImpulse(0,-35);      setCurrentState("jumping");      // play our jump sound when we jump:      SoundManager.play(active);    }
    [...]  }
  [...]
  void die() {    setCurrentState("dead");    setInteracting(false);    addImpulse(0,-30);    setForces(0,3);    // when we die, stop the background music    SoundManager.stop(getLevelLayer().getLevel());    // and then play the sad music =(    SoundManager.play(active);  }
  [...]}
Perhatikan bahwa kami mengubah durasi untuk keadaan "mati" Mario menjadi 100 bingkai, bukan 15, sekarang. Itu karena musik yang dimainkan membutuhkan 100 frame untuk dimainkan.

Pasukan Koopa dimodifikasi dengan cara yang sama:
class Koopa extends Interactor {  [...]
  void setStates() {    State walking = new State("idle", "graphics/enemies/Red-koopa-walking.gif", 1, 2);    walking.setAnimationSpeed(0.12);    // the only sound koopas can make while walking is the "getting squished" sound    SoundManager.load(walking, "audio/Squish.mp3");    addState(walking);
    State noshell = new State("no shell", "graphics/enemies/Naked-koopa-walking.gif", 1, 2);    noshell.setAnimationSpeed(0.12);    // we make the same sound when we're squished a second time.    SoundManager.load(noshell, "audio/Squish.mp3");    addState(noshell);
    setCurrentState("idle");  }
  [...]
  void squish() {    // play the squish sound, regardless of whether we lose our shell, or die    SoundManager.play(active);
    [...]  }}
Untuk pickup, kita memuat file di konstruktor, dan memutarnya ketika kita dijemput:
class Coin extends MarioPickup {  Coin(float x, float y) {    super("Regular coin", "graphics/assorted/Regular-coin.gif", 1, 4, x, y, true);    // set up the clip to play when we get picked up    SoundManager.load(this, "audio/Coin.mp3");  }  void pickedUp() {    // play our "picked up" sound clip    SoundManager.play(this);  }}
Jadi jika kita semua efek suara di mana pun kita menginginkannya, akhirnya kita mendapatkan permainan yang terasa seperti permainan nyata.


Dan di sana kita memilikinya, permainan penuh. Dan dalam waktu kurang dari sehari!
Jika Anda ingin melihat kode lengkap kami akhirnya menulis sebagai satu file besar, klik disini ini akan terlihat sangat dekat dengan apa yang Anda miliki di Processing jika Anda sudah memprogram!

23 comments:

  1. Sangat bermanfaat di tunggu lagi blog berikutnya

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. makasih ilmu nya ya gan, izin share ya gan

    ReplyDelete
  4. Thanks infonya bg, sekalian diajarin main game marionya biar langsung bg biar lebih pro:v

    ReplyDelete
  5. Keren bang, terus bisa jadi inspirasi dan informasinya sangat bermanfaat

    ReplyDelete