i
DAFTAR ISI DAFTAR ISI
II
1 KATA PENGANTAR
5
1.1 TIM AGATE ACADEMY
6
2 APA YANG AKAN KITA LAKUKAN?
7
2.1 PLATFORM YANG DIGUNAKAN 2.2 KEBUTUHAN 2.2.1 SPESIFIKASI PC 2.2.2 SOFTWARE YANG DIBUTUHKAN 2.2.3 FILE-FILE PENDUKUNG
7 7 7 7 7
3 GAME DEVELOPMENT OVERVIEW
9
3.1 PERAN TIM DEVELOPER 3.1.1 GAME DESIGNER 3.1.2 GAME ARTIST 3.1.3 GAME PROGRAMMER 3.2 PROSES PEMBUATAN GAME 3.2.1 PRE-PRODUCTION 3.2.2 PRODUCTION 3.2.3 RELEASE
9 9 10 10 10 10 10 11
4 PRE-PRODUCTION
12
ii
4.1 GAME DESIGN 4.1.1 CORE GAMEPLAY 4.1.2 FITUR DALAM GAME DAN GAME LOGIC 4.2 VISUAL DESIGN 4.2.1 MOCKUP 4.2.2 ASSET LIST
12 12 12 13 13 15
5 PRODUCTION
17
5.1 GAME ART PRODUCTION 5.1.1 PENGENALAN TOOLS UNTUK MEMBUAT ASET GAME 5.1.2 PEMBUATAN SPRITE 5.1.3 TILING 5.1.4 MAIN MENU/ SCREEN FLOW 5.2 GAME PROGRAMMING 5.2.1 PENGENALAN TOOLS PROGRAMMING YANG DIGUNAKAN 5.2.2 GAME LOOP 5.2.3 ARRAY MULTIDIMENSI 5.2.4 MEMASUKKAN GAMBAR DALAM GAME 5.2.5 MENGHANDLE INPUT PEMAIN 5.2.6 INTERAKSI ANTAR OBJECT 5.2.7 STATE MANAGEMENT 5.2.8 SAVE DAN LOAD GAME 5.2.9 EFEK SUARA 5.2.10 ENCHANCE YOUR GAME! 5.3 LEVEL DESIGN 5.3.1 TOOLS UNTUK MEMBUAT LEVEL 5.3.2 TIPS DALAM MEMBUAT LEVEL
17 17 19 23 25 28 28 32 34 36 44 47 60 79 82 87 88 88 92
6 RELEASE
94 iii
6.1 DIMANA KITA BISA RILIS GAME KITA? 6.2 CARA PUBLISH GAME DI NOKIA STORE
94 94
7 PENUTUP
101
LAMPIRAN A. MEMBUKA PROJECT PADA ECLIPSE PULSAR
102
iv
1
KATA PENGANTAR
Dalam beberapa tahun belakangan pasar dan industri mobile device berkembang dengan pesat. Di Indonesia saja, pengguna mobile device meningkat lebih dari 10% dari tahun 2010 ke 2011, dengan total pengguna 78% dari penduduk Indonesia di tahun 2011. Industri yang mendorong perkembangan pasar mobile adalah mobile gaming, karena pengguna mobile device cenderung menggunakan device mereka untuk bermain game. Nilai industri mobile game dunia diperkirakan mencapai 8 miliar USD tahun ini. Di Indonesia, industri mobile game sedang menjadi trend. Banyak sekali start-up mobile game developer dalam setahun belakangan. Setidaknya lima mobile game startup di Indonesia mendapat perhatian dari berbagai investor asing dari US dan Jepang dalam bentuk investasi. Di luar itu, puluhan mobile game studio sedang berkembang dan sebagian besar membuka kesempatan untuk bekerja menjadi mobile game developer. Buku ini dibuat untuk membuka wawasan pembacanya terhadap industri game, terutama dari sisi teknis pembuatan sebuah mobile game. Kami harap dengan buku ini, pembaca bisa mendapat gambaran tentang bagaimana cara membuat game, dan semoga pembaca bisa menikmati proses pembuatan game di buku ini. Mei 2012 Tim Penulis
5
1.1
TIM AGATE ACADEMY
Berikut ini adalah orang-orang yang terlibat dalam pembuatan buku ini: ADITIA DWIPERDANA Co-founder Agate studio dan Guild Master di Agate Academy. Aditia sering menjadi pembicara dalam event-event Seminar atau Workshop Game Development bersama Agate Studio.
[email protected] ARDIKA PRASETYO Memiliki gelar Academy Author, Ardika adalah orang yang berperan penting dalam pembuatan berbagai artikel tutorial dari Agate Academy baik di media cetak atau pun media online.
[email protected] HENKY JAYA DINATA Salah satu Wizard di Agate Studio. Selain membantu Agate Academy, Henky juga terlibat aktif dalam pembuatan game Football Saga 2.
[email protected]
6
2
APA YANG AKAN KITA LAKUKAN?
2.1
PLATFORM YANG DIGUNAKAN
Pada buku ini, kita akan mempelajari bagaimana membuat game di J2ME. J2ME merupakan platform yang memiliki konsep OOP yang kuat. Karena programming J2ME merupakan bahasa yang sangat dasar, maka ketika kita dapat menguasai J2ME, kita akan mudah untuk mempelajari bahasa pemrograman di platform lain.
2.2
KEBUTUHAN
2.2.1 SPESIFIKASI PC Spesifikasi komputer yang dibutuhkan tidak begitu besar, karena kita menggunakan eclipse sebagai IDE. Komputer dengan spesifikasi setara dengan netbook pun dapat digunakan. Jika teman – teman menggunakan NetBean, maka spesifikasi komputer yang dibutuhkan pun harus lebih tinggi.
2.2.2 SOFTWARE YANG DIBUTUHKAN Software yang kita butuhkan:
JDK 7, sebagai bahasa pemrograman, Eclipse Pulsar, sebagai IDE untuk membuat game. GIMP, sebagai tools untuk membuat asset gambar. Sun WTK, sebagai emulator default dari Java.
File-file installer untuk software-softare di atas dapat ditemukan juga pada CD yang menyertai buku ini atau dapat di akses di http://bit.ly/agatebooks-game-j2me.
2.2.3 FILE-FILE PENDUKUNG
7
Selain file Instalasi software-software yang dibutuhkan, pada CD yang disertakan dengan buku ini juga terdapat beberapa file project yang merupakan hasil akhir dari setiap bab pemrograman pada buku ini. File-file tersebut disediakan untuk membantu Anda dalam mengikuti tutorial dalam buku ini, tapi diharapkan Anda tetap mencoba menulis setiap kode yang diberikan dalam tutorial dan memahami maksud dari setiap bagian kode. Petunjuk untuk membuka file project untuk setiap bab dapat dilihat di Lampiran A. Membuka project pada Eclipse Pulsar. Atau bisa juga diakses di http://bit.ly/Lv1bRo.
8
3 3.1
GAME DEVELOPMENT OVERVIEW PERAN TIM DEVELOPER
Dalam sebuah tim game developer ada beberapa peran yang wajib ada, namun tidak harus satu peran full dilakukan oleh satu orang, tapi bisa saja memiliki peran rangkap kalau anggota tim terlalu sedikit.
3.1.1 GAME DESIGNER Game designer berperan merancang game yang akan dibuat dari sisi konsep, gameplay atau aturan main. Disini game designer akan merancang seluruh desain dari game yang akan dibuat. Mulai dari alur cerita, gameplay, art style, dan lain – lain. Game Designer juga akan membuat Game Design Document (GDD) sebagai tempat untuk mencurahkan idenya dan menyampaikan kepada artist dan programmer. GDD juga berguna sebagai panduan agar revisi dari game yang sedang dibuat tidak terlalu meleceng. Seorang Game Designer biasanya menggunakan tools untuk mendesain gamenya. Mulai dari GIMP untuk mendesain tampilan visual game, Tile Map Editor (dijelaskan di akhir buku) untuk mendesain level game, bahkan notepad untuk menulis catatan. Untuk mengetahui lebih http://bitly.com/LuPt9w.
jauh
tentang game designer
Anda
bisa
buka
9
3.1.2 GAME ARTIST Game artist berperan membuat semua aspek visual dari game, mulai dari karakter, background, sampai menu dan antarmuka/UI. Game artist bertanggung jawab agar game terlihat menarik. Seorang Game Artist biasanya menggunakan GIMP untuk membuat asset spritesheet, kadang menggunakan tool tersendiri untuk membuat sprite.
3.1.3 GAME PROGRAMMER Game programmer berperan menggabungkan game design dan art asset yang ada untuk menjadi sebuah video game yang dapat dimainkan. Seorang Game Programmer harus memiliki dasar logika yang kuat, karena game biasanya memasukkan unsur – unsur matematika dan fisika dalam pembuatannya. Game programmer juga bertanggung jawab agar gamenya tidak terlalu memakan terlalu banyak (khususnya pada handphone yang memorynya terbatas).
3.2
PROSES PEMBUATAN GAME
Pembuatan sebuah game melibatkan beberapa langkah yang perlu dilakukan, disini akan diberikan gambaran umum proses pembuatan game pada umumnya.
3.2.1 PRE-PRODUCTION Pada tahap ini kita menentukan game apa yang kita buat, seperti apa tampilannya, dan apa saja yang kita perlukan untuk mulai membuat game tersebut. Termasuk membuat prototype dari game kita dan negosiasi jika game ini untuk perusahaan lain. Disinilah seorang game designer banyak bekerja, mulai dari memikirkan ide, konsep art, gameplay, dan sebagainya.
3.2.2 PRODUCTION
10
Pada tahap ini kita terjun langsung mengerjakan game yang kita inginkan, dari sisi art, programming, dan level desain. Game artis dan programmer banyak bekerja. Dalam pembuatan game, programmer disarankan untuk memperlihatkan progress programmingnya, termasuk memasukkan asset ke dalam game walaupun hanya menggerakkan sprite. Hal itu perlu dilakukan karena biasanya pengembangan game yang cukup lama dapat menyebabkan motivasi dari artist dan designer menurun. Sehingga perlu dilakukan testing sehingga artist dan designer dapat melihat progress dan melakukan revisi jika terdapat kekurangan.
3.2.3 RELEASE Setelah game kita selesai pastinya kita perlu merilis game kita agar dapat dimainkan oleh orang banyak. Kita harus melakukan promosi agar game kita dikenal oleh banyak orang. Kita dapat melakukan publikasi di media ternama atau share melalui jejaring sosial ternama. Jangan lupa untuk melakukan maintenance ketika ada yang report bug. Dan jika perlu, kita dapat melakukan update agar game kita tetap dimainkan oleh orang.
11
4 4.1
PRE-PRODUCTION GAME DESIGN
4.1.1 CORE GAMEPLAY Game yang akan kita buat akan menggunakan tile map dan pixel art. Disini kita akan membuat game dimana player akan berusaha membantu John, seorang karyawan, untuk kabur dari kantornya. John harus menuju ke pintu keluar agar dapat menyelesaikan levelnya.
4.1.2 FITUR DALAM GAME DAN GAME LOGIC Berikut contoh konsep game yang akan kita buat, -
12
Map terbentuk dari array 2D. Terdapat bermacam – macam obstacle, mulai dari pintu, tembok, kunci, dan switch. Game ini memiliki tujuan mengantarkan hero kita ke tempat tujuan, yaitu pintu finishnya. Hero akan berjalan per kotak array. Ketika hero kita menemukan obstacle pintu dan tidak membawa kunci, maka hero akan diam. Lain halnya ketika hero membawa kunci, pintu akan terbuka. Lalu hero juga akan menemui switch, dimana ketika hero berada 1 kotak dengan switch, maka sesuatu akan terjadi, seperti pintu akan terbuka atau muncul kunci lainnya
Untuk itu, inilah urutan dari pembuatan game kita nanti: a. b. c. d. e. f.
4.2
Membuat layer map, berupa array2D, berfungsi untuk menyimpan data map. Membuat layer obstacle, berupa array 2D, berfungsi untuk menyimpan data obstacle, seperti kunci, switch, pintu, dan lain – lain. Membuat layer hero, berupa posisi X dan Y yang akan mempresentasikan posisi dari array map. Membuat input handler, agar ketika kita menekan tombol atas bawah kiri kanan akan menggerakkan hero kita. Cek collision by data, periksa apakah hero menabrak dinding, kunci, switch, atau pintu. Play SFX tertentu jika hero mengambil kunci atau menekan switch.
VISUAL DESIGN
4.2.1 MOCKUP Mock-up adalah gambaran kasar/sketsa atau bahkan bisa berupa gambar final yang menggambarkan game seperti apa yang kita inginkan. Dengan menggunakan mockup kita bisa menentukan beberapa hal seperti :
13
1. 2. 3.
Perbandingan antara karakter dengan layar atau karakter dengan environment ( Lihat Gambar 1 ) Menentukan sudut pandang game (Lihat Gambar 2) Menentukan layout User Interface (tombol-tombol atau menu)
New Zealand Story © Taito
Bubble Bobble © Taito
Gambar 1 Perbandingan Karakter dengan Layar/Environment
Turn-based tactics game Kenneth Fejer (Isometric View)
Bomberman © Hudson soft (3/4 View)
Gambar 2 Penentuan Sudut Pandang Game 14
4.2.2 ASSET LIST Asset List adalah daftar objek-objek grafis yang diperlukan untuk membuat game tersebut. Yang diperlukan dalam membuat asset list antara lain adalah : 1. 2. 3. 4. 5.
Nama asset Prioritas pengerjaan asset Format asset (PNG,SWF,JPEG,dll) Ukuran asset Jenis animasi dan jumlah frame tiap animasi (Apabila asset tersebut beranimasi) Berikut contoh sebagian asset list dari game yang akan dibuat : Prioritas
Nama
Animasi
Format
Size
Frame
Ket.
1
Hero_Front
-
PNG
16x16
1
Hero Hadap depan Hero Hadap belakang Hero Hadap Kiri
2
Hero_Back
-
PNG
16x16
1
3
Hero_Left
-
PNG
16x16
1
4
Hero_Right
-
PNG
16x16
1
5
Key
-
PNG
16x16
1
Hero Hadap Kanan -
6
Door_Open
-
PNG
16x16
1
Pintu terbuka
7
Door_Closed
-
PNG
16x16
1
Pintu tertutup
8
Switch_On
-
PNG
16x16
1
9
Switch_Off
-
PNG
16x16
1
On = Pada saat ditekan Off = Idle
10
Etc
…
…
…
…
…
15
2D ARTIST RECRUITMENT Some of a 2D artist's tasks in a game development process are:
Creating concept arts based on the ideas and feel from the game design. Creating 2D Animation for character Making Visual effects to polish the game feel and look 2D Artist Requirements
Creating the Graphic al User Interface (GUI) Making illustrations Making textures for 3D models
Proficiency in digital drawing Good teamwork, good programs: raster and/or vector. communicating skills, self-motivated. Able to create good quality 2D Passion for creating and playing animation. video games. A good skill and sense in creating Passion to push your skills to the next concept sketches. level. Knowledge and good sense in art Variations in art styles. basics. Do you think you are ready to take this position? If you are, apply to
[email protected] with subject Recruitment 2D Artist, be sure to send us your portfolio and CV. More info: http://agatestudio.com/index.php?page=career 16
5
PRODUCTION
5.1
GAME ART PRODUCTION
5.1.1 PENGENALAN TOOLS UNTUK MEMBUAT ASET GAME Software yang digunakan untuk membuat asset game pada mobile game adalah GIMP 2. Ada beberapa hal yang perlu kita lakukan untuk membuat aset game menggunakan GIMP 2, yaitu : 1.
2.
Bekerja di resolusi Kecil dengan background transparan. Untuk membuat halaman baru kita bisa mengakses File>New Image (Shortcut : Ctrl+N). kemudian atur resolusi halaman di bagian Image Size dengan warna background transparent. (Lihat gambar 4). Untuk memudahkan kita dalam pembuatan aset, maka kita perlu window tambahan yang menampilkan asset pada ukuran sebenarnya. Window tersebut bisa di akses di View > New View. Mengatur tools yang diperlukan untuk membuat aset. Tools yang akan sering digunakkan untuk membuat aset adalah sebagai berikut : a. Pencil tools ( Shortcut : N ) Digunakkan untuk menggambar aset game menggunakan pensil.bisa di akses dari toolbox. Karena gambar yang dibuat bersolusi kecil maka kita akan menggunakan pencil tools dengan scale pencil 0.10 (Lihat gambar 5 .) b.
c.
Eraser tools ( Shortcut : Shift+E) Digunakan untuk menghapus gambar. Atur penghapus menjadi “Hard Edge” (Lihat gambar 6). Eyedropper (Shortcut : O) Eyedropper digunakkan untuk mengambil warna berdasarkan warna yang dipilih pada gambar.
17
Gambar 4 Membuat Halaman Baru
18
Gambar 5 Pengaturan Pencil tools
Gambar 6 Pengaturan Eraser
5.1.2 PEMBUATAN SPRITE Sprites adalah graphic object / image yang dapat digerakkan terpisah dengan benda lain seperti background. Sprites yang digunakan dalam game ini mepresentasikan hero, obstacle, object kunci, object, object pintu. Selain itu, sprites dapat berupa animasi atau statis dan juga dapat digunakan untuk mewakili object game yang sudah ditentukan seperti harta karun, dan power up juga. Karena sprite dibuat terpisah terhadap layar background, maka background sprites tersebut harus transparan. Berikut akan dijelaskan membuat sprite Hero_front ( karakter menghadap kedepan) menggunakan GIMP : 1. Buka halaman baru File > New (Ctrl N) dan atur resolusi 16x16 pixel dengan background transparant.
19
2.
20
Untuk memudahkan pengerjaan kita bisa menampilkan grid pada menu View > Show Grid. Kemudian ke menu Image > Configure Grid untuk mengatur grid sehingga ukurannya pixel.
3.
Dengan menggunakan Pencil tool yang scalenya berukuran 0.1 buat base outline karakter.
4.
Beri warna dasar dengan menggunakan tools pencil yang memiliki warna yang berbeda. 21
5.
Tambahkan detail dengan menambahkan warna gelap pada tiap warna dasar.
6.
Agar sprite kita terlihat lebih halus maka Outline hitam bisa diganti menjadi warna gelap yang mendekati warna dasar sprite.
Dengan menggunakan cara diatas maka kita bisa membuat sprite-sprite lainnya.
22
5.1.3 TILING Dalam membuat background dapat menggunakkan berbagai cara, salah satunya adalah background tiles. Background tiles terdiri dari potongan-potongan gambar kecil (tile), dimana nantinya masing-masing tile tersebut akan disusun oleh oleh programmer menjadi background berukuran besar. Dengan menggunakan background tiles penggunaan memory dan disk space akan lebih sedikit daripada menggunakan background yang terdiri dari satu gambar utuh. Tileset adalah sekumpulan gambar yang terdiri dari beberapa tile yang akan digunakkan sebagai background game. Berikut contoh tileset yang akan kita gunakkan dalam game kita :
Berikut contoh pengaturan background tile.
23
24
5.1.4 MAIN MENU/ SCREEN FLOW Screen flow menunjukkan flow/aliran layar/menu-menu dalam game yang kita buat. Screen flow tersebut mencakup Main menu, level selections, in-game, result, dan about. Contoh screen flow dalam game yang akan kita buat :
Main Menu
About
Level Selection
In-Game
Berikut adalah contoh menu yang akan kita buat :
25
26
27
5.2
GAME PROGRAMMING
5.2.1 PENGENALAN TOOLS PROGRAMMING YANG DIGUNAKAN 5.2.1.1 PENGERTIAN J2ME Java2 Micro Edition atau yang biasa disebut J2ME adalah lingkungan pengembangan yang didesain untuk meletakkan perangkat lunak JAVA pada barang elektronik berserta perangkat pendukungnya. Pada J2ME, jika perangkat lunak berfungsi dengan baik pada sebuah perangkat maka belum tentu juga berfungsi baik pada perangkat yang lain. J2ME membawa Java ke dunia informasi, komunikasi, dan perangkat komputasi yang lebih kecil dibandingkan dengan computer desktop. J2ME biasa digunakan pada telepon selular, pager, PDA, dan sejenisnya. Teknologi J2ME juga memiliki beberapa keterbatasan jika diaplikasikan pada ponsel. J2ME sangat bergantung pada device yang digunakan, bisa dari merk ponsel, maupun kemampuan ponsel, dan dukungan terhadap teknologi J2ME. MIsalnya, jika sebuah ponsel tidak memiliki kamera maka jelas J2ME pada ponsel tersebut tidak dapat mengakses kamera.
5.2.1.2 CONNECTED LIMETED DEVICE CONFIGURATION (CLDC) CLDC atau Connected Limited Device Cofiguration adalah perangkat dasar dari J2ME, spesifikasi dasar yang berupa library atau API yang diimplementasikan pada J2ME, seperti yang digunakan pada telepon selular, pager, dan PDA. Perangkat tsb dibatasi dengan keterbatasan memory, sumber daya, dan kemampuan memproses. Spesifikasi CLDC pada J2ME adalah spesifikasi minimal pada package, class, dan sebagian fungsi Java Virtual Machine yang dikurangi agar dpat diimplementasikan dengan keterbatasan sumber daya pada alat tersebut, JVM yang digunakan adalah KVM (Kilobyte Virtual Machine).
5.2.1.3 MOBILE INFORMATION DEVICE PROFILE (MIDP) 28
MIDP atau Mobile Information Device Profile adalah spesifikasi untuk sebuah profil J2ME. MIDP memilik lapisan diatas CLDC, API tambahan untuk daur hidup aplikasi, antar muka, jaringan, dan pemyimpanan persisten.
5.2.1.4 MIDLET Suatu aplikasi Mobile Information Device Profile (MIDP) pada J2ME disebut MIDlet. mIdlet adalah bagian dari package javax.microedition.midlet. sebuah MIDlet harus diextend dengan class MIDlet. MIDlet terdiri dari beberapa method yang harus ada, yaitu contructor(), startApp(), pauseApp(), destroyApp(). Method yang pertama adalah startApp() dipanggil oleh Application Management Software (AMS) ketika MIDlet dijalankan atau diresume. Resume, dengan kata lain startApp() akan kembali dijalankan setelah di-pause. Maka berhati-hatilah ketika kita menempatkan inisialisasi di dalam startApp(). Method yang kedua adalah pauseApp(), yang akan di-invoke oleh AMS ketika dalam situasi tertentu, seperti pengguna menerima telepon ketika bermain game, peringatan low battery, dan lain-lain. Dan dapat digunakan untuk meminimasi CPU, memory, atau konsumsi baterenya, ketika game kita tidak dalam active state. Method yang terakhir adalah destroyApp(). Method ini dipanggil untuk destroy MIDlet oleh AMS. Dalam method ini harus clean-up resource, action, dan lain-lain. Dan memberitahukan AMS untuk menyempurnakan cleanup dengan notifDestroy() Ketiga method di atas merupakan bagian dari MIDP Lifecycle. Sangat penting untuk semua programmer J2ME memahaminya.
29
MIDP Lifecycle
5.2.1.5
GAME CANVAS
Class utama Game API dari MIDP adalah class GameCanvas. Class GameCanvas merupakan perluasan dari class Canvas yang kita gunakan dalam pembuatan low – level user interface. Dua kelemahan utama dari class Canvas dalam pemrograman game adalah tidak memadainya kemampuan untuk mengatur proses repaint dan ketidakmampuan untuk mengatur bagaimana pointer events serta quick keys diteruskan pada canvas. Komponen user interface dari MIDP umumnya berupa event driven. Events berupa antrian berurutan dan diteruskan terhadap aplikasi satu persatu, beserta tunda waktu antar waktu dimana event dibuat (key press). GameCanvas memungkinkan aplikasi mengumpulkan events yang terbuat dan melakukan proses repaint pada canvas dengan cepat. Struktur program menjadi lebih bersih karena terdapat rangkaian perulangan utama dimana proses painting dan pengumpulan events dilakukan. 30
GameCanvas menggunakan teknik double buffering. Seluruh proses pembuatan interface dilakukan di off-screen buffer, kemudian di transfer dari area buffer tersebut menuju area yang terlihat pada canvas. Aplikasi anda harus menggunakan instance method dari class Graphics berupa method getGraphics(). Setiap pemanggilan terhadap method ini mengembalikan sebuah instance baru dari offscreen buffer yang anda gunakan dalam proses pembuatan user interface. Untuk memperbaharui screen tersebut, anda harus memanggil flushGraphics() untuk melakukan proses repaint secara cepat dengan isi dari off-screen buffer. Perhatikan bahwa anda hanya perlu memanggil method getGraphics() sekali saja, karena sebuah buffer teralokasi setiap kali anda memanggil method ini.
31
5.2.2 GAME LOOP Game Loop merupakan jantung dari sebuah game. Biasanya Game Loop menangani user input, update game state, menangani AI, memainkan musik dan sound effect, dan menampilkan display game. bool game_is_running = true; while( game_is_running ) { update_game(); display_game(); }
Penanganan Game Loop khususnya pada game mobile, harus di atur sedemikian rupa dan seefisien mungkin, karena game mobile memiliki memory yang terbatas, input yang terbatas, ukuran display yang kecil, cpu yang lambat, dan lain-lain. Sehingga akan menjadi tantangan tersendiri bagi developer untuk mengembangkan game di J2ME. Gambar di samping ini adalah contoh dari Game Loop:
32
Untuk mengimplementasikan sebuah game loop, kita harus membuat game loop berjalan sendiri menggunakan thread. Sehingga game loop akan terus berjalan selama perulangannya bernilai true. Pertama – tama, buat kelas bernama GameScreen meng-inherit GameCanvas dan implements interface. public class GameScreen extends GameCanvas implements Runnable Lalu tambahkan inisialisasi proses Thread ke dalam konstruktor GameScreen. public GameScreen() { . . . Thread t = new Thread(this); t.start(); }
// create the game thread
Contoh game loop pada J2ME: public void run() { //Inisialisasi variable //yang hanya dipakai 1x while (gameIsRun) { //Berisi fungsi-fungsi //yang dipanggil agar game berjalan getInput(); update(); try { Thread.sleep(30); }catch (InterruptedException ie) { } flushGraphics(); } }
Pada contoh di atas, merupakan game loop yang paling simple. Belum memiliki state, menghandle pause game, dan lain-lain. Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code & Assets\chapt5.2.2.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me. 33
5.2.3 ARRAY MULTIDIMENSI Array 2D atau yang sering disebut juga dengan matrix, merupakan array 2 dimensi. Array 2D sering kali dipakai di dalam pengembangan game. Khususnya pada game yang akan kita buat sekarang, untuk membuat map, letak obstacle, dan posisi hero. Pada game ini, kita akan membuat array 2 dimensi yang akan kita gunakan untuk menggambar map dan obstacle. Karena kita akan memakai device dengan ukuran lebar 240 dan tinggi 320, dengan kata lain ukuran layar akan portrait. Asumsikan lebar dan tinggi tiap tile kita akan berukuran 16. Maka jumlah kolom tile kita adalah 240/16 = 15 kotak. Dan secara vertikal, 320/16 = 20 kotak, tapi kita coba sisakan 2 tile, sehingga untuk baris tile kita buat 18 kotak. Oke, pertama –tama kita buat 2 layer, yang pertama untuk menggambar map dan yang kedua untuk menggambar obstacle. public { 1, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 7, };
34
int[][] mapLVL1 = 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8, 8, 8, 8, 8, 8,
new int[][] 3, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 6, 0, 0, 0, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8, 8, 8, 8,
{ 0, 0, 0, 0, 0, 0, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
0, 0, 0, 0, 0, 0, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
0, 0, 0, 0, 0, 0, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
0}, 0}, 0}, 0}, 0}, 0}, 3}, 6}, 6}, 6}, 6}, 6}, 6}, 6}, 6}, 6}, 6}, 6}, 9}
Array diatas merupakan array 15 x 18. Nanti kita akan membuat 9 macam tile seperti ini,
Tile di atas akan dipresentasikan dalam angka, pada program ini tile tersebut akan bernilai 1
2
3
4
5
6
7
8
9
Setiap nilai dari array 2D di atas, akan di isi oleh asset kita, sesuai dengan nilainya. Pada chapter selanjutnya, kita akan membahas bagaimana menggambar semua itu ke canvas kita. public { 0, { 0, { 0, { 0, { 0, { 0, { 0, { 0, { 0, { 0, { 0, { 0, { 0, { 0, { 0,
int[][] obsLVL1 = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 4, 0, 0, 0, 0, 0, 2, 0, 0,
new int[][] 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 4, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 1,
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0},
35
{ { { { };
0, 0, 0, 0,
0, 0, 9, 0,
0, 0, 0, 0,
0, 0, 0, 0,
2, 2, 1, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0}, 0}, 0}, 0},
Sama seperti ketika menggambar map, disini kita akan menggambar obstacle yang ada. Berikut adalah asset yang akan digunakan:
1
2
3
4
5
6
7
8
9
Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code & Assets\chapt5.2.3.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me.
5.2.4 MEMASUKKAN GAMBAR DALAM GAME Image dibutuhkan untuk membuat game kita ini lebih menarik. Pada game ini, kita membutuhkan image terrain, hero, pintu, kunci, dan switch. Kita sudah memiliki array 2D untuk menggambar map dan obstacle. Sekarang mari kita coba gambar array yang sudah kita buat tadi, menjadi map di canvas. Pertama – tama mari kita inisialisasi dahulu variable image, di scope global. Image tile1, tile2, tile3, tile4,
36
tile5, tile6, tile7, tile8, tile9; Image c_front, c_back, c_right, c_left; Image obs_down, obs_up, obs_mid, obs_single; Image door_close, door_open; Image key; Image switch_on, switch_off; public void init() { //Initialize image try { //game tile1 = Image.createImage("/tile1.png"); tile2 = Image.createImage("/tile2.png"); tile3 = Image.createImage("/tile3.png"); tile4 = Image.createImage("/tile4.png"); tile5 = Image.createImage("/tile5.png"); tile6 = Image.createImage("/tile6.png"); tile7 = Image.createImage("/tile7.png"); tile8 = Image.createImage("/tile8.png"); tile9 = Image.createImage("/tile9.png"); obs_down = Image.createImage("/obs_down.png"); obs_mid = Image.createImage("/obs_mid.png"); obs_single = Image.createImage("/obs_single.png"); obs_up = Image.createImage("/obs_up.png"); c_back = Image.createImage("/c_back.png"); c_front = Image.createImage("/c_front.png"); c_left = Image.createImage("/c_left.png"); c_right = Image.createImage("/c_right.png"); switch_off = Image.createImage("/switch_off.png"); switch_on = Image.createImage("/switch_on.png"); key = Image.createImage("/key.png"); door_close = Image.createImage("/door_close.png"); door_open = Image.createImage("/door_open.png"); } catch (IOException e) { e.printStackTrace(); } }
Lalu pada kode di atas, kita akan menginisialisasi variable sebelumnya. Dengan cara Image.createImage(“/obs_down.png”), yang berada didalam tanda kurung merupakan lokasi dan nama file gambar yang akan diload. Semua file gambar harus diletakkan pada folder res di project eclipse kita. Seperti pada gambar berikut:
37
public void drawMapLv1() { for (int i = 0; i < mapLVL1.length; i++) { for (int j = 0; j < mapLVL1[0].length; j++) { switch (mapLVL1[i][j]) { case 1: g.drawImage(tile1, j*16, i*16, 0); break;
38
case 2: g.drawImage(tile2, break; case 3: g.drawImage(tile3, break; case 4: g.drawImage(tile4, break; case 5: g.drawImage(tile5, break; case 6: g.drawImage(tile6, break; case 7: g.drawImage(tile7, break; case 8: g.drawImage(tile8, break; case 9: g.drawImage(tile9, break; }
j*16, i*16, 0); j*16, i*16, 0);
j*16, i*16, 0); j*16, i*16, 0); j*16, i*16, 0); j*16, i*16, 0); j*16, i*16, 0); j*16, i*16, 0);
} } }
Fungsi diatas akan memeriksa nilai setiap array, dan menggambarnya kedalam canvas. Fungsi drawImage untuk menggambar gambar, dengan parameter pertama Image, posisi X, posisi Y, dan terakhir reference point dari gambar tersebut. 0 berarti titik reference point berada pada posisi kiri atas. j*16 maksudnya untuk menggambar tile tersebut pada posisi mana. Misalkan pada iterasi pertama, berarti j masih bernilai 0, sehingga hasilnya pun akan (0, 0). Iterasi kedua j = 1, maka akan (0, 16), dst. public void run() { System.out.println("getWidth: " + getWidth()); System.out.println("getHeight: " + getHeight()); init(); while (true) { g.setColor(115, 130, 147); g.fillRect(0, 0, getWidth(), getHeight()); drawMapLv1(); flushGraphics();
39
try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } }
Lalu kita panggil fungsi drawMapLvl1() pada game loop. Kemudian kita akan mencoba menjalankan pada emulatornya.
Gambar di atas adalah hasil susunan array 2D kita sebelumnya. public void drawObsLv1() { for (int i = 0; i < obsLVL1.length; i++) { for (int j = 0; j < obsLVL1[0].length; j++) { switch (obsLVL1[i][j]) { case 1: //obs down g.drawImage(obs_down, j*16, i*16, 0); break; case 2: //obs mid g.drawImage(obs_mid, j*16, i*16, 0); break; case 3: //obs single g.drawImage(obs_single, j*16, i*16, 0); break; case 4: //obs up
40
g.drawImage(obs_up, j*16, i*16, 0); break; case 5: //switch off g.drawImage(switch_off, j*16, i*16, 0); break; case 6: //switch on g.drawImage(switch_on, j*16, i*16, 0); break; case 7: //door close g.drawImage(door_close, j*16, i*16, 0); break; case 8: //door open g.drawImage(door_open, j*16, i*16, 0); break; case 9: //kunci g.drawImage(key, j*16, i*16, 0); break; } } } }
Lalu kita lakukan hal yang sama pada obstacle kita, lalu panggil kembali di game loop. Lalu run kembali ke emulator.
41
Gambar di atas merupakan hasil dari penambahan obstacle. Lalu sekarang kita akan menambahkan hero kita. Kode dibawah untuk variable yang akan digunakan. posX dan posY merupakan posisi dari hero kita. 2, 2 artinya hero kita berada di baris ke 2 dan kolom ke 2. Variable arahChar untuk menentukan kemana arah dari hero kita. Kita inisialisasikan dengan 4, yang artinya hero kita akan menghadap ke bawah. //state char int posX = 2, posY = 2; public int arahChar = 4; public static final int KIRI = 1; public static final int KANAN = 2; public static final int ATAS = 3; public static final int BAWAH = 4;
Lalu kode berikutnya untuk menggambar hero pada canvas. Caranya hampir sama ketika kita menggambar map dan obstacle. Bedanya kita bukan mengecek nilai array, 42
tapi mengecek posX dan posY, jika nilainya sama, maka kita buka statement SWITCH – CASE yang berguna untuk mengecek hadapan dari hero kita. public void drawChar() { for (int x = 0; x < mapLVL1.length; x++) { for (int y = 0; y < mapLVL1[0].length; y++) { if (y == posX && x == posY) { switch (arahChar) { case ATAS: g.drawImage(c_back, posX*16, posY*16, 0); break; case BAWAH: g.drawImage(c_front, posX*16, posY*16, 0); break; case KANAN: g.drawImage(c_right, posX*16, posY*16, 0); break; case KIRI: g.drawImage(c_left, posX*16, posY*16, 0); break; } } } } }
Jangan lupa kita panggil fungsi di atas pada game loop kita. g.setColor(115, 130, 147); g.fillRect(0, 0, getWidth(), getHeight()); drawMapLv1(); drawObsLv1(); drawChar(); flushGraphics();
Lalu jalankan kembali emulator kita.
43
Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code & Assets\chapt5.2.4.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me.
5.2.5 MENGHANDLE INPUT PEMAIN Input handler merupakan fungsi yang akan menangani ketika player menekan suatu tombol di device kita. Pada game ini kita akan buat 2 macam input, yang pertama untuk menangani input pada layar menu dan pada permainan. Karena ketika kita menekan tombol atas pada layar menu akan berbeda hasilnya ketika kita menekan di dalam game. Pada layar menu, tombol atas akan menggerakkan kursor ke atas, sedangkan pada game akan menggerakkan hero 1 kotak ke atas. Pada contoh game kali ini, game input hanya digunakan untuk menggerakkan hero dan pada menu utama. 44
Pertama – tama kita tentukan terlebih dahulu, variable apa yang akan digunakan. buttonHold berguna untuk menahan gerakan ketika kita menekan dan menahan suatu tombol. x dan y digunakan untuk mengubah posX dan posY. //game input boolean buttonHold = false; int x = 0, y = 0;
Lalu kita buat fungsi movePlayer(), pada awal fungsi kita buat agar x dan y bernilai 0, sehingga x dan y hanya berubah nilainya ketika kita menekan. Jika kita tidak merubah nilai x dan y, maka hero akan terus bergerak meskipun kita sudah melepas tombolnya. getKeyState() merupakan fungsi yang mengembalikan nilai int, yang merupakan penanda bahwa suatu tombol sedang ditekan. Lalu ada pengecekan, apakah keyState dan GameCanvas.LEFT_PRESSED tidak 0. Jika true, maka lakukan kode berikutnya. Kode selanjutnya ada pengecekan kembali, yaitu pengecekan terhadap buttonHold. Disini kita akan mengecek apakah player masih menekan tombol atau tidak. Jika YA, maka skip kode di dalamnya. Jika TIDAK, maka x kita rubah nilainya menjadi -1 . Lalu kita rubah nilai buttonHold menjadi true sehingga kode di dalam IF ini tidak akan dilakukan lagi sampai player melepas tombol. Dan juga kita rubah arahChar menjadi ke KIRI. Dan begitu selanjutnya, sampai pada bagian akhir dari IF – THEN – ELSE terdapat fungsi untuk merubah kembali buttonHold menjadi false, sehingga ketika player melepas tombol maka buttonHold pun berubah kembali menjadi false. Lalu kita rubah kembali arahChar menjadi ke bawah, ini bersifat opsional jika kita ingin posisi char tetap pada posisi hadapnya, maka teman – teman dapat menghapus kode arahChar = BAWAH;. public void movePlayer() { int keyState = getKeyStates(); x = 0; y = 0; if ((keyState & GameCanvas.LEFT_PRESSED) != 0) { if(!buttonHold) { x = -1; buttonHold = true; arahChar = KIRI; }
45
}else if ((keyState & GameCanvas.RIGHT_PRESSED) != 0) { if(!buttonHold) { x = 1; buttonHold = true; arahChar = KANAN; } }else if ((keyState & GameCanvas.UP_PRESSED) != 0) { if(!buttonHold) { y = -1; buttonHold = true; arahChar = ATAS; } }else if ((keyState & GameCanvas.DOWN_PRESSED) != 0) { if(!buttonHold) { y = 1; buttonHold = true; arahChar = BAWAH; } }else{ if(buttonHold) buttonHold = false; //arahChar = BAWAH; } posX += x; posY += y; }
Jangan lupa kita panggil fungsinya di game loop. Lalu coba jalankan ke emulator, dan coba gerakkan dengan tombolnya. g.setColor(115, 130, 147); g.fillRect(0, 0, getWidth(), getHeight()); drawMapLv1(); drawObsLv1(); drawChar(); movePlayer(); flushGraphics();
46
Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code & Assets\chapt5.2.5.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me.
5.2.6 INTERAKSI ANTAR OBJE CT Pada bab ini, akan menjabarkan tentang bagaimana interaksi hero dengan object yang ada di map. Kode berikut ini hanyalah mengubah dari fungsi movePlayer(), misalnya pada bagian x = -1, kita rubah menjadi kode dibawah tanpa mengubah susunan kode sebelum dan sesudahnya. Berikut list interaksi hero dengan objek: a.
Pertama – tama, kita buat interaksi hero dengan wall, dengan kata lain layer pertama. LEFT_PRESSED
if ((keyState & GameCanvas.LEFT_PRESSED) != 0) { if(!buttonHold)
47
{ if (mapLVL1[posY][posX-1] == 5) { x = -1; } buttonHold = true; arahChar = KIRI; step++; } }
RIGHT_PRESSED Sama dengan LEFT_PRESSED susunannya. Begitu pula pada DOWN dan UP_PRESSED else if ((keyState & GameCanvas.RIGHT_PRESSED) != 0) { if(!buttonHold) { if (mapLVL1[posY][posX+1] == 5) { x = 1; } buttonHold = true; arahChar = KANAN; step++; } }
UP_PRESSED else if ((keyState & GameCanvas.UP_PRESSED) != 0) { if(!buttonHold) { if (mapLVL1[posY-1][posX] == 5) { y = -1; } buttonHold = true; arahChar = ATAS; step++; } }
48
DOWN_PRESSED else if ((keyState & GameCanvas.DOWN_PRESSED) != 0) { if(!buttonHold) { if (mapLVL1[posY+1][posX] == 5) { y = 1; } buttonHold = true; arahChar = BAWAH; step++; } }
Kode di atas maksudnya, kita akan mengecek terlebih dahulu, apakah tile disampingnya bernilai 5, jika YA maka hero akan bergerak ke arah tersebut. Gambar dibawah merupakan tampilan dari emulator setelah kita rubah.
49
b.
Karena pada pembuatan obstacle, kita berbeda layer dengan map, maka kita juga harus membuat interaksi hero dengan obstacle, khususnya obstacle meja. Pada pengecekan ini, kita coba dengan cara yang berbeda, yaitu menggunakan fungsi untuk mengecek dengan parameter masukan posisi dari hero.
public boolean cekObs(int x, int y) { if (obsLVL1[x][y] == 1 || obsLVL1[x][y] == 2 || obsLVL1[x][y] == 3 || obsLVL1[x][y] == 4) { return true; }else
50
return false; }
Fungsi di atas akan mengecek apakan lokasi dari hero (x, y) berisi obstacle (1, 2, 3, 4) atau tidak. Jika ya, kembalikan nilai true, dan sebaliknya. Untuk cara pengecekannya, kita letakkan didalam IF sebelumnya. Sehingga pada game input, pengecekannya menjadi seperti ini. Oh iya, pengecekan dibawah kita update dari pengecekan sebelumnya. LEFT_PRESSED if ((keyState & GameCanvas.LEFT_PRESSED) != 0) { if(!buttonHold) { if (mapLVL1[posY][posX-1] == 5 && !cekObs(posY, posX-1)) { x = -1; } buttonHold = true; arahChar = KIRI; step++; } }
RIGHT_PRESSED Sama dengan LEFT_PRESSED susunannya. Begitu pula pada DOWN dan UP_PRESSED else if ((keyState & GameCanvas.RIGHT_PRESSED) != 0) { if(!buttonHold) { if (mapLVL1[posY][posX+1] == 5 && !cekObs(posY, posX+1)) { x = 1; } buttonHold = true; arahChar = KANAN; step++; } }
UP_PRESSED
51
else if ((keyState & GameCanvas.UP_PRESSED) != 0) { if(!buttonHold) { if (mapLVL1[posY-1][posX] == 5 && !cekObs(posY-1, posX)) { y = -1; } buttonHold = true; arahChar = ATAS; step++; } }
DOWN_PRESSED else if ((keyState & GameCanvas.DOWN_PRESSED) != 0) { if(!buttonHold) { if (mapLVL1[posY+1][posX] == 5 && !cekObs(posY+1, posX)) { y = 1; } buttonHold = true; arahChar = BAWAH; step++; } }
Lalu kita coba run pada emulator.
52
c.
Lalu kita buat interaksi hero dengan tuas. Ketika kita berada di atas tuas, maka akan terjadi sesuatu. Misalkan ada meja yang hilang, atau muncul kunci, dan lain – lain. Pada stage pertama ini, kita akan membuat 1 tuas, ketika hero kita berada pada tile yang sama dengan tuas tersebut, maka akan ada sebuah meja yang hilang. Tentu saja ketika kita menonaktifkan tuas tersebut, maka meja pun akan kembali muncul. Pertama – tama kita tentukan dahulu variable yang akan digunakan. stateTuas digunakan untuk mendefinisikan keadaan dari tuas, apakah sedang terbuka atau tertutup. Ini berguna untuk mengubah gambar tuas ketika sedang terbuka atau tertutup.
53
int stateTuas = 0;
Lalu kita buat fungsi openObstacle(), untuk melakukan pengecekan terhadap posisi hero kita. Karena posX dan posY merupakan posisi dari hero kita (yang merupakan posisi dalam array juga), maka dapat kita gunakan untuk mengecek nilai dari array obstacle. Lalu ada SWITCH – CASE, untuk memeriksa apakah tuas berada dalam posisi ON atau OFF. Jika bernilai 0, maka obstacle pada baris ke 13 kolom ke 1 kita rubah menjadi 0, yang artinya meja pun hilang dan stateTuas pun berubah nilainya menjadi 1. Jika kita menginjaknya lagi, maka meja pun muncul lagi dan stateTuas berubah menjadi 0 kembali. public void openObstacle(int x, int y) { //fungsi ngebuka obstacle if (obsLVL1[posY][posX] == 5) { switch (stateTuas) { case 0: obsLVL1[x][y] = 0; stateTuas = 1; break; case 1: obsLVL1[x][y] = 3; stateTuas = 0; break; } } }
54
55
d.
Setelah itu kita buat interaksi hero dengan kunci. Ketika kita berada di atas objek kunci, maka kunci akan pindah ke inventory hero. Pertama – tama kita buat variable invent, yang berguna untuk menyimpan jumlah kunci. Disini kita simpan dalam bentuk int, sehingga ketika hero mengambil kunci, maka invent hanya kita tambah 1 saja.
int invent = 0;
Lalu kita tambahkan kode berikut pada fungsi openObstacle(). Caranya sama dengan pengecekan tile wall dan obstacle, kita cek apakah posisi berdiri hero ini bernilai 9 (obstacle kunci). Jika YA, maka invent kita tambah nilainya, lalu object kunci kita hilangkan dari canvas dengan cara mengubah nilai array menjadi 0. public void openObstacle(int x, int y) { //fungsi ngebuka obstacle
56
if (obsLVL1[posY][posX] == 5) { switch (stateTuas) { case 0: obsLVL1[x][y] = 0; stateTuas = 1; break; case 1: obsLVL1[x][y] = 3; stateTuas = 0; break; } } //fungsi ngambil kunci else if (obsLVL1[posY][posX] == 9) { invent++; obsLVL1[posY][posX] = 0; } }
Setelah itu kita jalankan kembali emulatornya.
57
e.
Dan yang terakhir, kita buat interaksi hero dengan pintu. Dimana pintu ini adalah tempat tujuan dari hero kita. Ketika hero kita tidak membawa kunci, maka kita tidak bisa melalui pintu tersebut. Ketika kita membawa kunci, maka hero kita pun akan memenangkan stage itu. Kita buat fungsi cekPintu(), untuk mengecek apakah disekeliling hero terdapat pintu atau tidak. Jika YA, maka periksa lagi, apakah invent tidak bernilai 0, jika YA baru kita rubah obstaclenya menjadi 8, yaitu gambar pintu terbuka. Dan jangan lupa, kita rubah juga map kita menjadi 5, agar hero kita bisa masuk ke dalam pintu (cek fungsi pada movePlayer()). Lalu kita kurangi jumlah invent. Parameter masukan x dan y berguna untuk menentukan posisi pintu yang akan dibuka (berdasarkan array).
public void cekPintu(int x, int y) { if (obsLVL1[posY-1][posX] == 7 || obsLVL1[posY+1][posX] == 7 || obsLVL1[posY][posX-1] == 7 || obsLVL1[posY][posX+1] == 7) { if (invent != 0) { obsLVL1[x][y] = 8; mapLVL1[x][y] = 5; invent--; } } }
Susunan terakhir dari game loop kita,
58
public void run() { System.out.println("getWidth: " + getWidth()); System.out.println("getHeight: " + getHeight()); init(); while (true) { g.setColor(115, 130, 147); g.fillRect(0, 0, getWidth(), getHeight()); drawMapLv1(); drawObsLv1(); drawChar(); movePlayer(); openObstacle(13,1); cekPintu(6,12); flushGraphics(); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } }
59
Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code & Assets\chapt5.2.6.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me.
5.2.7 STATE MANAGEMENT State management pada sebuah game sering dipakai dalam membuat alur screen flow dari sebuah game. Misalkan pada game ada splash screen, menu utama, level selection, level 1, level 2, highscore, dan lain – lain. Agar susunan kode program lebih rapih dan lebih mudah melakukan debugging jika terjadi error. Pada buku ini, kita akan membuat main menu yang memiliki 3 pillihan, yaitu Play, About, dan Exit. Play akan membuka ke Level Selection setelah itu baru kita dapat bermain. Sedangkan About akan menuju ke menu about, dan terakhir Exit, tentu saja untuk keluar dari game. Pada tutorial kali ini, kita akan membuat 5 state: a. Menu Utama Pertama – tama kita buat dulu variable lokal yang dibutuhkan. Variable pilih digunakan sebagai key pada SWITCH – CASE statement. Lalu kita buat variable int statis, untuk menyimpan nilai pilihan. Lalu kita buat variable curMenu, untuk membuat posisi pointer kita nanti (cursor menu). Jangan lupa kita buat variable untuk memuat image yang dibutuhkan. //state management int pilih = 0; protected final static int LVL_SEL = 1; protected final static int ABOUT = 2; protected final static int EXIT = 3; protected final static int LVL1 = 11; protected final static int LVL2 = 12; protected final static int LVL3 = 13; protected final static int MENU = 0; protected final static int GAMEOVER = 99; int curMenu = 1; Image b_about_off, b_about_on, b_exit_off, b_exit_on, b_play_off, b_play_on, bg_about, bg_mainmenu, hand_pointer;
60
Sekarang kita inisialisasi image, letakkan kode berikut pada fungsi init() //main menu b_about_off = Image.createImage("/mainMenu/b_about_off.png"); b_about_on = Image.createImage("/mainMenu/b_about_on.png"); b_exit_off = Image.createImage("/mainMenu/b_exit_off.png"); b_exit_on = Image.createImage("/mainMenu/b_exit_on.png"); b_play_off = Image.createImage("/mainMenu/b_play_off.png"); b_play_on = Image.createImage("/mainMenu/b_play_on.png"); bg_about = Image.createImage("/mainMenu/bg_about.png"); bg_mainmenu = Image.createImage("/mainMenu/bg_mainmenu.png"); hand_pointer = Image.createImage("/mainMenu/hand_pointer.png");
Setelah memuat inisialisasi gambar, mari kita buat fungsi mainMenu(), ini akan kita panggil sebagai menu utama pada game ini. Pertama – tama, kita drawImage terlebih dahulu bg_mainmenu. Lalu kita buat SWITCH – CASE statement dengan curMenu sebagai kuncinya. Ini berguna ketika kita menekan atas atau bawah, maka nilai curMenu akan ikut berubah. Sehingga CASE – nya pun akan selalu berubah. Misalkan pada case 1, maka button Play yang sedang aktif, dan hand_pointer pun berada pada posisi Play. Lalu kita buat fungsi input agar ketika kita menekan atas atau bawah, akan merubah nilai dari curMenu. Dan jika tombol FIRE ditekan, maka variable pilih pun akan berubah. Variable pilih digunakan untuk menentukan menu mana yang akan dijalankan, apakah menu utama, about, level selection, atau yang lain – lain. public void mainMenu() { g.drawImage(bg_mainmenu, 0, 0, 0); switch (curMenu) { case 1: g.drawImage(b_play_on, getWidth()/2, 180, Graphics.HCENTER | Graphics.VCENTER); g.drawImage(b_about_off, getWidth()/2, 220, Graphics.HCENTER | Graphics.VCENTER); g.drawImage(b_exit_off, getWidth()/2, 260, Graphics.HCENTER | Graphics.VCENTER); g.drawImage(hand_pointer, getWidth()/5+5, 180, Graphics.HCENTER | Graphics.VCENTER); break; case 2: g.drawImage(b_play_off, getWidth()/2, 180, Graphics.HCENTER | Graphics.VCENTER); g.drawImage(b_about_on, getWidth()/2, 220, Graphics.HCENTER | Graphics.VCENTER);
61
g.drawImage(b_exit_off, getWidth()/2, 260, Graphics.HCENTER | Graphics.VCENTER); g.drawImage(hand_pointer, getWidth()/5+5, 220, Graphics.HCENTER | Graphics.VCENTER); break; case 3: g.drawImage(b_play_off, getWidth()/2, 180, Graphics.HCENTER | Graphics.VCENTER); g.drawImage(b_about_off, getWidth()/2, 220, Graphics.HCENTER | Graphics.VCENTER); g.drawImage(b_exit_on, getWidth()/2, 260, Graphics.HCENTER | Graphics.VCENTER); g.drawImage(hand_pointer, getWidth()/5+5, 260, Graphics.HCENTER | Graphics.VCENTER); break; default: break; } int keyState = getKeyStates(); if ((keyState & UP_PRESSED) != 0) { if (curMenu <= 1) { curMenu = 3; }else curMenu--; }else if ((keyState & DOWN_PRESSED) != 0) { if (curMenu >= 3) { curMenu = 1; }else curMenu++; } if ((keyState & FIRE_PRESSED) != 0) { pilih = curMenu; } }
Lakukan sedikit perubahan pada game loop kita (yang terdapat di fungsi run()). Pada awalnya, kita langsung menulis kode pada level 1 di game loop. Sekarang kita buat SWITCH – CASE statement sehingga kita bisa mengatur menu mana yang akan dijalankan. Variable pilih menjadi kunci pada pemilihan CASE. MENU memiliki nilai 0, maka dari itu, pilih kita inisialisasikan dengan nilai 0 terlebih dahulu. 62
public void run() { System.out.println("getWidth: " + getWidth()); System.out.println("getHeight: " + getHeight()); init(); while (true) { switch (pilih) { case MENU: mainMenu(); break; case LVL_SEL: menuLevel(); break; case ABOUT: menuAbout(); break; case EXIT: parent.notifyDestroyed(); break; case LVL1: g.setColor(115, 130, 147); g.fillRect(0, 0, getWidth(), getHeight()); initMap(); drawChar(); movePlayer(); openObstacle(13,1); cekPintu(6,12); if (obsLVL[posY][posX] == 8) { pilih = 99; if (openLevel < 12) { openLevel = 12; } } break; case GAMEOVER: menuResult(); break; } flushGraphics(); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } }
63
b.
Menu Level Selection Buat variable curLevel dan openLevel. curLevel untuk cursor ktia pada level selection. Karena pada sebelumnya LVL1 kita inisialisasikan dengan nilai 11, maka diusahakan ketika kita menekan tombol FIRE, maka variable pilih harus berubah menjadi 11. openLevel merupakan level yang dibuka. Pada awalnya, kita inisialisasikan 11, sehingga ketika pertama kali memainkan game ini, maka level 1 sudah pasti terbuka. Nantinya akan kita tambahkan fitur load, sehingga akan melihat level mana yang sudah terbuka atau belum.
int curMenu = 1; int curLevel = 11;
Lalu seperti biasa, buat variable untuk image.
64
Image bg_tileset, b_level1, b_level2, b_level3, b_locked, levelselection_mid, levelselection_down, levelselection_top;
Dan inisialisasikan image sebelum game loop. //level selection b_locked = Image.createImage("/mainMenu/b_locked.png"); bg_tileset = Image.createImage("/mainMenu/bg_tileset.png"); b_level1 = Image.createImage("/mainMenu/b_level1.png"); b_level2 = Image.createImage("/mainMenu/b_level2.png"); b_level3 = Image.createImage("/mainMenu/b_level3.png"); levelselection_mid = Image.createImage("/mainMenu/levelselection_mid.png"); levelselection_down = Image.createImage("/mainMenu/levelselection_down.png"); levelselection_top = Image.createImage("/mainMenu/levelselection_top.png");
Kemudian kita buat fungsi menuLevel(), kita drawImage bg_tileset sebagai lapisan paling belakang. Lalu levelselection_top, levelselection_mid, dan levelselection_down sesuaikan posisinya pada canvas. Baru setelah itu kita masukkan gambar level dan pointer dengan menggunakan SWITCH – CASE statement menggunakan openLevel sebagai kuncinya. Lalu kita buat SWITCH – CASE statement 1 lagi untuk menggambar pointer, dengan menggunakan curLevel sebagai kuncinya. Kita juga membuat input handle difungsi ini, yang menangani tombol atas, bawah dan fire. Tombol atas dan bawah secara fungsi sama seperti sebelumnya. Pada tombol FIRE, akan terjadi pengecekan, apakah curLevel lebih kecil atau sama dengan openLevel, jika YA berarti level tersebut sudah bisa dimainkan. public void menuLevel() { g.drawImage(bg_tileset, 0, 0, 0); g.drawImage(levelselection_top, 0, 0, 0); g.drawImage(levelselection_mid, getWidth()/2, getHeight()/3,
65
Graphics.HCENTER | Graphics.VCENTER); g.drawImage(levelselection_down, 0, getHeight(), Graphics.LEFT | Graphics.BOTTOM); switch (openLevel) { case 11: g.drawImage(b_level1, Graphics.HCENTER | g.drawImage(b_locked, Graphics.HCENTER | g.drawImage(b_locked, Graphics.HCENTER | break;
getWidth()/2, 150, Graphics.VCENTER); getWidth()/2, 200, Graphics.VCENTER); getWidth()/2, 250, Graphics.VCENTER);
case 12: g.drawImage(b_level1, Graphics.HCENTER | g.drawImage(b_level2, Graphics.HCENTER | g.drawImage(b_locked, Graphics.HCENTER | break;
getWidth()/2, 150, Graphics.VCENTER); getWidth()/2, 200, Graphics.VCENTER); getWidth()/2, 250, Graphics.VCENTER);
case 13: g.drawImage(b_level1, Graphics.HCENTER | g.drawImage(b_level2, Graphics.HCENTER | g.drawImage(b_level3, Graphics.HCENTER | break;
getWidth()/2, 150, Graphics.VCENTER); getWidth()/2, 200, Graphics.VCENTER); getWidth()/2, 250, Graphics.VCENTER);
} switch (curLevel) { case 11: g.drawImage(hand_pointer, getWidth()/3, 150, Graphics.LEFT | Graphics.VCENTER); break; case 12: g.drawImage(hand_pointer, getWidth()/3, 200, Graphics.LEFT | Graphics.VCENTER); break; case 13: g.drawImage(hand_pointer, getWidth()/3, 250, Graphics.LEFT | Graphics.VCENTER); break; }
66
int keyState = getKeyStates(); if ((keyState & UP_PRESSED) != 0) { if (curLevel <= 11) { curLevel = 13; }else curLevel--; }else if ((keyState & DOWN_PRESSED) != 0) { if (curLevel >= 13) { curLevel = 11; }else curLevel++; } if ((keyState & FIRE_PRESSED) != 0) { if(curLevel <= openLevel){ pilih = curLevel; level = curLevel - 10; } } }
67
Untuk mengubah nilai dari openLevel, kita tambahkan kode berikut pada case LVL1. case LVL1: g.setColor(115, 130, 147); g.fillRect(0, 0, getWidth(), getHeight()); initMap(); drawChar(); movePlayer(); openObstacle(13,1); cekPintu(6,12); if (obsLVL[posY][posX] == 8) { pilih = 99; if (openLevel < 12) { openLevel = 12; } score = (56*100)/step; } break;
Ketika posisi hero berada pada pintu yang terbuka, maka kita rubah nilai pilih menjadi 99 dan rubah nilai dari openLevel menjadi 12. Lakukan hal yang sama pada LVL2 dan LVL3. Menu About Menu about sangat simple, hanya drawImage lalu input handle ketika kita menekan tombol FIRE, yang akan merubah nilai pilih menjadi 0.
c.
public void menuAbout() { g.drawImage(bg_about, 0, 0, 0); int keyState = getKeyStates(); if ((keyState & FIRE_PRESSED) != 0) { pilih = 0; } }
68
d.
Menu Result Seperti biasa, tentukan variable yang dibutuhkan. curResult untuk cursor selama di menu result. Variable step dan score untuk pemberian penilaian tentang berapa step yang telah dilalui dan berapa persen kemenangan dari player. Lalu variable image beserta inisialisasinya.
int step = 0; int score = 0; int curResult = 1; Image bg_result, b_nextlevel_off, b_nextlevel_on, b_tryagain_off, b_tryagain_on;
Inisialisasi, //win condition bg_result = Image.createImage("/mainMenu/bg_result.png");
69
b_nextlevel_off = Image.createImage("/mainMenu/b_nextlevel_off.png"); b_nextlevel_on = Image.createImage("/mainMenu/b_nextlevel_on.png"); b_tryagain_off = Image.createImage("/mainMenu/b_tryagain_off.png"); b_tryagain_on = Image.createImage("/mainMenu/b_tryagain_on.png");
Dan seperti sebelumnya kita buat SWITCH – CASE statement dengan kunci curResult, untuk memberi tanda pointernya. Ketika menekan FIRE, menggunakan SWITCH – CASE statement lagi. public void menuResult() { g.drawImage(bg_result, 0, 0, 0); switch (curResult) { case 1: g.drawImage(b_nextlevel_on, getWidth()/2, getHeight()/2+45, Graphics.HCENTER | Graphics.VCENTER); g.drawImage(b_tryagain_off, getWidth()/2, getHeight()/2+75, Graphics.HCENTER | Graphics.VCENTER); g.drawImage(hand_pointer, getWidth()/5, getHeight()/2+45, Graphics.LEFT | Graphics.VCENTER); break; case 2: g.drawImage(b_nextlevel_off, getWidth()/2, getHeight()/2+45, Graphics.HCENTER | Graphics.VCENTER); g.drawImage(b_tryagain_on, getWidth()/2, getHeight()/2+75, Graphics.HCENTER | Graphics.VCENTER); g.drawImage(hand_pointer, getWidth()/5, getHeight()/2+75, Graphics.LEFT | Graphics.VCENTER); break; } g.setColor(0, 0, 0); g.drawString(" " + step, getWidth()/2, getHeight()/3 + 3, 0); g.drawString(" " + score + " %", getWidth()/2, getHeight()/3 + 23, 0); int keyState = getKeyStates();
70
if ((keyState & UP_PRESSED) != 0) { if (curResult <= 1) { curResult = 2; }else curResult -= 1; }else if ((keyState & DOWN_PRESSED) != 0) { if (curResult >= 2) { curResult = 1; }else curResult += 1; } if ((keyState & FIRE_PRESSED) != 0) { switch (curResult) { case 1: pilih = LVL_SEL; posX = 2; posY = 2; stateTuas = 0; invent = 0; step = 0; score = 0; arahChar = BAWAH; break; case 2: pilih = MENU; posX = 2; posY = 2; stateTuas = 0; invent = 0; step = 0; score = 0; arahChar = BAWAH; break; } } }
Berikut tampilan setelah kita run pada emulator.
71
e.
Menu Exit Buat variable parent, untuk mengakses MIDlet dari gamecanvas. Lalu pada konstruktor, buat parameter masukan MIDlet. Pada bagian bawahnya kita tambahkan this.parent = parent agar kita bisa mengakses fungsi – fungsi dari MIDlet dengan menggunakan parent.
public GameScreen(MIDlet parent) { super(false); t = new Thread(this); g = getGraphics(); setFullScreenMode(true); this.parent = parent; }
72
Tentu saja, pada game MIDlet kita juga ada yang sedikit dirubah, dengan menambahkan this sebagai parameter masukan. protected void startApp() throws MIDletStateChangeException { if (gScreen == null) { gScreen = new GameScreen(this); } gScreen.startGame(); Display.getDisplay(this).setCurrent(gScreen); }
Pada case EXIT kita tambahkan fungsi parent.notifyDestroyed() agar memaksa MIDlet menutup program kita. case EXIT: parent.notifyDestroyed(); break;
f.
Menu 1, 2, 3 Untuk mempermudah dalam membuat level dan menghemat resource memory, kita akan membuat fungsi initMap(). Dimana fungsi ini akan digunakan untuk memuat array dan menggambarnya. Pada tutorial ini aka dijelaskan bagaimana cara memuat level map dengan 1 variable, yaitu variable mapLVL dan obsLVL. Pada fungsi menuLevel(), pada bagian tombol FIRE ditekan, ada kode program level = curLevel – 10; kode itu berfungsi untuk memberikan nilai pada variable level yang nantinya akan digunakan sebagai kunci pada SWITCH – CASE statement dibawah. Pada bagian akhir CASE, level akan dirubah nilainya menjadi 0, sehingga pada perulangan selanjutnya, CASE akan menjalankan kode di DEFAULT.
public void initMap() { switch (level) { case 1: mapLVL = new int[][] { { 1, 2, 2, 2, 2, 2, 2, { 4, 5, 5, 5, 5, 5, 5, { 4, 5, 5, 5, 5, 5, 5, { 4, 5, 5, 5, 5, 5, 5,
3, 6, 6, 6,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0}, 0}, 0}, 0},
73
{ 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 7, };
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
obsLVL = new int[][] { { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 3, 3, 3, 0, 3, 3, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 3, 3, 3, 4, 0, 0, { 0, 0, 0, 0, 2, 0, 0, { 0, 0, 0, 0, 2, 0, 0, { 0, 0, 0, 0, 2, 0, 0, { 0, 9, 0, 0, 1, 0, 0, { 0, 0, 0, 0, 0, 0, 0, }; level = 0; break; case 2: mapLVL = new int[][] { { 1, 2, 2, 2, 2, 3, 0, { 4, 5, 5, 5, 5, 6, 0, { 4, 5, 5, 5, 5, 6, 0, { 4, 5, 5, 5, 5, 6, 0, { 4, 5, 5, 5, 5, 6, 0,
74
6, 6, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
0, 0, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
0, 0, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
0, 0, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
0, 0, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
0, 0, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
0, 0, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
0}, 0}, 3}, 6}, 6}, 6}, 6}, 6}, 6}, 6}, 6}, 6}, 6}, 6}, 9}
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0},
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0}, 0}, 0}, 0}, 0},
{ 7, { 0, { 0, { 0, { 0, { 1, { 4, { 4, { 4, { 4, { 4, { 4, { 4, { 7, };
8, 0, 0, 0, 0, 2, 5, 5, 5, 5, 5, 5, 5, 8,
8, 0, 0, 0, 0, 2, 5, 5, 5, 5, 5, 5, 5, 8,
8, 4, 4, 4, 4, 2, 5, 5, 5, 5, 5, 5, 5, 8,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
6, 6, 6, 6, 6, 6, 6, 6, 2, 5, 6, 6, 6, 6,
0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 8, 0, 0, 0,
obsLVL = new int[][] { { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 5, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 3, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 9, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, }; level = 0; break; case 3: mapLVL = new int[][] { { 1, 2, 2, 2, 2, 2, 2, { 4, 5, 5, 5, 5, 5, 5, { 4, 5, 5, 5, 5, 5, 5, { 4, 5, 5, 5, 5, 5, 5, { 4, 5, 5, 5, 5, 5, 5, { 4, 5, 5, 5, 5, 5, 5, { 4, 5, 5, 5, 5, 5, 5, { 4, 5, 5, 5, 5, 5, 5,
0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 8, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 8, 0, 0, 0,
0, 0, 0, 1, 4, 4, 4, 4, 2, 5, 8, 0, 0, 0,
0, 0, 0, 2, 5, 5, 5, 5, 5, 5, 8, 0, 0, 0,
0, 0, 0, 2, 5, 5, 5, 5, 5, 5, 8, 0, 0, 0,
0, 0, 0, 2, 5, 5, 5, 5, 5, 5, 8, 0, 0, 0,
0, 0, 0, 2, 5, 5, 5, 5, 5, 5, 8, 0, 0, 0,
0}, 0}, 0}, 3}, 6}, 6}, 6}, 6}, 6}, 6}, 9}, 0}, 0}, 0}
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0},
2, 5, 5, 5, 5, 5, 5, 5,
2, 5, 5, 5, 5, 5, 5, 5,
2, 5, 5, 5, 5, 5, 5, 5,
2, 5, 5, 5, 5, 5, 5, 5,
2, 5, 5, 5, 5, 5, 5, 5,
2, 5, 5, 5, 5, 5, 5, 5,
2, 5, 5, 5, 5, 5, 5, 5,
3}, 6}, 6}, 6}, 6}, 6}, 6}, 6},
75
{ 4, { 4, { 4, { 4, { 4, { 7, { 0, { 0, { 0, { 0, { 0, { 0, };
5, 5, 5, 5, 5, 8, 0, 0, 0, 0, 0, 0,
5, 5, 5, 5, 5, 8, 0, 0, 0, 0, 0, 0,
5, 5, 5, 5, 5, 8, 0, 0, 0, 0, 0, 0,
5, 5, 5, 5, 5, 8, 0, 0, 0, 0, 0, 0,
5, 5, 5, 5, 5, 8, 4, 4, 4, 4, 4, 7,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
obsLVL = new int[][] { { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 4, 0, { 0, 0, 0, 0, 0, 2, 0, { 0, 0, 0, 0, 0, 2, 0, { 0, 0, 0, 0, 0, 2, 0, { 0, 0, 0, 0, 0, 2, 0, { 0, 0, 0, 0, 0, 2, 0, { 0, 0, 0, 0, 0, 2, 0, { 0, 3, 3, 3, 3, 1, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, }; break;
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8,
6}, 6}, 6}, 6}, 6}, 6}, 6}, 6}, 6}, 6}, 9}, 9}
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 4, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 1, 0,
0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0,
0, 0, 0, 6, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 7,
0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0,
0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}
Pada bagian DEFAULT dari SWITCH – CASE di atas, kita akan memindahkan kode di dalam fungsi drawMapLvl1() dan drawObsLvl1(). default: for (int i = 0; i < mapLVL.length; i++) { for (int j = 0; j < mapLVL[0].length; j++) { switch (mapLVL[i][j]) {
76
case 1: g.drawImage(tile1, break; case 2: g.drawImage(tile2, break; case 3: g.drawImage(tile3, break; case 4: g.drawImage(tile4, break; case 5: g.drawImage(tile5, break; case 6: g.drawImage(tile6, break; case 7: g.drawImage(tile7, break; case 8: g.drawImage(tile8, break; case 9: g.drawImage(tile9, break; }
j*16, i*16, 0); j*16, i*16, 0);
j*16, i*16, 0); j*16, i*16, 0); j*16, i*16, 0); j*16, i*16, 0); j*16, i*16, 0); j*16, i*16, 0);
j*16, i*16, 0);
} } for (int i = 0; i < obsLVL.length; i++) { for (int j = 0; j < obsLVL[0].length; j++) { switch (obsLVL[i][j]) { case 1: //obs down g.drawImage(obs_down, j*16, i*16, 0); break; case 2: //obs mid g.drawImage(obs_mid, j*16, i*16, 0); break; case 3: //obs single g.drawImage(obs_single, j*16, i*16, 0); break; case 4: //obs up g.drawImage(obs_up, j*16, i*16, 0); break; case 5: //switch off g.drawImage(switch_off, j*16, i*16, 0); break; case 6: //switch on
77
g.drawImage(switch_on, j*16, i*16, 0); break; case 7: //door close g.drawImage(door_close, j*16, i*16, 0); break; case 8: //door open g.drawImage(door_open, j*16, i*16, 0); break; case 9: //kunci g.drawImage(key, j*16, i*16, 0); break; } } } break; } }
Dengan begini, mapLVL dan obsLVL penggunaannya akan reuseable, seiring dengan penambahan level. Cara penggunaannya, hanya panggil initMap() di level utama. Kode diatas hanya membuat 3 level saja. case LVL1: g.setColor(115, 130, 147); g.fillRect(0, 0, getWidth(), getHeight()); initMap(); drawChar(); movePlayer(); openObstacle(13,1); cekPintu(6,12); if (obsLVL[posY][posX] == 8) { pilih = 99; if (openLevel < 12) { openLevel = 12; } score = (56*100)/step; } break; case LVL2: g.setColor(115, 130, 147); g.fillRect(0, 0, getWidth(), getHeight()); initMap(); drawChar(); movePlayer();
78
openObstacle(9,4); cekPintu(8,12); if (obsLVL[posY][posX] == 8) { pilih = 99; if (openLevel < 13) { openLevel = 13; } score = (42*100)/step; } break; case LVL3: g.setColor(115, 130, 147); g.fillRect(0, 0, getWidth(), getHeight()); initMap(); drawChar(); movePlayer(); //openObstacle(9,4); //cekPintu(8,12); if (obsLVL[posY][posX] == 8) { pilih = 99; if (openLevel < 13) { openLevel = 13; } score = (42*100)/step; } break;
Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code & Assets\chapt5.2.7.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me.
5.2.8 SAVE DAN LOAD GAME Penyimpanan data pada J2ME dapat kita lakukan dengan menggunakan Record Management Store (RMS). Sebenarnya masih banyak teknik penyimpanan data yang bisa digunakan, salah satunya dengan penyimpanan ke eksternal file. Tapi pada tutorial kali ini, kita akan mempelajari bagaimana penyimpanan data dengan menggunakan RMS. Sekarang waktunya membuat game ini bisa di save dan di load. Pada kesempatan ini, save dan load hanya sebatas penyimpanan data level saja, dimana dia terakhir
79
berada di level berapa. Kita akan menyimpan nilai dari openLevel yang berfungsi untuk menentukan level mana yang sudah mainkan. Buat variable untuk menyimpan string. Nanti akan kita gunakan sebagai nama database kita. //save dan load public static final String STORE = "simpanLevel";
Lalu kita buat fungsi ambilData() untuk load data yang sudah ada di dalam RMS. Jangan lupa kita harus meng-import package RMS. Nanti fungsi ini akan mengembalikan nilai integer. Variable retVal untuk menyimpan nilai dari database. static int ambilData(){ int retVal = 0; RecordStore store = null; try { store = RecordStore.openRecordStore(STORE, true); int numRecords = store.getNumRecords(); if (numRecords > 0) { byte[] rec = store.getRecord(1); retVal = rec[0]; } } catch (Exception e) { }finally{ try { store.closeRecordStore(); } catch (Exception e) { } } return(retVal); }
Setelah itu kita akan membuat fungsi simpanData(), untuk menyimpan data openLevel ke database. Dengan parameter masukan bertipe integer yang berfungsi sebagai nilai yang akan disimpan ke database. static void simpanData(int size){ RecordStore store = null; try { if (size > 127) { size = 127; }
80
store = RecordStore.openRecordStore(STORE, true); byte[] record = new byte[1]; record[0] = (byte)(size); int numRecord = store.getNumRecords(); if (numRecord > 0) { store.setRecord(1, record, 0, 1); }else { store.addRecord(record, 0, 1); } } catch (Exception e) { } finally{ try { store.closeRecordStore(); } catch (Exception e) { } } }
Lalu lakukan penambahan kode sebelum game loop (sebelum while (true)). Kita ambil datanya terlebih dahulu lalu simpan pada variable openLevel. Cek dahulu apakah ada datanya, kalau belum pasti bernilai 0. if (ambilData() != 0) openLevel = ambilData();
Kemudian untuk menyimpan openLevel, dapat kita simpan pada setiap level. Contohnya pada kasus ini, kita akan menyimpannya pada CASE LVL1. case LVL1: g.setColor(115, 130, 147); g.fillRect(0, 0, getWidth(), getHeight()); initMap(); drawChar(); movePlayer(); openObstacle(13,1); cekPintu(6,12); if (obsLVL[posY][posX] == 8) { pilih = 99; if (openLevel < 12) { openLevel = 12; } score = (56*100)/step; simpanData(openLevel); }
81
break;
Sekarang coba jalankan emulatornya, kode diatas akan menyimpan data level yang telah diselesaikan sebelumnya. Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code & Assets\chapt5.2.8.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me.
5.2.9 EFEK SUARA J2ME menyediakan API untuk menangani sound FX dan BGM, yang sering disebut dengan Mobile Media API (MMAPI). MMAPI di desain untuk jalan di setiap virtual machine berbasis J2ME, termasuk CDC dan CLDC virtual machine. MMAPI memiliki fitur:
Mendukung untuk Tone Generation, Playback, and Recording of Time-Based Media: paket ini mendukung setiap isi audio dan video yang berbasis waktu (time-based). Small Footprint: MMAPI bekerja pada batas-batas memori yang tegas dari alatalat CLDC. Protocol- and Content-Agnostic: API tidak dibiaskan ke tipe atau protokol yang spesifik. Subsettable: pengembang dapat mendukung tipe tertentu. Extensible: Fitur baru dapat ditambahkan dengan mudah tanpa menghilangkan kemampuan sistem yang sebelumnya. Lebih penting lagi, format-format tambahan dapat dengan mudah didukung, dikbuat framework dan ditempatkan untuk kontrol tambahan. Options for Implementers: API menawarkan fitur untuk tujuan-tujuan yang tertentu. API didesain dan dirancang untuk mengizinkan developer meninggalkan beberapa fitur jika mereka tidak bisa didukung.
Ada dua bagian untuk mengolah multimedia: 82
Protocol Handling: membaca data dari suatu sumber seperti sebuah file atau suatu streaming server ke dalam sistem media-processing. Content Handling: menguraikan (parsing) atau decoding data dan merendernya menjadi sebuah keluaran seperti spiker audio atau layar video.
Agar dapat memfasilitasi operasi di atas, API menyediakan dua tipe obyek high-level:
DataSource mengencapsulasi penanganan protokol dengan penyembunyian detail bagaimana data itu dibaca dari sumbernya. Method obyek mempunyai berguna yang memungkinkan obyek Player menangani isi. Player membaca data dari DataSource, memprosesnya merendernya untuk ditampilkan di piranti. Obyek ini menyediakan metoda untuk mengendalikan media untuk playback, termasuk metoda untuk mengendalikan tipe tertentu mengakses fitur tipe media tertentu..
MMAPI menetapkan obyek ketiga, sebuah mekanisme yang dikenal sebagai Manager yang memungkinkan aplikasi membuat Players dari DataSources, dan dari InputStreams.
Obyek Manager menyediakan method createPlayer(), yang merupakan level tertinggi API untuk memasukkan input. Berikut contoh penggunaannya:
83
Player player = Manager.createPlayer(String url); Keterangan:
url : berisi protokol dan isi url format pengisian url <protokol>:
Aplikasi menggunakan method yang dikembalikan Player untuk mengontrol data yang didapatkan dan playback media yang berdasarkan waktu (time-based). Life–cycle Player berisi lima keadaan: UNREALIZED, REALIZED, PREFETCHED, STARTED, dan CLOSED. Ke enam method menghasilkan transisi keadaan: realize() prefetch() start() stop() deallocate() close() Ketika sebuah player dibuat, berubah ke keadaan UNREALIZED. Kemudian memanggil realize() pindah ke keadaan REALIZED dan menganalisa informasi. Memanggil prefetch() keadaan pindah ke PREFETCHED, menetapkan koneksi jaringan untuk streaming data, dan melakukan tugas-tugas inisialisasi lainnya. Memangil start() menyebabkan transisi ke keadaan STARTED, dimana player dapat memproses data. Ketika selesai memproses data (sampai ke bagian terakhir data), akan mengembalikan keadaan PREFETCHED. Memanggil close() meyebabkan player pindah ke status CLOSED. Pada kesempatan ini, kita akan memanggil 3 buah sound effect, ketika menginjak tuas, mengambil kunci, dan membuka pintu. Simpan file musik ke dalam folder res.
84
Pertama – tama kita tentukan variable yang dibutuhkan. //suara InputStream media; Player ambil_kunci, buka_kunci, menang, tuas; boolean stateTuasSFX = false;
Lalu inisialisasi Player dan InputStream, tambahkan kode berikut pada fungsi init(). //Initialize sound fx media = getClass().getResourceAsStream("/sfx/ambil_kunci.wav"); ambil_kunci = Manager.createPlayer(media, "audio/X-wav"); ambil_kunci.realize(); ambil_kunci.prefetch(); media = getClass().getResourceAsStream("/sfx/buka_kunci.wav"); buka_kunci = Manager.createPlayer(media, "audio/X-wav"); buka_kunci.realize(); buka_kunci.prefetch(); media = getClass().getResourceAsStream("/sfx/switch.wav"); tuas = Manager.createPlayer(media, "audio/X-wav"); tuas.realize(); tuas.prefetch();
Setelah itu, kita sisipkan pemanggilan Player pada event yang ingin kita tambahkan suara. stateTuas berguna untuk menghandle agar sfx tidak loop terus menerus, 85
karena hero akan berada pada tile tersebut sampai dia pindah. Cek terlebih dahulu nilai dari stateTuasSFX, jika bernilai false artinya kita akan memainkan tuas.start(). Lalu kita rubah nilai dari stateTuasSFX menjadi true. Sehingga tuas.start(0 hanya akan di akses 1x, sampai hero pindah tile, yang akan mengakibatkan nilai dari stateTuasSFX menjadi false kembali. Juga lakukan hal yang sama ketika hero mengambil kunci. bedanya, tidak perlu menggunakan state, karena object kunci akan langsung hilang dari canvas ketika hero mengambilnya. public void openObstacle(int x, int y) { //fungsi ngebuka obstacle if (obsLVL[posY][posX] == 5) { if (!stateTuasSFX) { try { tuas.start(); } catch (MediaException e) { e.printStackTrace(); } switch (stateTuas) { case 0: obsLVL[x][y] = 0; stateTuas = 1; break; case 1: obsLVL[x][y] = 3; stateTuas = 0; break; } stateTuasSFX = true; } } //fungsi ngambil kunci else if (obsLVL[posY][posX] == 9) { invent++; try { ambil_kunci.start(); } catch (MediaException e) { e.printStackTrace(); } obsLVL[posY][posX] = 0; } else { if (stateTuasSFX) { stateTuasSFX = false;
86
} } }
Terakhir kita tambahkan suara pada fungsi cekPintu(), yaitu ketika kita berhasil membuka pintu. public void cekPintu(int x, int if (obsLVL[posY-1][posX] == 7 || obsLVL[posY+1][posX] == || obsLVL[posY][posX-1] == || obsLVL[posY][posX+1] == if (invent != 0) { try { buka_kunci.start(); } catch (MediaException e) e.printStackTrace(); } obsLVL[x][y] = 8; mapLVL[x][y] = 5; invent--; } } }
y) { 7 7 7) {
{
Jalankan emulator, coba mengambil kunci. Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code & Assets\chapt5.2.9.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me.
5.2.10 ENCHANCE YOUR GAME! Selesai sudah tutorial pembuatan game ini. Game tersebut masih sangat simple. Masih banyak hal yang harus dilakukan untuk membuat game tersebut menarik. Mari kita buat agar game ini lebih menyenangkan. Ada beberapa aspek yang membuat sebuah game akan menyenangkan, yaitu:
Desain level yang menarik dan menantang. Apa yang akan kurang dari game kita sekarang? Yup, setiap level terlalu mudah. Belum ada musuh, atau teka – teki yang lebih menantang. Dan tentu saja, media sharing ke facebook dan twitter. 87
Silahkan coba kembangkan dan masukkan ide – ide yang lebih fresh pada game
5.3
Pemberian background music yang tepat dan sound effect yang menarik. Tentu saja game tanpa suara rasanya hambar. Tentu saja, harus bugfree
LEVEL DESIGN
5.3.1 TOOLS UNTUK MEMBUAT LEVEL Untuk membuat level, kita dapat menggunakan berbagai macam tool yang di desain khusus untuk merancang map tile. Salah satunya adalah Tile Map Editor (TME) v1.2.2. Aplikasi ini free dan termasuk mudah untuk penggunaannya. Berikut tampilan awal dari TME.
88
Kita akan mencoba membuat map baru. Pilih File > New TileMap… akan ada window baru, masukkan nama map dan ukuran dari map. Map Width dan Map Height merupakan jumlah tile secara lebar dan tinggi. Sedangkan Tile Width dan Tile Height merupakan lebar dan tinggi dari suatu tile. Pada contoh dibawah, akan di isi Map Width 20, Map Height 15, Tile Width 16, dan Tile Height 16. Artinya width dari map akan 20 x 16 = 320 dan heightnya akan 15 x 16 = 240 (sesuai dengan ukuran device).
Setelah itu kita akan diminta untuk mencari file image yang akan jadi map kita. Contohnya seperti dibawah ini:
89
Ketika TME selesai me-load, maka akan muncul tampilan seperti dibawah.
Tekan OK, maka kita dapat segera memulai mendesain map. Klik tile yang ingin dipasangkan lalu klik pada canvasnya. Desain sedemikian rupa sehingga menjadi seperti gambar dibawah.
90
Lalu coba export ke array, File > Export Array… > Java , jika berhasil maka akan muncul array yang dapat kita gunakan di game kita.
Dengan menggunakan tool ini, maka akan memudahkan desainer untuk merancang level. 91
5.3.2 TIPS DALAM MEMBUAT LEVEL Menjadi game designer adalah hal yang cukup sulit, karena seorang game designer harus orang yang kreatif dan inovatif. Beberapa tips untuk mendesain level: a. b. c. d.
Buat level mudah dipelajari tapi sulit untuk mencapai best score. Juga buat level tutorial untuk memudahkan player untuk mengerti cara bermainnya. Jangan terlalu monoton, tambahkan tingkat kesulitan yang bertahap dari mudah ke susah. Tambahkan sound effect dan background music agar player tidak bosan. Setiap level selalu ada tantangan tersendiri. Misalkan pada game yang telah kita buat di bab sebelumnya, agar lebih menantang kita dapat menambahkan AI di levelnya.
Masih banyak aspek yang dapat mempengaruhi agar sebuah game dapat menarik. Misalnya kita ingin menargetkan gamer yang hanya ingin mengisi waktu, kita dapat membuat game simple tapi dengan menambahkan fitur share highscore, atau game yang kompleks seperti game RPG untuk gamer yang memang hobi bermain game RPG.
92
93
Agate Studio is now hiring!
6 6.1
RELEASE DIMANA KITA BISA RILIS GAME KITA?
Banyak app store yang siap untuk menampung game kita. Salah satu yang terbesar dan terbanyak usernya adalah Nokia Store. Dengan hanya membayar 1 euro, kita akan memiliki akun di Nokia Ovi Store. Keunggulan Ovi Store dibandingkan app store lainnya: a. b. c. d.
6.2
Pengguna Nokia yang cukup banyak dari Indonesia khususnya. Banyak tipe handphone Nokia yang telah beredar dipasaran, sehingga memperbesar peluang kita. Pengguna Nokia dimudahkan dalam membeli aplikasi di Ovi Store, sehingga memperbesar kemungkinan pengguna Nokia untuk membeli game kita. Sistem QA yang bagus, game kita akan melalui serangkaian tes sehingga game kita benar – benar siap dipasarkan.
CARA PUBLISH GAME DI NOKIA STORE
Untuk mempublish game ke Nokia Store, caranya cukup mudah, berikut adalah step – by – step: 1.
2.
94
Daftarkan diri atau tim anda di Nokia Store sebagai publisher, melalui tautan berikut https://publish.ovi.com/register/country_and_account_type masukkan jenis akun yang ingin didaftarkan, corporate ketika kita ingin mempublish game atas nama perusahaan atau tim. Lalu isi informasi yang dibutuhkan selanjutnya sampai pada bagian pembayaran dengan menggunakan credit card. Biaya untuk pendaftaran sebagai publisher hanya 1 Euro atau sekitar Rp. 12.000,00. Setelah mendaftar menjadi publisher, kita dapat login ke http://publish.ovi.com/ untuk mensubmit game kita. Kita juga diberi page oleh Nokia untuk menampilkan game – game yang telah kita publish. Contohnya page Agate
3.
Studio http://store.ovi.com/publisher/Agate Studio/ disana terdapat berbagai macam game yang telah dikembangkan oleh Agate Studio. Mari kita mencoba membuat content baru, pilih Java Application ketika ditanyakan tipe content kita.
4.
Lalu kita akan diberi pertanyaan tentang kelegalan dari game kita.
5.
Setelah itu kita akan diminta untuk mengisi metadata. Mulai dari category, nama aplikasi, harga aplikasi, dan sebagainya.
95
6.
Kita juga akan diminta screenshot dan logo dari game kita. Setelah berhasil, kita akan ke halaman berikut.
7.
Setelah itu kita pilih Content Files untuk mengunggah file game kita.
96
Ada beberapa ketentuan sebelum kita mengunggah game kita, diantaranya game kita sizenya harus kurang dari atau sama dengan 2MB. Lalu nama filenya harus 5 – 48 character. Gambar dibawah adalah ketentuan dalam mengunggah game kita. Jangan lupa untuk mengubah file Application Descriptor pada project game kita. Beberapa yang perlu dirubah yaitu Overview dan MIDlets. Untuk membuild game kita, klik kanan pada project kita. Lalu pilih Mobile Tools for Java > Create Package. Setelah itu, kita dapat mengambil file JAR kita di folder project kita > deployed.
97
8.
98
Setelah selesai mengunggah, kita akan berada pada page Distribution. Disini kita akan memasukkan kompatibilitas game pada tipe handphone yang tersedia (usahakan ada 1 device yang fully tested, jika fully tested tidak lolos QA maka device lain pun akan gagal). Lalu memasukkan Negara dimana game kita akan dipasarkan, terakhir kita akan memasukkan bahasa yang digunakan pada game tersebut.
9.
Langkah terakhir, kita Submit to QA. Disini kita akan menunggu tim Quality Assurance Nokia untuk memeriksa game kita. Setelah submit, kita dapat melihat progress QA kita.
10. Jika berhasil lolos pada QA, kita dapat melihat game kita di Nokia Store. Contohnya seperti ini, http://bit.ly/KWUhBh, game Sexy Witch dari Agate Studio. 99
100
7
PENUTUP
Kami harap penjelasan yang ada di buku ini bisa membantu pembaca dalam membuat mobile game, dan semoga bisa memberi pengalaman yang bermanfaat. Jika Anda tertarik untuk mengetahui lebih lanjut tentang game development, baik teknis atau non-teknis, Anda bisa kunjungi blog Agate Studio di www.agatestudio.com/blog. Lalu jika tertarik untuk mengikuti berbagai pelatihan Game Development, Anda bisa kunjungi website Agate Academy via http://bit.ly/agateacademy.
101
LAMPIRAN A. MEMBUKA PROJECT PADA ECLIPSE PULSAR Buku ini disertai dengan sebuah CD yang berisi file-file yang dibutuhkan untuk mengikuti tutorial pada buku ini. Jika CD tidak tersedia, Anda bisa akses http://bit.ly/agatebooks-game-j2me untuk mengunduh file-file yang dibutuhkan. Pada folder “Source Code & Assets” terdapat beberapa file *.zip untuk setiap bab pada bagian pemrograman.
Untuk membuka project di tiap file Anda perlu ekstrak file zip yang bersangkutan.
102
Buka Eclipse, lalu pilih File > New > MIDlet Project. Isi nama project sesuai kebutuhan Anda. Pada bagian Contents, pastikan Anda memilih “Create project from existing source”.
103
Lalu pilih folder hasil ekstrak yang sudah dibuat.
104
Jika berhasil, pada Eclipse akan muncul project baru dengan folder “res” dan “src” yang sudah berisi file yang dibutuhkan.
Untuk menjalankan project, klik kanan pada nama project > Run As > Emulated Java ME MIDlet. 105
Berikut adalah contoh hasil akhir dari bab 5.2.2.
106