Pengenalan Game Design

Fungsi

Fungsi dalam bahasa pemrograman, sesuai namanya, adalah sebuah komponen dari program yang melakukan tugas tertentu. Penggunaan fungsi memungkinkan kita untuk membuat sebuah program yang modular, dengan komponen-komponen terpisah yang bisa digunakan lagi untuk program yang lain. Selain itu, dengan membiasakan menggunakan fungsi, maka kita bisa melatih diri untuk berpikir prosedural, menyelesaikan masalah dengan pola pikir yang lebih runut.

Sejauh ini, kita telah menggunakan 2 fungsi bawaan Processing, yang perlu ada agar program animasi kita bisa berjalan, yakni setup() dan draw(). Di dalam bab ini, kita akan membuat sendiri fungsi-fungsi lain yang bisa kita gunakan untuk keperluan pembuatan animasi kita.

Menggunakan Fungsi

Format penulisan fungsi adalah dengan sintaks berikut:

tipe_data nama_fungsi (tipe_data_parameter parameter) {
    // tubuh fungsi
}

Tipe data yang digunakan, sama dengan yang kita gunakan saat membuat variabel. Ini menunjukkan data seperti apa yang akan dihasilkan saat fungsi selesai berjalan. Untuk itu, ada juga tipe data bernama void yang artinya fungsi tidak akan mengembalikan nilai apapun setelah selesai berjalan.

Fungsi juga diberikan nama sesuai dengan aturan penamaan variabel. Nama fungsi ini yang akan dipanggil saat kita akan menggunakan fungsi tersebut.

Parameter bersifat opsional, ia adalah input yang diperlukan fungsi untuk berjalan. Sehingga tubuh atau isi fungsi berisi kumpulan pernyataan yang mengolah parameter atau argumen tersebut. Fungsi yang menggunakan parameter harus dipanggil dengan menyertakan parameternya. Sehingga katakan kita memiliki fungsi bernama hitung yang akan melakukan penjumlahan dua buah variabel, bisa saja kita memanggil tersebut seperti ini:

hitung(x, y);

Perlu diperhatikan juga bahwa variabel yang dipanggil harus memiliki tipe data yang sama dengan parameter yang ditetapkan saat membuat fungsi tersebut.

Membuat Fungsi

Fungsi bisa dibuat di bagian manapun dalam program dan bisa dipanggil juga dengan aturan yang sama. Namun kita tidak bisa membuat fungsi di dalam fungsi, itu saja aturannya.

Lalu kapankah kita perlu membuat fungsi sendiri? Aturan dasarnya adalah fungsi perlu dibuat berdiri sendiri apabila kita telah mengumpulkan cukup banyak pernyataan di dalam sebuah fungsi, di mana pernyataan-pernyataan ini memang melakukan tugas tertentu. Selain itu, dengan ini maka rutinitas pemanggilan pernyataan-pernyataan ini bisa kita permudah dengan cara cukup memanggil fungsi yang memuat pernyataan tersebut.

Mari kita ambil contoh seperti berikut. Kita ingin membuat sebuah karakter abstrak yang terdiri dari beberapa bentuk yang berbeda:

  • 1 buah lingkaran besar sebagai badannya
  • 2 buah lingkaran kecil sebagai matanya
  • 1 buah segitiga sebagai mulutnya

Program menggambarnya bisa jadi seperti berikut:

int xPos = 300;
int yPos = 200;

void setup() {
    size (600, 400);
    noStroke();
}

void draw() {
    background (100);

    // mulai menggambar karakter
    fill (0, 120, 150);
    ellipse (xPos, yPos, 200, 200);
    fill (0);
    ellipse (xPos - 40, yPos - 50, 50, 50);
    ellipse (xPos + 40, yPos - 50, 50, 50);
    fill (220, 50, 0);

triangle (xPos - 40, yPos, xPos + 40, yPos, xPos, yPos + 150); }

Pada program di atas, kita membuat sebuah karakter dengan menjalankan beberapa pernyataan, mulai dari bagian yang diberi komentar. Selanjutnya, maka kita bisa mengumpulkan bagian tersebut ke dalam sebuah fungsi yang kita beri nama gambarKarakter().

Kalau kita perhatikan, semua pernyataan menggambar bentuk, menggunakan variabel bernama xPos dan yPos yang diolah dalam berbagai operasi. Kedua variabel ini menentukan posisi di mana karakter akan digambar. Maka, agar fungsi bisa lebih fleksibel, dan kita bisa membuat gambar karakter tadi di mana pun, fungsi gambarKarakter bisa kita lengkapi dengan parameter yang menampung variabel-variabel posisinya. Sehingga, lengkapnya, fungsi gambarKarakter() bisa kita buat dan panggil seperti ini:

// membuat fungsi
void gambarKarakter ( int xPos, int yPos ) {
    fill (0, 120, 150);
    ellipse (xPos, yPos, 200, 200);
    fill (0);
    ellipse (xPos - 40, yPos - 50, 50, 50);
    ellipse (xPos + 40, yPos - 50, 50, 50);
    fill (220, 50, 0);

triangle (xPos - 40, yPos, xPos + 40, yPos, xPos, yPos + 150); }

// memanggil fungsi
// menggambar karakter di koordinat (400, 400)
gambarKarakter ( 400, 400 );

Perhatikan bahwa fungsi gambarKarakter() berisi pernyataan-pernyataan yang melibatkan operasi dengan input berupa xPos dan yPos, yang mana keduanya adalah parameter dari fungsi tersebut. Ketika fungsi dipanggil dengan pernyataan gambarKarakter(400, 400), artinya kedua nilai tersebut secara berurutan dioper ke xPos dan yPos yang ada di tubuh fungsi. Sehingga, pada saat menjalankan fungsi, nilai xPos dan yPos adalah 400. Ini dilakukan berurutan, sesuai dengan posisi variabel pada saat mendeklarasikan fungsi.

Tentu saja, program versi lengkapnya bisa dibuat dengan menggabungkan program di atas ke program sebelumnya. Karena pada dasarnya, kita hanyalah memisahkan sekumpulan pernyataan ke dalam satu fungsi. Sehingga, program lengkapnya adalah sebagai berikut:

Contoh 7-1 Program dengan Fungsi Menggambar Karakter

void setup() {
    size (600, 400);
    noStroke();
}

void draw() {
    background (100);

    // memanggil fungsi
    // menggambar karakter di koordinat (400, 400)
    gambarKarakter ( 400, 400 );
}

// membuat fungsi
void gambarKarakter ( int xPos, int yPos ) {
    fill (0, 120, 150);
    ellipse (xPos, yPos, 200, 200);
    fill (0);
    ellipse (xPos - 40, yPos - 50, 50, 50);
    ellipse (xPos + 40, yPos - 50, 50, 50);
    fill (220, 50, 0);
  triangle (xPos - 40, yPos, xPos + 40, yPos, xPos, yPos + 150);
}

Kita lihat bahwa fungsi gambarKarakter() kita panggil terus menerus di dalam draw(). Dalam prakteknya, bisa saja kita panggil di dalam setup() agar ia hanya dilakukan sekali. Pada titik ini, posisi gambarKarakter() sudah sama dengan fungsi-fungsi lain yang telah kita gunakan. Dengan demikian, selamat, karena Anda telah berhasil membuat fungsi sendiri.

Latihan 7-1 Kembangkan fungsi gambarKarakter() agar ketika dipanggil, warna karakter juga bisa ditentukan. Lalu coba ubah program agar karakter selalu digambar mengikuti posisi mouse.

Nilai Kembali (Return Value) dalam Fungsi

Dalam fungsi di pemrograman, dikenal juga istilah nilai kembali atau return value. Ini adalah nilai yang dibawa kembali oleh fungsi ke dalam program, setelah ia selesai berjalan. Nilai kembali ini ditentukan oleh tipe data yang ditulis ketika mendeklarasikan fungsi. Fungsi gambarKarakter() yang telah dibuat sebelumnya, memiliki tipe data void, yang artinya setelah selesai berjalan, ia tidak mengembalikan data apa-apa. Masuk akal, karena tugas fungsi tersebut adalah menggambar, dan kewajiban tersebut telah ia tunaikan dengan menampilkan bentuk-bentuk di layar.

Untuk mendemonstrasikan bagaimana nilai kembali ini diaplikasikan oleh Processing, mari kita buat fungsi baru yang akan menentukan arah pergerakan karakter yang kita buat di contoh sebelumnya. Kita beri nama fungsi ini gerakKarakter(). Fungsi ini akan melakukan operasi yang menentukan arah gerak karakter dalam bentuk variabel. Operasi akan mengembalikan sebuah nilai integer yang diterima oleh parameter fungsi gambarKarakter(). Agar lebih terasa manfaatnya, fungsi ini akan dilengkapi dengan interaktivitas sederhana melalui input dari keyboard yang akan mendeterminasikan arah geraknya. Jadi ketika tombol 1 ditekan, maka karakter akan bergerak ke kanan. Jika tombol 2 ditekan, maka karakter akan bergerak ke kiri.

Deklarasi fungsinya adalah sebagai berikut:

// variabel global, dibuat di luar fungsi
int arahX = 0;

int gerakKarakter () {
  // hanya terjadi jika tombol keyboard ditekan
  if (keyPressed) {
    // jika tombol 1 ditekan, maka gerak ke kanan
    if (key == '1') {
      arahX += 1;
    }
    // jika tombol 2 ditekan, maka gerak ke kiri
    else if (key == '2') {
      arahX -= 1;
    }
  }
  // besar variabel arahX akan dikembalikan
  return arahX;
}

Jangan lupa, pernyataan return arahX; harus ditulis, karena ini menunjukkan nilai apa yang dikembalikan oleh fungsi. Dalam hal ini, besarnya nilai arahX lah yang akan diberikan oleh fungsi.

Selanjutnya, yang perlu dilakukan, selain memanggil fungsi gerakKarakter(), adalah menyediakan tempat untuk menampung variabel yang dikembalikan oleh fungsi tersebut. Untuk itu, mari kita buat sebuah variabel yang nilainya adlaah hasil operasi di gerakKarakter(). Selanjutnya, nilai variabel ini akan diberikan ke parameter gambarKarakter(), sehingga karakter yang digambar bisa bergerak. Ini semua kita lakukan dalam fungsi draw() agar animasi bisa berjalan.

// variabel global, dibuat di luar fungsi
int posisiX = 0;

void draw() {
  background (140);

  // memanggil fungsi gerakKarakter dan nilainya ditampung oleh variabel posisiX
  posisiX = gerakKarakter();

  // menggambar karakter pada koordinat (posisiX, 200)
  gambarKarakter ( posisiX, 200 );
}

Dari sini kita bisa melihat, bahwa dalam hal meng-assign nilai, fungsi dengan nilai kembali, bisa diperlakukan sama dengan variabel. Contoh di atas menunjukkan sebuah variabel bernama posisiX yang diinisialisasi dengan besar 0, bisa memiliki nilai yang berubah-ubah, mengikuti hasil operasi yang dikembalikan oleh fungsi gerakKarakter(). Pemberian nilai ini pun dilakukan mirip dengan saat kita memberikan nilai pada sebuah variabel. Jika sebelumnya kita pernah menulis pernyataan mirip dengan ini:

posisiX = 50;

Atau

nilai = 78;
posisiX = nilai;

Maka kali ini kita menuliskan

posisiX = gerakKarakter();

int gerakKarakter() {
    .
    .
    .
}

Sudah melihat kemiripannya?

Selanjutnya, nilai posisiX inilah yang kita oper ke gambarKarakter() sebagai parameter yang menentukan posisi karakter. Karena operasi ini dilakukan berulang-ulang selama loop dalam fungsi draw() berjalan, maka karakter pun bisa bergerak secara dinamis.

Program lengkapnya adalah sebagai berikut:

Contoh 7-2 Menggerakkan Karakter dengan Fungsi

int posisiX = 0;
int arahX = 0;

void setup() {
  size (600, 400);
  noStroke();
}

void draw() {
  background (140);

  // memanggil fungsi gerakKarakter dan nilainya ditampung oleh variabel posisiX
  posisiX = gerakKarakter();

  // menggambar karakter pada koordinat (posisiX, 200)
  gambarKarakter ( posisiX, 200 );
}

// membuat fungsi menggambar karakter
void gambarKarakter ( int xPos, int yPos ) {
  fill (0, 120, 150);
  ellipse (xPos, yPos, 200, 200);
  fill (0);
  ellipse (xPos - 40, yPos - 50, 50, 50);
  ellipse (xPos + 40, yPos - 50, 50, 50);
  fill (220, 50, 0);
  triangle (xPos - 40, yPos, xPos + 40, yPos, xPos, yPos + 150);
}

// membuat fungsi menggerakkan karakter
int gerakKarakter () {
  if (keyPressed) {
    if (key == '1') {
      arahX += 1;
    } else if (key == '2') {
      arahX -= 1;
    }
  }
  return arahX;
}

Scope Variabel dalam Fungsi

Pembaca yang jeli mungkin akan mengajukan pertanyaan dari contoh 7-2, "mengapa kedua variabel arahX dan posisiX dibuat sebagai variabel global?" Jawabannya adalah karena kita ingin memiliki sebuah variabel yang bisa terus tersimpan keadaan terakhirnya ketika menjalankan fungsi yang melakukan operasi menggunakan variabel tersebut.

Berdasarkan aturan scope variabel, baik variabel arahX dan posisiX, adalah variabel global, yang nilainya bisa dibaca oleh semua fungsi. Variabel-variabel ini diinisialisasikan di luar fungsi. Sebaliknya, apabila ia diinisialisasikan di dalam fungsi, maka ia menjadi variabel lokal, yang besarnya hanya bisa dibaca oleh fungsi tempat ia dibuat.

Dengan demikian, bila kita melakukan ini di dalam fungsi gerakKarakter()

int gerakKarakter() {
    int arahX = 0;
    arahX ++;
    return arahX;
}

Maka nilai arahX akan terus menerus kembali ke 0, setiap kali fungsi ini berjalan, sehingga karakter tidak akan bergerak. Oleh karena itu, maka ia dibuat sebagai variabel global, yang nilai terakhirnya akan disimpan, dan ketika fungsi berjalan, ia akan melanjutkan menambahkan nilai ke nilai yang sudah ada sebelumnya. Argumen yang sama juga berlaku untuk posisiX.

Namun, ini bukan berarti bahwa penggunaan variabel lokal adalah sebuah hal yang buruk. Aturannya, apabila variabel tersebut hanya dipakai oleh sebuah fungsi dan tidak perlu disimpan terus nilainya, maka sebaiknya, gunakan variabel lokal. Ini akan mencegah anomali dalam program karena sebuah variabel secara tidak sengaja diubah nilainya oleh fungsi lain.

Ambil contoh ini. Saya ingin mengubah ukuran mulut karakter kita. Ini akan saya lakukan dalam fungsi gambarKarakter(), karena tidak perlu dinamis. Sebelumnya, kita lihat bahwa mulut karakter dibuat oleh pernyataan

triangle (xPos - 40, yPos, xPos + 40, yPos, xPos, yPos + 150);

Kali ini, saya ingin mengubah sejauh operan untuk operasi yang melibatkan xPos, agar ukuran mulut karakter bisa berubah.

triangle (xPos - ukuranMulut, yPos, xPos + ukuranMulut, yPos, xPos, yPos + 150);

Lengkapnya adalah sebagai berikut:

void gambarKarakter ( int xPos, int yPos, int offsetMulut ) {
  int ukuranMulut = offsetMulut;
  fill (0, 120, 150);
  ellipse (xPos, yPos, 200, 200);
  fill (0);
  ellipse (xPos - 40, yPos - 50, 50, 50);
  ellipse (xPos + 40, yPos - 50, 50, 50);
  fill (220, 50, 0);
  triangle (xPos - ukuranMulut, yPos, xPos + ukuranMulut, yPos, xPos, yPos + 150);
}

Di sini kita lihat bahwa variabel ukuranMulut adalah sebuah variabel lokal, yang hanya digunakan oleh fungsi gambarKarakter(). Nilainya pun tidak perlu dinamis, karena hanya akan digunakan untuk menggambar sekali. Besarnya nilai variabel ini, kemudian akan ditentukan oleh parameter ketiga dari fungsi gambarKarakter(), yakni offsetMulut, yang mana besarnya diberikan pada saat memanggil fungsi.

Dengan demikian, kita bisa menggambar 2 karakter dengan bentuk mulut yang berbeda dengan pernyataan seperti ini:

gambarKarakter ( posisiX, 100, 20 );
gambarKarakter ( posisiX, 300, 60 );

Latihan 7-2

Buatlah fungsi yang bisa digunakan untuk mengatur secara dinamis ukuran dari karakter kita.