Script dan Skenario
Perkembangan teknologi komputer sangat
mempengaruhi perkembangan yang ada dalam dunia game. Pembentukan karakter pada
game semakin mendekati dengan keadaan sebenarnya pada dunia nyata. Ketika
proses pembuatan game membutuhkan susunan yang lebih kompleks, timbullah
kebutuhan untuk memisahkan konten (desain perilaku) dari mesin. Kehandalan
seorang desainer game benar-benar dibutuhkan untuk merancang perilaku yang luas
dari karakter. Banyak tersedia berbagai macam bentuk bahasa pemrograman yang
ada. Tentunya dari kesemuanya itu memiliki kelebihan dan kekurangan
masing-masing. Maka game developer harus benar-benar teliti dalam memilih
bahasa dan menyusun script untuk membuat suatu game, agar nantinya game dapat
berjalan lancar, tanpa bug, dan mampu menjalankan script dengan kebutuhan
hardware seminimum mungkin.
Konsep Pembuatan
Scripting pada game
·
Pemilihan Bahasa Pemrograman
Pemrograman suatu game bisa menggunakan
berbagai macam jenis bahasa pemrograman. Diantaranya yang terkenal adalah C++,
C dan Java.
Proses pembuatan game modern bisa memakan
waktu 1-3 tahun untuk menyelesaikannya. Lamanya suatu pengembangan bergantung
pada sejumlah faktor, seperti genre, skala, platform pengembangan dan jumlah
aset.
Sebagai contoh, sebuah game puzzle yang
sederhana dengan menggunakan grafik 2D akan jauh lebih sedikit memakan waktu
untuk dikembangkan daripada game role-playing komputer 3D.
Walaupun terdapat banyak sekali pilihan
bahasa pemrograman untuk membuat game, namun pada intinya bahasa tersebut harus
memenuhi syarat sebagai berikut :
a) Speed
Bahasa scripting untuk game harus mampu
berjalan secepat mungkin. Jika kita berniat untuk menggunakan banyak script
untuk perilaku karakter dan kejadian di level game, maka script akan perlu
untuk mengeksekusi sebagai bagian dari loop game utama. Ini berarti bahwa
script yang lambat akan memakan waktu yang kita butuhkan untuk membuat suatu
adegan, menjalankan mesin fisika, atau menyiapkan audio.
b) Kompilasi dan Interpretasi (penyusunan dan
penafsiran)
Bahasa scripting secara luas
diinterpretasikan,melalui susunan serangkaian byte. Penafsiran bahasa diambil
melalui format teks. Interpreter melihat setiap baris, penyusunan bekerja
sesuai yang maksud dari script, dan melakukan tindakan yang spesifik.
Bahasa byte-terkompilasi dikonversi dari teks
ke format internal, yang disebut byte code. Kode byte biasanya jauh lebih padat
daripada format teks. Karena kode byte dalam suatu format dioptimalkan ketika
dieksekusi, agar dapat berjalan lebih cepat.
c) Ekstensibilitas dan Integrasi
Bahasa scripting perlu memiliki akses ke
fungsi yang signifikan ke dalam game. Sebuah script yang mengendalikan
karakter, misalnya, harus mampu untuk menanyai game untuk mencari tahu apa yang
bisa dilihat dan kemudian membiarkan game tahu apa yang akan dilakukan sebagai
aksinya.
Serangkaian fungsi yang dibutuhkan untuk
mengakses ini jarang diketahui ketika bahasa scripting telah diimplementasikan
atau dipilih. Hal ini penting untuk memiliki sebuah bahasa yang dapat dengan
mudah memanggil fungsi-fungsi atau menggunakan kelas main code dalam game.
Biasanya, itu adalah penting bagi programmer untuk dapat mengekspos fungsi baru
atau kelas yang dengan mudah ketika pembuat script memintanya.
d) Re-Entrancy (ikut serta ulang)
Fungsi ini sering berguna untuk memanggil
script menjadi diikutsertakan ulang. Mereka dapat berjalan untuk sementara
waktu, dan ketika anggaran waktu telah habis script akan dapat ditunda. Ketika
script selanjutnya mendapatkan beberapa waktu kembali, maka akan dapat
menjalankan kembali script yang ditunda sebelumnya.
Hal ini sering membantu untuk membiarkan
kontrol hasil script saat mencapai jeda normal. Kemudian sebuah algoritma
penjadwalan dapat memberikan lebih banyak waktu untuk meminimalisasi penggunaan
sumber daya.
Sebuah script untuk mengendalikan sebuah
karakter, misalnya, mungkin memiliki lima tahapan yang berbeda (memeriksa
situasi, memeriksa kesehatan, menentukan gerakan, rencana rute, dan
melaksanakan gerakan). Ini semua dapat dimasukkan dalam satu script yang
menghasilkan penyekat antara setiap bagian. Kemudian masing-masing akan
berjalan dengan setiap lima frame, dan beban dari eksekusi AI akan
didistribusikan.
Re-entrancy yang lebih canggih harus
memungkinkan penulis script untuk menandai bagian yang tidak boleh terputus.
·
Embedding (penanaman)
Embedding berhubungan dengan ekstensibilitas. Sebuah
bahasa yang tertanam dirancang untuk dimasukkan ke dalam program lain. Ketika
kita menjalankan bahasa scripting dari workstation, biasanya akan menjalankan
program khusus untuk menafsirkan file source code. Dalam game, sistem scripting
perlu dikontrol dari dalam program utama. Game yang menentukan jalannya script
harus dijalankan dan harus dapat memberitahu mesin terkait bahasa scripting
yang cocok untuk memproses script tersebut.
·
Bahasa Open Source
Banyak bahasa scripting game populer yang dirilis di
bawah lisensi open source. Software open-source dirilis di bawah lisensi yang
memberikan hak user untuk memasukkannya ke dalam perangkat lunak mereka sendiri
tanpa membayar biaya tambahan.
Memulai sebagai teks dalam sebuah file teks, script
biasanya melewati empat tahap:
1.
Tokenization :
Tokenizing
mengidentifikasi unsur-unsur dalam teks. Sebuah file teks hanya berisi
serangkaian karakter (dalam pengertian karakter ASCII). Hasil tokenizer keluar
sebagai kumpulan byte tertentu dan jenis dari kelompok tertentu yang membentuk
mereka.
2. Parsing
(penguraian) :
Makna
dari sebuah program adalah sangat hirarkis: nama variabel dapat ditemukan dalam
sebuah statement pemberian nilai, ditemukan di dalam pernyataan IF-, yang ada
di dalam tubuh fungsi, di dalam definisi kelas, maupun di dalam sebuah
deklarasi namespace, misalnya. Parser mengambil urutan token, mengidentifikasi
peran masing-masing kode dalam program, dan mengidentifikasi struktur hirarkis
keseluruhan program.
Contoh1 :
if (a < b) return;
dapat
dilakukan proses parsing seperti pada bagan di bawah ini :
3.
Compiler :
mengubah
parse tree ke dalam kode byte yang dapat dijalankan oleh interpreter. Kode byte
biasanya berbentuk data biner berurutan. Compiler non-optimasi biasanya berisi
output kode byte sebagai terjemahan literal dari parse tree.
4.
Interpreting :
Tahap
akhir dari serangkaian ini ialah menjalankan kode byte. Dalam sebuah compiler
untuk bahasa seperti C atau C++, produk akhir akan menjadi mesin instruksi yang
dapat langsung dijalankan oleh prosesor. Dalam bahasa scripting, anda sering
perlu untuk menyediakan layanan (seperti re-entrancy dan eksekusi yang aman)
yang tidak mudah dicapai dengan bahasa mesin.Kode byte
akhir dijalankan pada sebuah “mesin virtual”. Ini secara efektif menjadi sebuah
emulator untuk sebuah mesin yang belum pernah ada di hardware. Anda menentukan
instruksi agar mesin dapat mengeksekusi, dengan instruksi kode byte.
Contoh Pembuatan game mengunakan script bahasa C.
Disini kami mengunakan game snake mengunakan script bahasa C
Langkah pertama maskan sintaks library
1.
#include <stdio.h>
2. #include <stdlib.h>
3.
#include <time.h>
4. #include <sys\timeb.h>
5.
#include <Windows.h>
Dalam sintaks digunakan Array dimana Array ini kita gunakan untuk menampung ular. Satu
elemen pada array sama
dengan satu segmen ular. Tiap elemen berisi posisi koordinat (x,y) segmen di
layar. Berikut ini bentuk strukturnya. Kita beri nama Segment.
1.
/** Struktur **********/
2.
3.
/**
4. Struktur untuk menampung data tiap segment
dari snake
5.
(array)
6. */
7.
struct Segment {
8. int x, y;
9.
};
Kita tambahkan dua
variabel global, yaitu array snake, dan length untuk
menyimpan panjangnya.
1.
/** Variabel global **********/
2.
3.
// Array untuk menampung data ular
4. struct Segment snake[2000];
5.
6. // Variabel untuk
menyimpan panjang ular (array snake)
7.
int length = 0;
Untuk game ini,
kita menggunakan konsep queue. Artinya,
elemen pada array akan ditambahkan di awal
(head), dan ketika dihapus, yang hilang adalah bagian
akhir (tail). Istilahnya first in first out.
Berikut ini fungsi untuk melakukan
penambahan push() dan penghapusan pop().
1.
/** Fungsi-fungsi **********/
2.
3.
/**
4. Push segment ke snake (pada bagian head).
5.
*/
6. void push(int x, int y) {
7.
for(int i = length; i > 0; i--) {
8. snake[i] = snake[i-1];
9.
}
10. snake[0].x = x;
11. snake[0].y = y;
12. length++;
13. }
14.
15. /**
16. Pop bagian ekor snake.
17. */
18. void pop() {
19. length--;
20. }
Ular 3 Segment
Sekarang mari kita coba buat ular
sepanjang 3 segmen pada bagian main(). Oke, supaya
mudah untuk mengubah-ubah pengaturan panjang awalnya, kita simpan nilai 3
tersebut di variabel globalsnake_size. Ketiga
segmen ini kita tempatkan di baris pertama (y = 0), di kolom ke 1, 2, dan 3 (x
= 0 s.d. 2).
1.
/** Konfigurasi permainan **********/
2.
3.
// Panjang segment snake saat awal
permainan
4. int snake_size = 3;
5.
6. /**
7.
Program utama
8. */
9.
int main() {
10. // Pertama-tama, push
segment (node) ke kanan
11. // sebanyak 3 segment
(sesuai nilai variable snake_size)
12. for (int i = 0; i < snake_size; i++) {
13. push(i, 0);
14. }
15.
16. return 0;
17. }
Rendering
Setelah ular dibuat, kita
akan mencetak ular tersebut di layar. Untuk mencetak, kita buat fungsidisplay(). Fungsi display() ini akan membaca nilai x dan y setiap element lalu mencetak satu karakter ‘O’ di
posisi tersebut.
Untuk bisa mencetak di
posisi (x,y), kita harus memindahkan kursor ke posisi tersebut. Untuk itu kita
buat juga fungsi gotoxy().
1.
/**
2. Pindahkan posisi kursor di layar
3.
Fungsi ini spesifik untuk OS windows.
4. */
5.
void gotoxy(int x, int y) {
6. COORD pos;
7.
pos.X = x;
8. pos.Y = y;
9.
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
10. }
11.
12. /**
13. Gambar snake (array) di layar
14. */
15. void display() {
16. for(int i = 0; i < length; i++) {
17. // Cetak di posisi x,y
18. gotoxy(snake[i].x, snake[i].y);
19. printf("O");
20. }
21. }
Sekarang, mari
panggil display() di main(), jalankan
program dan lihat hasilnya (lihat baris 11-19).
1.
/**
2. Program utama
3.
*/
4. int main() {
5.
// Pertama-tama, push
segment (node) ke kanan
6. // sebanyak 3 segment
(sesuai nilai variable snake_size)
7.
for (int i = 0; i < snake_size; i++) {
8. push(i, 0);
9.
}
10.
11. // Tampilkan kondisi
permainan saat ini di layar...
12.
13. // Bersihkan layar
14. system("cls");
15.
16. // Cetak (render)
snake di layar
17. display();
18.
19. getchar();
20. return 0;
21. }
Game Loop
Bagaimana caranya agar
ular bisa bergerak? Caranya, adalah dengan membuat infinite loop untuk me-render ulang layar setiap putarannya. Dengan
demikian, setiap ada perubahan situasi (state) pada arraysnake, entah itu jumlah element (length) atau
nilai x dan y nya, perubahan itu akan langsung tercermin di layar.
Mari kita taruh bagian
rendering tadi ke dalam infinite loop (lihat
baris 11-20).
1. /**
2. Program utama
3. */
4. int main() {
5. // Pertama-tama, push segment (node) ke kanan
6. // sebanyak 3 segment (sesuai nilai variable snake_size)
7. for (int i = 0; i < snake_size; i++) {
8. push(i, 0);
9. }
10.
11. // Game loop. Bagian di dalam while akan dieksekusi terus menerus
12. while (true) {
13. // Tampilkan kondisi permainan saat ini di layar...
14.
15. // Bersihkan layar
16. system("cls");
17.
18. // Cetak (render) snake di layar
19. display();
20. }
21.
22. getchar();
23. return 0;
24. }
Untuk menggerakkan ular
ke kanan setiap 200ms, pertama-tama, di dalam game loop kita
menghitung berapa waktu yang sudah terlewati, jika waktu yang berlalu sudah
lebih atau sama dengan 200ms, maka kita geser ular. Sama dengan sebelumnya,
agar nilai 200 ini mudah diubah-ubah, kita simpan dalam variabel global snake_speed.
1. // Kecepatan gerakan snake dalam ms
2. int snake_speed = 200;
Untuk menghitung interval
waktu yang berlalu, kita gunakan fungsi ftime() untuk
mendapat kan penanda waktu.
Cara menggeser ular,
adalah dengan melakukan pop(), lalu push() kembali
di posisi koordinat headdengan nilai x ditambah 1 karena saat ini
kepala ular mengarah ke kanan.
(Lihat baris 6-8, 17-40)
1. /**
2. Program utama
3. */
4. int main() {
5.
6. // Untuk menyimpan penanda waktu saat snake bergerak
7. struct timeb last_timestamp;
8. ftime(&last_timestamp); // Set nilai awal
9.
10. // Pertama-tama, push segment (node) ke kanan
11. // sebanyak 3 segment (sesuai nilai variable snake_size)
12. for (int i = 0; i < snake_size; i++) {
13. push(i, 0);
14. }
15. // Game loop. Bagian di dalam while akan dieksekusi terus menerus
16. while (true) {
17. // Ambil penanda waktu saat ini
18. struct timeb current_timestamp;
19. ftime(¤t_timestamp);
20.
21. // Selisih waktu terakhir dengan waktu sekarang dalam ms
22. int interval = 1000 * (current_timestamp.time - last_timestamp.time) + (current_timestamp.millitm - last_timestamp.millitm);
23.
24. // Snake bergerak setiap 200 ms (sesuai nilai variable snake_speed)
25. // Dihitung dengan membandingkan selisih waktu sekarang dengan waktu
26. // terakhir kali snake bergerak.
27. if (interval >= snake_speed) {
28. // Tentukan posisi x,y ke mana snake akan bergerak.
29. int x, y;
30. x = snake[0].x + 1;
31. y = snake[0].y;
32.
33. // Pop ekor, lalu push segment ke depan head sehingga
34. // snake tampak bergerak maju.
35. pop();
36. push(x, y);
37.
38. // Perbarui penanda waktu
39. last_timestamp = current_timestamp;
40. }
41.
42. // Tampilkan kondisi permainan saat ini di layar...
43.
44. // Bersihkan layar
45. system("cls");
46.
47. // Cetak (render) snake di layar
48. display();
49. }
50.
51. ...
52. }
Coba jalankan lagi. Sekarang ular sudah bisa bergerak!
Tapi layar tampaknya
berkedip-kedip. Hal ini terjadi karena program mencoba mengosongkan layar
dengan system(“cls”); sebelum
menggambar lagi. Umumnya pembuat game akan melakukan teknik double buffering untuk menghindari layar berkedip (flicker). Namun untuk menyederhanakan tutorial ini,
kita akan lakukan pendekatan lain, yaitu dengan me-render ulang layar hanya ketika ular
bergerak. Sehingga rendering hanya
terjadi setiap 200ms sekali (5 FPS).
Caranya mudah, kita
pindahkan baris-baris rendering ke
dalam blok if(interval >= snake_speed) { } (lihat baris
30-37).
1. /**
2. Program utama
3. */
4. int main() {
5. ...
6.
7. // Game loop. Bagian di dalam while akan dieksekusi terus menerus
8. while (true) {
9. // Ambil penanda waktu saat ini
10. struct timeb current_timestamp;
11. ftime(¤t_timestamp);
12.
13. // Selisih waktu terakhir dengan waktu sekarang dalam ms
14. int interval = 1000 * (current_timestamp.time - last_timestamp.time) + (current_timestamp.millitm - last_timestamp.millitm);
15.
16. // Snake bergerak setiap 200 ms (sesuai nilai variable snake_speed)
17. // Dihitung dengan membandingkan selisih waktu sekarang dengan waktu
18. // terakhir kali snake bergerak.
19. if (interval >= snake_speed) {
20. // Tentukan posisi x,y ke mana snake akan bergerak.
21. int x, y;
22. x = snake[0].x + 1;
23. y = snake[0].y;
24.
25. // Pop ekor, lalu push segment ke depan head sehingga
26. // snake tampak bergerak maju.
27. pop();
28. push(x, y);
29.
30. // Tampilkan kondisi permainan saat ini di layar...
31.
32. // Bersihkan layar
33. system("cls");
34.
35. // Cetak (render) snake di layar
36. display();
37.
38. // Perbarui penanda waktu
39. last_timestamp = current_timestamp;
40. }
41. }
42.
43. ...
44. }
Mengontrol
Arah Gerakan Ular
Untuk bisa mengontrol
arah gerakan ular, kita membuat sebuah variabel global tambahan bernamadir. Variabel ini memberitahu arah push() berikutnya, apakah ke kanan, bawah,
kiri, atau atas. Arah ini akan ditentukan berdasarkan input tombol panah yang
ditekan.
Pertama-tama, buat
variabel global dir, dengan nilai awal ke arah kanan. VK_RIGHT
adalah konstanta berisi kode untuk tombol panah kanan.
1. // Arah kepala saat awal permainan
2. int dir = VK_RIGHT;
Sekarang kita modifikasi
penentuan nilai x dan y untuk melakukan push() berdasarkan
variabel dir. Lalu di dalam game loop,
dilakukan juga pengecekan tombol yang sedang ditekan. Jika merupakan salah satu
dari empat tombol panah di keyboard, maka ubah nilai dir (lihat baris 17-37, 56-73).
1. /**
2. Program utama
3. */
4. int main() {
5. ...
6.
7. // Game loop. Bagian di dalam while akan dieksekusi terus menerus
8. while (true) {
9.
10. ...
11.
12. // Snake bergerak setiap 200 ms (sesuai nilai variable snake_speed)
13. // Dihitung dengan membandingkan selisih waktu sekarang dengan waktu
14. // terakhir kali snake bergerak.
15. if (interval >= snake_speed) {
16. // Tentukan posisi x,y ke mana snake akan bergerak.
17. // Posisi dilihat dari koordinat segment kepala (head)
18. // dan arah (variable dir)
19. int x, y;
20. switch (dir) {
21. case VK_LEFT:
22. x = snake[0].x - 1;
23. y = snake[0].y;
24. break;
25. case VK_RIGHT:
26. x = snake[0].x + 1;
27. y = snake[0].y;
28. break;
29. case VK_UP:
30. x = snake[0].x;
31. y = snake[0].y - 1;
32. break;
33. case VK_DOWN:
34. x = snake[0].x;
35. y = snake[0].y + 1;
36. break;
37. }
38.
39. // Pop ekor, lalu push segment ke depan head sehingga
40. // snake tampak bergerak maju.
41. pop();
42. push(x, y);
43.
44. // Tampilkan kondisi permainan saat ini di layar...
45.
46. // Bersihkan layar
47. system("cls");
48.
49. // Cetak (render) snake di layar
50. display();
51.
52. // Perbarui penanda waktu
53. last_timestamp = current_timestamp;
54. }
55.
56. // Ubah arah jika tombol panah ditekan
57. if (GetKeyState(VK_LEFT) < 0) {
58. dir = VK_LEFT;
59. }
60. if (GetKeyState(VK_RIGHT) < 0) {
61. dir = VK_RIGHT;
62. }
63. if (GetKeyState(VK_UP) < 0) {
64. dir = VK_UP;
65. }
66. if (GetKeyState(VK_DOWN) < 0) {
67. dir = VK_DOWN;
68. }
69.
70. // Keluar dari program jika menekan tombol ESC
71. if (GetKeyState(VK_ESCAPE) < 0) {
72. return 0;
73. }
74. }
75.
76. ...
77. }
Kita juga bisa menambahkan pengecekan untuk keluar dari
program jika pemain menekan tombol ESC.
Coba jalankan lagi program, sekarang kita bisa
menggerakan ular dengan bebas
Collision Detection
Salah satu aspek yang
penting dalam permainan ini adalah pengecekan apakah kepala ular bertabrakan
dengan dinding atau dirinya sendiri. Di sini kita bisa melakukan pengecekan
saat program memeroleh posisi x dan y yang baru, sebelum melakukan pop() dan push().
Jika posisi x berada di
luar batasan 0-79 (panjang console) atau posisi y berada diluar batasan 0-24
(tinggi console), maka ular telah menabrak dinding, dan permainan berakhir.
Sama seperti sebelum-sebelumnya, untuk nilai panjang dan lebar console bisa
kita simpan di variabel global console_widthdan console_height.
1. // Panjang console
2. int console_width = 80;
3.
4. // Tinggi console
5. int console_height = 25;
Pengecekan berikutnya
yaitu mengecek apabila posisi x dan y sama dengan posisi salah satu node, yang artinya
ular menabrak dirinya sendiri. Untuk mengeceknya, kita buat fungsi check_collision().
1. /**
2. Memeriksa apakah terdapat salah satu segment
3. snake (array) di koordinat x,y.
4. Return 0 artinya tidak bertumpuk, 1 artinya bertumpuk.
5. */
6. int check_collision(int x, int y) {
7. for(int i = 0; i < length; i++) {
8. if (snake[i].x == x && snake[i].y == y) {
9. return 1;
10. }
11. }
12. return 0;
13. }
Berikut ini baris-baris
yang ditambahkan di main() untuk
melakukan pengecekan tadi, serta tambahan baris yang dilakukan di luar game loop, setelah
permainan berakhir (game over) (lihat baris 18-32,
44-48).
1. /**
2. Program utama
3. */
4. int main() {
5. ...
6.
7. // Game loop. Bagian di dalam while akan dieksekusi terus menerus
8. while (true) {
9.
10. ...
11. // Snake bergerak setiap 200 ms (sesuai nilai variable snake_speed)
12. // Dihitung dengan membandingkan selisih waktu sekarang dengan waktu
13. // terakhir kali snake bergerak.
14. if (interval >= snake_speed) {
15.
16. ...
17.
18. // Jika posisi kepala (head) menabrak tembok pembatas,
19. // maka permainan berakhir (keluar dari game loop)
20. if (x < 0 || x >= console_width || y < 0 || y >= console_height) {
21. break;
22. }
23.
24. // Jika posisi kepala (head) menabrak dirinya sendiri
25. // (posisi sama dengan salah satu segment), maka permainan
26. // berakhir (keluar dari game loop)
27. if (check_collision(x, y) == 1) {
28. break;
29. }
30.
31. // Jika tidak terjadi tabrakan (collision), maka snake
32. // boleh bergerak maju..
33. // Pop ekor, lalu push segment ke depan head sehingga
34. // snake tampak bergerak maju.
35. pop();
36. push(x, y);
37.
38. // Tampilkan kondisi permainan saat ini di layar...
39. ...
40. }
41. ...
42. }
43.
44. // Setelah keluar dari game loop, berarti permainan berakhir (game over)
45. system("cls");
46. printf("GAME OVER\n");
47.
48. printf("Press ENTER to exit...");
49. getchar();
50. return 0;
51. }
Jalankan program sekali
lagi, lalu coba arahkan ular ke dinding. Untuk pengetesan tabrakan terhadap
diri sendiri, bisa dilakukan dengan mengubah snake_size dengan
nilai yang lebih besar, agar ular cukup panjang untuk menabrak dirinya sendiri.
Tampilan layar
saat terjadi tabrakan. Permainan berakhir.
Makanan!!!
Ini adalah bagian terakhir dari tutorial
ini, makanan! Ular perlu melahap makanan untuk menjadi lebih panjang. Untuk
itu, kita perlu menempatkan makanan di koordinat acak. Untuk menaruh koordinat
makanan, kita tambahkan dua variabel global food_x dan food_y.
1.
// Posisi makanan
2. int food_x, food_y;
Meskipun makanan ditaruh secara acak, ada dua hal yang
perlu diperhatikan:
- Makanan harus berada di dalam layar console berukuran 80×25.
- Makanan tidak boleh bertumpuk dengan ular saat
ditempatkan.
Maka dari itu, kita buat sebuah fungsi place_food() untuk menaruh makanan dengan
memerhatikan kedua syarat tersebut. Untuk syarat nomor 2, kita bisa
memanfaatkan fungsi check_collision() yang
baru saja dibuat.
1.
/**
2. Taruh makanan secara acak, namun
memastikan
3.
makanan tidak bertumpuk dengan salah satu
segment
4. snake (array)
5.
*/
6. void place_food() {
7.
// Jika makanan
bertumpuk dengan salah satu segment
8. // snake, ulangi
penempatan makanan secara acak.
9.
do {
10. food_x = rand() % console_width;
11. food_y = rand() % console_height;
12. } while (check_collision(food_x, food_y) == 1);
13. }
Di awal program sebelum memasuki game loop, kita menempatkan makanan pertama.
Berikutnya, makanan akan ditempatkan ulang jika posisi x dan y baru dari ular
sama dengan koordinat makanan, yang artinya ular memakan makanan. Dalam hal
ini, kita hanya melakukan push() tanpa
melakukanpop(), sehingga jumlah elemen bertambah.
Jangan lupa pula untuk melakukan rendering makanan di layar.
Di samping itu, kita juga bisa
menerapkan sistem penilaian, misalnya nilai bertambah 100 jika ular memakan
makanan. Lalu pada akhir permainan (saat game over), nilai
yang sudah terkumpul ditampilkan kepada pemain.
(Lihat baris 5-6, 12-13, 21-22, 41-53, 64-67, 80)
1.
/**
2. Program utama
3.
*/
4. int main() {
5.
// Randomize
6. srand(time(NULL));
7.
8. // Untuk menyimpan
penanda waktu saat snake bergerak
9.
struct timeb last_timestamp;
10. ftime(&last_timestamp); // Set nilai awal
11.
12. // Untuk menyimpan
nilai
13. int score = 0;
14.
15. // Pertama-tama, push
segment (node) ke kanan
16. // sebanyak 3 segment
(sesuai nilai variable snake_size)
17. for (int i = 0; i < snake_size; i++) {
18. push(i, 0);
19. }
20.
21. // Tempatkan makanan
secara acak
22. place_food();
23.
24. // Game loop. Bagian
di dalam while akan dieksekusi terus menerus
25. while (true) {
26.
27. ...
28.
29. // Snake bergerak setiap 500 ms (sesuai
nilai variable snake_speed)
30. // Dihitung dengan membandingkan selisih
waktu sekarang dengan waktu
31. // terakhir kali snake bergerak.
32. if (interval >= snake_speed) {
33.
34. ...
35.
36. // Jika tidak terjadi
tabrakan (collision), maka snake
37. // boleh bergerak
maju..
38.
39. // Pop ekor, lalu push
segment ke depan head sehingga
40. // snake tampak
bergerak maju.
41. // Namun jika posisi
x,y ke mana kepala (head) snake akan
42. // bergerak berada di
posisi makanan, tidak perlu pop
43. // sehingga segment
bertambah panjang.
44. if (x == food_x && y == food_y) {
45. // Dalam hal snake
memakan makanan, maka nilai bertambah
46. score += 100;
47.
48. // Lalu makanan
ditempatkan ulang secara acak
49. place_food();
50. }
51. else {
52. pop();
53. }
54. push(x, y);
55.
56. // Tampilkan kondisi
permainan saat ini di layar...
57.
58. // Bersihkan layar
59. system("cls");
60.
61. // Cetak (render)
snake di layar
62. display();
63.
64. // Cetak (render)
makanan di layar
65. gotoxy(food_x, food_y);
66. printf("X");
67.
68. // Perbarui penanda
waktu
69. last_timestamp = current_timestamp;
70. }
71.
72. ...
73.
74. }
75.
76. // Setelah keluar dari
game loop, berarti permainan berakhir (game over)
77. // Tampilkan nilai
yang diraih pemain
78. system("cls");
79. printf("GAME
OVER\n");
80. printf("Your score :
%d\n\n", score);
81.
82. printf("Press ENTER to
exit...");
83. getchar();
84.
85. ...
86.
87. }
Selesai! Uji coba program untuk terakhir kalinya, dan
game sudah siap dimainkan!
Layar game over menunjukkan
permainan berakhir beserta nilai yang diperoleh.
StoryBoard
Referensi :