ONLINE Buku Membuat Game J2ME Agate Academy PDF
ONLINE Buku Membuat Game J2ME Agate Academy PDF
DAFTAR ISI DAFTAR ISI 1 KATA PENGANTAR 1.1 TIM AGATE ACADEMY 2 APA YANG AKAN KITA LAKUKAN? 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 3 GAME DEVELOPMENT OVERVIEW 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 4 PRE-PRODUCTION II 5 6 7 7 7 7 7 7 9 9 9 10 10 10 10 10 11 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 5 PRODUCTION 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 6 RELEASE
12 12 12 13 13 15 17 17 17 19 23 25 28 28 32 34 36 44 47 60 79 82 87 88 88 92 94
iii
6.1 DIMANA KITA BISA RILIS GAME KITA? 6.2 CARA PUBLISH GAME DI NOKIA STORE 7 PENUTUP LAMPIRAN A. MEMBUKA PROJECT PADA ECLIPSE PULSAR
94 94 101 102
iv
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
1.1
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. dwiperdana@agatestudio.com 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. ardika@agateacademy.com HENKY JAYA DINATA Salah satu Wizard di Agate Studio. Selain membantu Agate Academy, Henky juga terlibat aktif dalam pembuatan game Football Saga 2. henky@agatewizard.com
2 2.1
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.
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.
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.
3 3.1
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.2
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
12
Untuk itu, inilah urutan dari pembuatan game kita nanti: a. b. c. d. e. f. 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.
4.2
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)
Nama Hero_Front Hero_Back Hero_Left Hero_Right Key Door_Open Door_Closed Switch_On Switch_Off Etc
Animasi
Format
Size 16x16 16x16 16x16 16x16 16x16 16x16 16x16 16x16 16x16
Frame 1 1 1 1 1 1 1 1 1
Ket. Hero Hadap depan Hero Hadap belakang Hero Hadap Kiri Hero Hadap Kanan Pintu terbuka Pintu tertutup On = Pada saat ditekan Off = Idle
1 2 3 4 5 6 7 8 9 10
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 careers@agatestudio.com 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 5.1
2.
c.
17
18
19
2.
Untuk memudahkan pengerjaan kita bisa menampilkan grid pada menu View > Show Grid. Kemudian ke menu Image > Configure Grid untuk mengatur grid sehingga ukurannya pixel.
20
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 :
23
24
Main Menu
Level Selection
About
In-Game
25
26
27
5.2
GAME PROGRAMMING
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
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(); }
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
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}
34
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 4 7 2 5 8 3 6 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,
Sama seperti ketika menggambar map, disini kita akan menggambar obstacle yang ada. Berikut adalah asset yang akan digunakan:
1 4 7
2 5 8
3 6 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.
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);
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
Lalu kita panggil fungsi drawMapLvl1() pada game loop. Kemudian kita akan mencoba menjalankan pada emulatornya.
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();
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.
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.
47
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++; } }
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; } }
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--; } } }
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.
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.
64
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; case 12: g.drawImage(b_level1, Graphics.HCENTER | g.drawImage(b_level2, Graphics.HCENTER | g.drawImage(b_locked, Graphics.HCENTER | break; case 13: g.drawImage(b_level1, Graphics.HCENTER | g.drawImage(b_level2, Graphics.HCENTER | g.drawImage(b_level3, Graphics.HCENTER | break; } 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;
c.
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.
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
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; } } }
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.
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,
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,
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,
74
{ 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,
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,
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,
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);
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.
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.
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>:<isi lokasi>
Aplikasi menggunakan method yang dikembalikan Player untuk mengontrol data yang didapatkan dan playback media yang berdasarkan waktu (time-based). Lifecycle 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
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.
Silahkan coba kembangkan dan masukkan ide ide yang lebih fresh pada game Pemberian background music yang tepat dan sound effect yang menarik. Tentu saja game tanpa suara rasanya hambar. Tentu saja, harus bugfree
5.3
LEVEL DESIGN
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
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
6 6.1
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. 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.
6.2
Untuk mempublish game ke Nokia Store, caranya cukup mudah, berikut adalah step by step: 1. 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
2.
94
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.
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.
98
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
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
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
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
106