ONLINE Buku Membuat Game J2ME Agate Academy
ONLINE Buku Membuat Game J2ME Agate Academy
DAFTAR ISI
DAFTAR ISI
II
1 KATA PENGANTAR
7
7
7
7
7
9
9
10
10
10
10
10
11
4 PRE-PRODUCTION
12
ii
12
12
12
13
13
15
5 PRODUCTION
17
17
17
19
23
25
28
28
32
34
36
44
47
60
79
82
87
88
88
92
6 RELEASE
94
iii
94
94
7 PENUTUP
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.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.
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.
jauh
Anda
bisa
buka
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
PRE-PRODUCTION
GAME DESIGN
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
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.
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.
Hero_Front
PNG
16x16
Hero Hadap
depan
Hero Hadap
belakang
Hero Hadap Kiri
Hero_Back
PNG
16x16
Hero_Left
PNG
16x16
Hero_Right
PNG
16x16
Key
PNG
16x16
Hero Hadap
Kanan
-
Door_Open
PNG
16x16
Pintu terbuka
Door_Closed
PNG
16x16
Pintu tertutup
Switch_On
PNG
16x16
Switch_Off
PNG
16x16
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:
PRODUCTION
5.1
2.
c.
17
18
19
2.
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.
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
About
Level Selection
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
31
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
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
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:
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
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
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;
}
}
}
}
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.
40
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
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.
Pertama tama, kita buat interaksi hero dengan wall, dengan kata lain layer
pertama.
LEFT_PRESSED
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.
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
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;
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).
58
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
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
63
b.
int curMenu = 1;
int curLevel = 11;
64
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
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.
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
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.
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);
}
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,
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,
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,
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,
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;
}
}
}
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
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
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:
MMAPI menetapkan obyek ketiga, sebuah mekanisme yang dikenal sebagai Manager
yang memungkinkan aplikasi membuat Players dari DataSources, dan dari
InputStreams.
83
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) {
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
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
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
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
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.
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
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
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