Anda di halaman 1dari 106

i

DAFTAR ISI
DAFTAR ISI

II

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

7
7
7
7
7

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

9
9
10
10
10
10
10
11

4 PRE-PRODUCTION

12

ii

4.1 GAME DESIGN


4.1.1 CORE GAMEPLAY
4.1.2 FITUR DALAM GAME DAN GAME LOGIC
4.2 VISUAL DESIGN
4.2.1 MOCKUP
4.2.2 ASSET LIST

12
12
12
13
13
15

5 PRODUCTION

17

5.1 GAME ART PRODUCTION


5.1.1 PENGENALAN TOOLS UNTUK MEMBUAT ASET GAME
5.1.2 PEMBUATAN SPRITE
5.1.3 TILING
5.1.4 MAIN MENU/ SCREEN FLOW
5.2 GAME PROGRAMMING
5.2.1 PENGENALAN TOOLS PROGRAMMING YANG DIGUNAKAN
5.2.2 GAME LOOP
5.2.3 ARRAY MULTIDIMENSI
5.2.4 MEMASUKKAN GAMBAR DALAM GAME
5.2.5 MENGHANDLE INPUT PEMAIN
5.2.6 INTERAKSI ANTAR OBJECT
5.2.7 STATE MANAGEMENT
5.2.8 SAVE DAN LOAD GAME
5.2.9 EFEK SUARA
5.2.10 ENCHANCE YOUR GAME!
5.3 LEVEL DESIGN
5.3.1 TOOLS UNTUK MEMBUAT LEVEL
5.3.2 TIPS DALAM MEMBUAT LEVEL

17
17
19
23
25
28
28
32
34
36
44
47
60
79
82
87
88
88
92

6 RELEASE

94
iii

6.1 DIMANA KITA BISA RILIS GAME KITA?


6.2 CARA PUBLISH GAME DI NOKIA STORE

94
94

7 PENUTUP

101

LAMPIRAN A. MEMBUKA PROJECT PADA ECLIPSE PULSAR

102

iv

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

TIM AGATE ACADEMY

Berikut ini adalah orang-orang yang terlibat dalam pembuatan buku ini:
ADITIA DWIPERDANA
Co-founder Agate studio dan Guild Master di Agate Academy. Aditia sering menjadi
pembicara dalam event-event Seminar atau Workshop Game Development bersama
Agate Studio. 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

APA YANG AKAN KITA LAKUKAN?

2.1

PLATFORM YANG DIGUNAKAN

Pada buku ini, kita akan mempelajari bagaimana membuat game di J2ME. J2ME
merupakan platform yang memiliki konsep OOP yang kuat. Karena programming
J2ME merupakan bahasa yang sangat dasar, maka ketika kita dapat menguasai J2ME,
kita akan mudah untuk mempelajari bahasa pemrograman di platform lain.

2.2

KEBUTUHAN

2.2.1 SPESIFIKASI PC
Spesifikasi komputer yang dibutuhkan tidak begitu besar, karena kita menggunakan
eclipse sebagai IDE. Komputer dengan spesifikasi setara dengan netbook pun dapat
digunakan. Jika teman teman menggunakan NetBean, maka spesifikasi komputer
yang dibutuhkan pun harus lebih tinggi.

2.2.2 SOFTWARE YANG DIBUTUHKAN


Software yang kita butuhkan:

JDK 7, sebagai bahasa pemrograman,


Eclipse Pulsar, sebagai IDE untuk membuat game.
GIMP, sebagai tools untuk membuat asset gambar.
Sun WTK, sebagai emulator default dari Java.

File-file installer untuk software-softare di atas dapat ditemukan juga pada CD yang
menyertai buku ini atau dapat di akses di http://bit.ly/agatebooks-game-j2me.

2.2.3 FILE-FILE PENDUKUNG

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

GAME DEVELOPMENT OVERVIEW


PERAN TIM DEVELOPER

Dalam sebuah tim game developer ada beberapa peran yang wajib ada, namun tidak
harus satu peran full dilakukan oleh satu orang, tapi bisa saja memiliki peran rangkap
kalau anggota tim terlalu sedikit.

3.1.1 GAME DESIGNER


Game designer berperan merancang game yang akan dibuat dari sisi konsep,
gameplay atau aturan main. Disini game designer akan merancang seluruh desain
dari game yang akan dibuat. Mulai dari alur cerita, gameplay, art style, dan lain lain.
Game Designer juga akan membuat Game Design Document (GDD) sebagai tempat
untuk mencurahkan idenya dan menyampaikan kepada artist dan programmer. GDD
juga berguna sebagai panduan agar revisi dari game yang sedang dibuat tidak terlalu
meleceng. Seorang Game Designer biasanya menggunakan tools untuk mendesain
gamenya. Mulai dari GIMP untuk mendesain tampilan visual game, Tile Map Editor
(dijelaskan di akhir buku) untuk mendesain level game, bahkan notepad untuk
menulis catatan.
Untuk mengetahui lebih
http://bitly.com/LuPt9w.

jauh

tentang game designer

Anda

bisa

buka

3.1.2 GAME ARTIST


Game artist berperan membuat semua aspek visual dari game, mulai dari karakter,
background, sampai menu dan antarmuka/UI. Game artist bertanggung jawab agar
game terlihat menarik. Seorang Game Artist biasanya menggunakan GIMP untuk
membuat asset spritesheet, kadang menggunakan tool tersendiri untuk membuat
sprite.

3.1.3 GAME PROGRAMMER


Game programmer berperan menggabungkan game design dan art asset yang ada
untuk menjadi sebuah video game yang dapat dimainkan. Seorang Game
Programmer harus memiliki dasar logika yang kuat, karena game biasanya
memasukkan unsur unsur matematika dan fisika dalam pembuatannya. Game
programmer juga bertanggung jawab agar gamenya tidak terlalu memakan terlalu
banyak (khususnya pada handphone yang memorynya terbatas).

3.2

PROSES PEMBUATAN GAME

Pembuatan sebuah game melibatkan beberapa langkah yang perlu dilakukan, disini
akan diberikan gambaran umum proses pembuatan game pada umumnya.

3.2.1 PRE-PRODUCTION
Pada tahap ini kita menentukan game apa yang kita buat, seperti apa tampilannya,
dan apa saja yang kita perlukan untuk mulai membuat game tersebut. Termasuk
membuat prototype dari game kita dan negosiasi jika game ini untuk perusahaan
lain. Disinilah seorang game designer banyak bekerja, mulai dari memikirkan ide,
konsep art, gameplay, dan sebagainya.

3.2.2 PRODUCTION

10

Pada tahap ini kita terjun langsung mengerjakan game yang kita inginkan, dari sisi
art, programming, dan level desain. Game artis dan programmer banyak bekerja.
Dalam pembuatan game, programmer disarankan untuk memperlihatkan progress
programmingnya, termasuk memasukkan asset ke dalam game walaupun hanya
menggerakkan sprite. Hal itu perlu dilakukan karena biasanya pengembangan game
yang cukup lama dapat menyebabkan motivasi dari artist dan designer menurun.
Sehingga perlu dilakukan testing sehingga artist dan designer dapat melihat progress
dan melakukan revisi jika terdapat kekurangan.

3.2.3 RELEASE
Setelah game kita selesai pastinya kita perlu merilis game kita agar dapat dimainkan
oleh orang banyak. Kita harus melakukan promosi agar game kita dikenal oleh
banyak orang. Kita dapat melakukan publikasi di media ternama atau share melalui
jejaring sosial ternama. Jangan lupa untuk melakukan maintenance ketika ada yang
report bug. Dan jika perlu, kita dapat melakukan update agar game kita tetap
dimainkan oleh orang.

11

4
4.1

PRE-PRODUCTION
GAME DESIGN

4.1.1 CORE GAMEPLAY


Game yang akan kita buat akan menggunakan tile map dan pixel art. Disini kita akan
membuat game dimana player akan berusaha membantu John, seorang karyawan,
untuk kabur dari kantornya. John harus menuju ke pintu keluar agar dapat
menyelesaikan levelnya.

4.1.2 FITUR DALAM GAME DAN GAME LOGIC


Berikut contoh konsep game yang akan kita buat,
-

12

Map terbentuk dari array 2D. Terdapat bermacam macam obstacle, mulai dari
pintu, tembok, kunci, dan switch.
Game ini memiliki tujuan mengantarkan hero kita ke tempat tujuan, yaitu pintu
finishnya.
Hero akan berjalan per kotak array. Ketika hero kita menemukan obstacle pintu
dan tidak membawa kunci, maka hero akan diam. Lain halnya ketika hero
membawa kunci, pintu akan terbuka. Lalu hero juga akan menemui switch,
dimana ketika hero berada 1 kotak dengan switch, maka sesuatu akan terjadi,
seperti pintu akan terbuka atau muncul kunci lainnya

Untuk itu, inilah urutan dari pembuatan game kita nanti:


a.
b.
c.
d.
e.
f.

4.2

Membuat layer map, berupa array2D, berfungsi untuk menyimpan data map.
Membuat layer obstacle, berupa array 2D, berfungsi untuk menyimpan data
obstacle, seperti kunci, switch, pintu, dan lain lain.
Membuat layer hero, berupa posisi X dan Y yang akan mempresentasikan posisi
dari array map.
Membuat input handler, agar ketika kita menekan tombol atas bawah kiri kanan
akan menggerakkan hero kita.
Cek collision by data, periksa apakah hero menabrak dinding, kunci, switch, atau
pintu.
Play SFX tertentu jika hero mengambil kunci atau menekan switch.

VISUAL DESIGN

4.2.1 MOCKUP
Mock-up adalah gambaran kasar/sketsa atau bahkan bisa berupa gambar final yang
menggambarkan game seperti apa yang kita inginkan. Dengan menggunakan mockup
kita bisa menentukan beberapa hal seperti :

13

1.
2.
3.

Perbandingan antara karakter dengan layar atau karakter dengan environment (


Lihat Gambar 1 )
Menentukan sudut pandang game (Lihat Gambar 2)
Menentukan layout User Interface (tombol-tombol atau menu)

New Zealand Story Taito

Bubble Bobble Taito

Gambar 1 Perbandingan Karakter dengan Layar/Environment

Turn-based tactics game Kenneth Fejer


(Isometric View)

Bomberman Hudson soft


(3/4 View)

Gambar 2 Penentuan Sudut Pandang Game


14

4.2.2 ASSET LIST


Asset List adalah daftar objek-objek grafis yang diperlukan untuk membuat game
tersebut. Yang diperlukan dalam membuat asset list antara lain adalah :
1.
2.
3.
4.
5.

Nama asset
Prioritas pengerjaan asset
Format asset (PNG,SWF,JPEG,dll)
Ukuran asset
Jenis animasi dan jumlah frame tiap animasi (Apabila asset tersebut beranimasi)
Berikut contoh sebagian asset list dari game yang akan dibuat :
Prioritas

Nama

Animasi

Format

Size

Frame

Ket.

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:

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

PRODUCTION

5.1

GAME ART PRODUCTION

5.1.1 PENGENALAN TOOLS UNTUK MEMBUAT ASET GAME


Software yang digunakan untuk membuat asset game pada mobile game adalah
GIMP 2. Ada beberapa hal yang perlu kita lakukan untuk membuat aset game
menggunakan GIMP 2, yaitu :
1.

2.

Bekerja di resolusi Kecil dengan background transparan.


Untuk membuat halaman baru kita bisa mengakses File>New Image (Shortcut :
Ctrl+N). kemudian atur resolusi halaman di bagian Image Size dengan warna
background transparent. (Lihat gambar 4). Untuk memudahkan kita dalam
pembuatan aset, maka kita perlu window tambahan yang menampilkan asset
pada ukuran sebenarnya. Window tersebut bisa di akses di View > New View.
Mengatur tools yang diperlukan untuk membuat aset.
Tools yang akan sering digunakkan untuk membuat aset adalah sebagai berikut :
a. Pencil tools ( Shortcut : N )
Digunakkan untuk menggambar aset game menggunakan pensil.bisa di
akses dari toolbox. Karena gambar yang dibuat bersolusi kecil maka kita
akan menggunakan pencil tools dengan scale pencil 0.10 (Lihat gambar 5 .)
b.

c.

Eraser tools ( Shortcut : Shift+E)


Digunakan untuk menghapus gambar. Atur penghapus menjadi Hard Edge
(Lihat gambar 6).
Eyedropper (Shortcut : O)
Eyedropper digunakkan untuk mengambil warna berdasarkan warna yang
dipilih pada gambar.

17

Gambar 4 Membuat Halaman Baru

18

Gambar 5 Pengaturan Pencil tools

Gambar 6 Pengaturan Eraser

5.1.2 PEMBUATAN SPRITE


Sprites adalah graphic object / image yang dapat digerakkan terpisah dengan benda
lain seperti background. Sprites yang digunakan dalam game ini mepresentasikan
hero, obstacle, object kunci, object, object pintu. Selain itu, sprites dapat berupa
animasi atau statis dan juga dapat digunakan untuk mewakili object game yang
sudah ditentukan seperti harta karun, dan power up juga. Karena sprite dibuat
terpisah terhadap layar background, maka background sprites tersebut harus
transparan. Berikut akan dijelaskan membuat sprite Hero_front ( karakter
menghadap kedepan) menggunakan GIMP :
1. Buka halaman baru File > New (Ctrl N) dan atur resolusi 16x16 pixel dengan
background transparant.

19

2.

20

Untuk memudahkan pengerjaan kita bisa menampilkan grid pada menu


View > Show Grid. Kemudian ke menu Image > Configure Grid untuk
mengatur grid sehingga ukurannya pixel.

3.

Dengan menggunakan Pencil tool yang scalenya berukuran 0.1 buat base
outline karakter.

4.

Beri warna dasar dengan menggunakan tools pencil yang memiliki warna
yang berbeda.
21

5.

Tambahkan detail dengan menambahkan warna gelap pada tiap warna


dasar.

6.

Agar sprite kita terlihat lebih halus maka Outline hitam bisa diganti menjadi
warna gelap yang mendekati warna dasar sprite.

Dengan menggunakan cara diatas maka kita bisa membuat sprite-sprite lainnya.

22

5.1.3 TILING
Dalam membuat background dapat menggunakkan berbagai cara, salah satunya
adalah background tiles. Background tiles terdiri dari potongan-potongan gambar
kecil (tile), dimana nantinya masing-masing tile tersebut akan disusun oleh oleh
programmer menjadi background berukuran besar. Dengan menggunakan
background tiles penggunaan memory dan disk space akan lebih sedikit daripada
menggunakan background yang terdiri dari satu gambar utuh.
Tileset adalah sekumpulan gambar yang terdiri dari beberapa tile yang akan
digunakkan sebagai background game. Berikut contoh tileset yang akan kita
gunakkan dalam game kita :

Berikut contoh pengaturan background tile.

23

24

5.1.4 MAIN MENU/ SCREEN FLOW


Screen flow menunjukkan flow/aliran layar/menu-menu dalam game yang kita buat.
Screen flow tersebut mencakup Main menu, level selections, in-game, result, dan
about. Contoh screen flow dalam game yang akan kita buat :

Main Menu

About

Level Selection

In-Game

Berikut adalah contoh menu yang akan kita buat :

25

26

27

5.2

GAME PROGRAMMING

5.2.1 PENGENALAN TOOLS PROGRAMMING YANG DIGUNAKAN


5.2.1.1 PENGERTIAN J2ME
Java2 Micro Edition atau yang biasa disebut J2ME adalah lingkungan pengembangan
yang didesain untuk meletakkan perangkat lunak JAVA pada barang elektronik
berserta perangkat pendukungnya. Pada J2ME, jika perangkat lunak berfungsi
dengan baik pada sebuah perangkat maka belum tentu juga berfungsi baik pada
perangkat yang lain. J2ME membawa Java ke dunia informasi, komunikasi, dan
perangkat komputasi yang lebih kecil dibandingkan dengan computer desktop. J2ME
biasa digunakan pada telepon selular, pager, PDA, dan sejenisnya. Teknologi J2ME
juga memiliki beberapa keterbatasan jika diaplikasikan pada ponsel. J2ME sangat
bergantung pada device yang digunakan, bisa dari merk ponsel, maupun kemampuan
ponsel, dan dukungan terhadap teknologi J2ME. MIsalnya, jika sebuah ponsel tidak
memiliki kamera maka jelas J2ME pada ponsel tersebut tidak dapat mengakses
kamera.

5.2.1.2 CONNECTED LIMETED DEVICE CONFIGURATION (CLDC)


CLDC atau Connected Limited Device Cofiguration adalah perangkat dasar dari J2ME,
spesifikasi dasar yang berupa library atau API yang diimplementasikan pada J2ME,
seperti yang digunakan pada telepon selular, pager, dan PDA. Perangkat tsb dibatasi
dengan keterbatasan memory, sumber daya, dan kemampuan memproses.
Spesifikasi CLDC pada J2ME adalah spesifikasi minimal pada package, class, dan
sebagian fungsi Java Virtual Machine yang dikurangi agar dpat diimplementasikan
dengan keterbatasan sumber daya pada alat tersebut, JVM yang digunakan adalah
KVM (Kilobyte Virtual Machine).

5.2.1.3 MOBILE INFORMATION DEVICE PROFILE (MIDP)


28

MIDP atau Mobile Information Device Profile adalah spesifikasi untuk sebuah profil
J2ME. MIDP memilik lapisan diatas CLDC, API tambahan untuk daur hidup aplikasi,
antar muka, jaringan, dan pemyimpanan persisten.

5.2.1.4 MIDLET
Suatu aplikasi Mobile Information Device Profile (MIDP) pada J2ME disebut MIDlet.
mIdlet adalah bagian dari package javax.microedition.midlet. sebuah MIDlet harus diextend dengan class MIDlet. MIDlet terdiri dari beberapa method yang harus ada,
yaitu contructor(), startApp(), pauseApp(), destroyApp(). Method yang pertama
adalah startApp() dipanggil oleh Application Management Software (AMS) ketika
MIDlet dijalankan atau diresume. Resume, dengan kata lain startApp() akan kembali
dijalankan setelah di-pause. Maka berhati-hatilah ketika kita menempatkan
inisialisasi di dalam startApp().
Method yang kedua adalah pauseApp(), yang akan di-invoke oleh AMS ketika dalam
situasi tertentu, seperti pengguna menerima telepon ketika bermain game,
peringatan low battery, dan lain-lain. Dan dapat digunakan untuk meminimasi CPU,
memory, atau konsumsi baterenya, ketika game kita tidak dalam active state.
Method yang terakhir adalah destroyApp(). Method ini dipanggil untuk destroy
MIDlet oleh AMS. Dalam method ini harus clean-up resource, action, dan lain-lain.
Dan memberitahukan AMS untuk menyempurnakan cleanup dengan notifDestroy()
Ketiga method di atas merupakan bagian dari MIDP Lifecycle. Sangat penting untuk
semua programmer J2ME memahaminya.

29

MIDP Lifecycle

5.2.1.5

GAME CANVAS

Class utama Game API dari MIDP adalah class GameCanvas. Class GameCanvas
merupakan perluasan dari class Canvas yang kita gunakan dalam pembuatan low
level user interface. Dua kelemahan utama dari class Canvas dalam pemrograman
game adalah tidak memadainya kemampuan untuk mengatur proses repaint dan
ketidakmampuan untuk mengatur bagaimana pointer events serta quick keys
diteruskan pada canvas.
Komponen user interface dari MIDP umumnya berupa event driven. Events berupa
antrian berurutan dan diteruskan terhadap aplikasi satu persatu, beserta tunda
waktu antar waktu dimana event dibuat (key press).
GameCanvas memungkinkan aplikasi mengumpulkan events yang terbuat dan
melakukan proses repaint pada canvas dengan cepat. Struktur program menjadi lebih
bersih karena terdapat rangkaian perulangan utama dimana proses painting dan
pengumpulan events dilakukan.
30

GameCanvas menggunakan teknik double buffering. Seluruh proses pembuatan


interface dilakukan di off-screen buffer, kemudian di transfer dari area buffer
tersebut menuju area yang terlihat pada canvas. Aplikasi anda harus menggunakan
instance method dari class Graphics berupa method getGraphics().
Setiap pemanggilan terhadap method ini mengembalikan sebuah instance baru dari
offscreen buffer yang anda gunakan dalam proses pembuatan user interface.
Untuk memperbaharui screen tersebut, anda harus memanggil flushGraphics() untuk
melakukan proses repaint secara cepat dengan isi dari off-screen buffer. Perhatikan
bahwa anda hanya perlu memanggil method getGraphics() sekali saja, karena sebuah
buffer teralokasi setiap kali anda memanggil method ini.

31

5.2.2 GAME LOOP


Game Loop merupakan jantung dari sebuah game. Biasanya Game Loop menangani
user input, update game state, menangani AI, memainkan musik dan sound effect,
dan menampilkan display game.
bool game_is_running = true;
while( game_is_running ) {
update_game();
display_game();
}

Penanganan Game Loop khususnya pada game mobile,


harus di atur sedemikian rupa dan seefisien mungkin,
karena game mobile memiliki memory yang terbatas,
input yang terbatas, ukuran display yang kecil, cpu yang
lambat, dan lain-lain. Sehingga akan menjadi tantangan
tersendiri bagi developer untuk mengembangkan game di
J2ME. Gambar di samping ini adalah contoh dari Game
Loop:

32

Untuk mengimplementasikan sebuah game loop, kita harus membuat game loop
berjalan sendiri menggunakan thread. Sehingga game loop akan terus berjalan
selama perulangannya bernilai true. Pertama tama, buat kelas bernama
GameScreen meng-inherit GameCanvas dan implements interface.
public class GameScreen extends GameCanvas implements Runnable
Lalu tambahkan inisialisasi proses Thread ke dalam konstruktor GameScreen.
public GameScreen()
{
. . .
Thread t = new Thread(this);
t.start();
}

// create the game thread

Contoh game loop pada J2ME:


public void run() {
//Inisialisasi variable
//yang hanya dipakai 1x
while (gameIsRun) {
//Berisi fungsi-fungsi
//yang dipanggil agar game berjalan
getInput();
update();
try {
Thread.sleep(30);
}catch (InterruptedException ie) {
}
flushGraphics();
}
}

Pada contoh di atas, merupakan game loop yang paling simple. Belum memiliki state,
menghandle pause game, dan lain-lain.
Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code
& Assets\chapt5.2.2.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me.
33

5.2.3 ARRAY MULTIDIMENSI


Array 2D atau yang sering disebut juga dengan matrix, merupakan array 2 dimensi.
Array 2D sering kali dipakai di dalam pengembangan game. Khususnya pada game
yang akan kita buat sekarang, untuk membuat map, letak obstacle, dan posisi hero.
Pada game ini, kita akan membuat array 2 dimensi yang akan kita gunakan untuk
menggambar map dan obstacle. Karena kita akan memakai device dengan ukuran
lebar 240 dan tinggi 320, dengan kata lain ukuran layar akan portrait. Asumsikan
lebar dan tinggi tiap tile kita akan berukuran 16. Maka jumlah kolom tile kita adalah
240/16 = 15 kotak. Dan secara vertikal, 320/16 = 20 kotak, tapi kita coba sisakan 2
tile, sehingga untuk baris tile kita buat 18 kotak.
Oke, pertama tama kita buat 2 layer, yang pertama untuk menggambar map dan
yang kedua untuk menggambar obstacle.
public
{ 1,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 7,
};

34

int[][] mapLVL1 =
2, 2, 2, 2, 2, 2,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
8, 8, 8, 8, 8, 8,

new int[][]
3, 0, 0, 0,
6, 0, 0, 0,
6, 0, 0, 0,
6, 0, 0, 0,
6, 0, 0, 0,
6, 0, 0, 0,
2, 2, 2, 2,
5, 5, 5, 5,
5, 5, 5, 5,
5, 5, 5, 5,
5, 5, 5, 5,
5, 5, 5, 5,
5, 5, 5, 5,
5, 5, 5, 5,
5, 5, 5, 5,
5, 5, 5, 5,
5, 5, 5, 5,
5, 5, 5, 5,
8, 8, 8, 8,

{
0,
0,
0,
0,
0,
0,
2,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

0,
0,
0,
0,
0,
0,
2,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

0,
0,
0,
0,
0,
0,
2,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

0},
0},
0},
0},
0},
0},
3},
6},
6},
6},
6},
6},
6},
6},
6},
6},
6},
6},
9}

Array diatas merupakan array 15 x 18. Nanti kita akan membuat 9 macam tile seperti
ini,

Tile di atas akan dipresentasikan dalam angka, pada program ini tile tersebut akan
bernilai
1

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.

5.2.4 MEMASUKKAN GAMBAR DALAM GAME


Image dibutuhkan untuk membuat game kita ini lebih menarik. Pada game ini, kita
membutuhkan image terrain, hero, pintu, kunci, dan switch. Kita sudah memiliki
array 2D untuk menggambar map dan obstacle. Sekarang mari kita coba gambar
array yang sudah kita buat tadi, menjadi map di canvas.
Pertama tama mari kita inisialisasi dahulu variable image, di scope global.
Image tile1, tile2, tile3, tile4,

36

tile5, tile6, tile7, tile8, tile9;


Image c_front, c_back, c_right, c_left;
Image obs_down, obs_up, obs_mid, obs_single;
Image door_close, door_open;
Image key;
Image switch_on, switch_off;
public void init() {
//Initialize image
try {
//game
tile1 = Image.createImage("/tile1.png");
tile2 = Image.createImage("/tile2.png");
tile3 = Image.createImage("/tile3.png");
tile4 = Image.createImage("/tile4.png");
tile5 = Image.createImage("/tile5.png");
tile6 = Image.createImage("/tile6.png");
tile7 = Image.createImage("/tile7.png");
tile8 = Image.createImage("/tile8.png");
tile9 = Image.createImage("/tile9.png");
obs_down = Image.createImage("/obs_down.png");
obs_mid = Image.createImage("/obs_mid.png");
obs_single = Image.createImage("/obs_single.png");
obs_up = Image.createImage("/obs_up.png");
c_back = Image.createImage("/c_back.png");
c_front = Image.createImage("/c_front.png");
c_left = Image.createImage("/c_left.png");
c_right = Image.createImage("/c_right.png");
switch_off = Image.createImage("/switch_off.png");
switch_on = Image.createImage("/switch_on.png");
key = Image.createImage("/key.png");
door_close = Image.createImage("/door_close.png");
door_open = Image.createImage("/door_open.png");
} catch (IOException e) {
e.printStackTrace();
}
}

Lalu pada kode di atas, kita akan menginisialisasi variable sebelumnya. Dengan cara
Image.createImage(/obs_down.png), yang berada didalam tanda kurung
merupakan lokasi dan nama file gambar yang akan diload. Semua file gambar harus
diletakkan pada folder res di project eclipse kita. Seperti pada gambar berikut:

37

public void drawMapLv1() {


for (int i = 0; i < mapLVL1.length; i++) {
for (int j = 0; j < mapLVL1[0].length; j++) {
switch (mapLVL1[i][j]) {
case 1:
g.drawImage(tile1, j*16, i*16, 0);
break;

38

case 2:
g.drawImage(tile2,
break;
case 3:
g.drawImage(tile3,
break;
case 4:
g.drawImage(tile4,
break;
case 5:
g.drawImage(tile5,
break;
case 6:
g.drawImage(tile6,
break;
case 7:
g.drawImage(tile7,
break;
case 8:
g.drawImage(tile8,
break;
case 9:
g.drawImage(tile9,
break;
}

j*16, i*16, 0);


j*16, i*16, 0);

j*16, i*16, 0);


j*16, i*16, 0);
j*16, i*16, 0);
j*16, i*16, 0);
j*16, i*16, 0);
j*16, i*16, 0);

}
}
}

Fungsi diatas akan memeriksa nilai setiap array, dan menggambarnya kedalam
canvas. Fungsi drawImage untuk menggambar gambar, dengan parameter pertama
Image, posisi X, posisi Y, dan terakhir reference point dari gambar tersebut. 0 berarti
titik reference point berada pada posisi kiri atas. j*16 maksudnya untuk menggambar
tile tersebut pada posisi mana. Misalkan pada iterasi pertama, berarti j masih bernilai
0, sehingga hasilnya pun akan (0, 0). Iterasi kedua j = 1, maka akan (0, 16), dst.
public void run() {
System.out.println("getWidth: " + getWidth());
System.out.println("getHeight: " + getHeight());
init();
while (true) {
g.setColor(115, 130, 147);
g.fillRect(0, 0, getWidth(), getHeight());
drawMapLv1();
flushGraphics();

39

try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Lalu kita panggil fungsi drawMapLvl1() pada game loop. Kemudian kita akan
mencoba menjalankan pada emulatornya.

Gambar di atas adalah hasil susunan array 2D kita sebelumnya.


public void drawObsLv1() {
for (int i = 0; i < obsLVL1.length; i++) {
for (int j = 0; j < obsLVL1[0].length; j++) {
switch (obsLVL1[i][j]) {
case 1: //obs down
g.drawImage(obs_down, j*16, i*16, 0);
break;
case 2: //obs mid
g.drawImage(obs_mid, j*16, i*16, 0);
break;
case 3: //obs single
g.drawImage(obs_single, j*16, i*16, 0);
break;
case 4: //obs up

40

g.drawImage(obs_up, j*16, i*16, 0);


break;
case 5: //switch off
g.drawImage(switch_off, j*16, i*16, 0);
break;
case 6: //switch on
g.drawImage(switch_on, j*16, i*16, 0);
break;
case 7: //door close
g.drawImage(door_close, j*16, i*16, 0);
break;
case 8: //door open
g.drawImage(door_open, j*16, i*16, 0);
break;
case 9: //kunci
g.drawImage(key, j*16, i*16, 0);
break;
}
}
}
}

Lalu kita lakukan hal yang sama pada obstacle kita, lalu panggil kembali di game loop.
Lalu run kembali ke emulator.

41

Gambar di atas merupakan hasil dari penambahan obstacle. Lalu sekarang kita akan
menambahkan hero kita. Kode dibawah untuk variable yang akan digunakan. posX
dan posY merupakan posisi dari hero kita. 2, 2 artinya hero kita berada di baris ke 2
dan kolom ke 2. Variable arahChar untuk menentukan kemana arah dari hero kita.
Kita inisialisasikan dengan 4, yang artinya hero kita akan menghadap ke bawah.
//state char
int posX = 2, posY = 2;
public int arahChar = 4;
public static final int KIRI = 1;
public static final int KANAN = 2;
public static final int ATAS = 3;
public static final int BAWAH = 4;

Lalu kode berikutnya untuk menggambar hero pada canvas. Caranya hampir sama
ketika kita menggambar map dan obstacle. Bedanya kita bukan mengecek nilai array,
42

tapi mengecek posX dan posY, jika nilainya sama, maka kita buka statement SWITCH
CASE yang berguna untuk mengecek hadapan dari hero kita.
public void drawChar() {
for (int x = 0; x < mapLVL1.length; x++) {
for (int y = 0; y < mapLVL1[0].length; y++) {
if (y == posX && x == posY) {
switch (arahChar) {
case ATAS:
g.drawImage(c_back, posX*16, posY*16, 0);
break;
case BAWAH:
g.drawImage(c_front, posX*16, posY*16, 0);
break;
case KANAN:
g.drawImage(c_right, posX*16, posY*16, 0);
break;
case KIRI:
g.drawImage(c_left, posX*16, posY*16, 0);
break;
}
}
}
}
}

Jangan lupa kita panggil fungsi di atas pada game loop kita.
g.setColor(115, 130, 147);
g.fillRect(0, 0, getWidth(), getHeight());
drawMapLv1();
drawObsLv1();
drawChar();
flushGraphics();

Lalu jalankan kembali emulator kita.

43

Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code
& Assets\chapt5.2.4.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me.

5.2.5 MENGHANDLE INPUT PEMAIN


Input handler merupakan fungsi yang akan menangani ketika player menekan suatu
tombol di device kita. Pada game ini kita akan buat 2 macam input, yang pertama
untuk menangani input pada layar menu dan pada permainan. Karena ketika kita
menekan tombol atas pada layar menu akan berbeda hasilnya ketika kita menekan di
dalam game. Pada layar menu, tombol atas akan menggerakkan kursor ke atas,
sedangkan pada game akan menggerakkan hero 1 kotak ke atas.
Pada contoh game kali ini, game input hanya digunakan untuk menggerakkan hero
dan pada menu utama.
44

Pertama tama kita tentukan terlebih dahulu, variable apa yang akan digunakan.
buttonHold berguna untuk menahan gerakan ketika kita menekan dan menahan
suatu tombol. x dan y digunakan untuk mengubah posX dan posY.
//game input
boolean buttonHold = false;
int x = 0, y = 0;

Lalu kita buat fungsi movePlayer(), pada awal fungsi kita buat agar x dan y bernilai 0,
sehingga x dan y hanya berubah nilainya ketika kita menekan. Jika kita tidak merubah
nilai x dan y, maka hero akan terus bergerak meskipun kita sudah melepas
tombolnya. getKeyState() merupakan fungsi yang mengembalikan nilai int, yang
merupakan penanda bahwa suatu tombol sedang ditekan. Lalu ada pengecekan,
apakah keyState dan GameCanvas.LEFT_PRESSED tidak 0. Jika true, maka lakukan
kode berikutnya. Kode selanjutnya ada pengecekan kembali, yaitu pengecekan
terhadap buttonHold. Disini kita akan mengecek apakah player masih menekan
tombol atau tidak. Jika YA, maka skip kode di dalamnya. Jika TIDAK, maka x kita rubah
nilainya menjadi -1 . Lalu kita rubah nilai buttonHold menjadi true sehingga kode di
dalam IF ini tidak akan dilakukan lagi sampai player melepas tombol. Dan juga kita
rubah arahChar menjadi ke KIRI. Dan begitu selanjutnya, sampai pada bagian akhir
dari IF THEN ELSE terdapat fungsi untuk merubah kembali buttonHold menjadi
false, sehingga ketika player melepas tombol maka buttonHold pun berubah kembali
menjadi false. Lalu kita rubah kembali arahChar menjadi ke bawah, ini bersifat
opsional jika kita ingin posisi char tetap pada posisi hadapnya, maka teman teman
dapat menghapus kode arahChar = BAWAH;.
public void movePlayer() {
int keyState = getKeyStates();
x = 0;
y = 0;
if ((keyState & GameCanvas.LEFT_PRESSED) != 0) {
if(!buttonHold)
{
x = -1;
buttonHold = true;
arahChar = KIRI;
}

45

}else if ((keyState & GameCanvas.RIGHT_PRESSED) != 0) {


if(!buttonHold)
{
x = 1;
buttonHold = true;
arahChar = KANAN;
}
}else if ((keyState & GameCanvas.UP_PRESSED) != 0) {
if(!buttonHold)
{
y = -1;
buttonHold = true;
arahChar = ATAS;
}
}else if ((keyState & GameCanvas.DOWN_PRESSED) != 0) {
if(!buttonHold)
{
y = 1;
buttonHold = true;
arahChar = BAWAH;
}
}else{
if(buttonHold)
buttonHold = false;
//arahChar = BAWAH;
}
posX += x;
posY += y;
}

Jangan lupa kita panggil fungsinya di game loop. Lalu coba jalankan ke emulator, dan
coba gerakkan dengan tombolnya.
g.setColor(115, 130, 147);
g.fillRect(0, 0, getWidth(), getHeight());
drawMapLv1();
drawObsLv1();
drawChar();
movePlayer();
flushGraphics();

46

Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code
& Assets\chapt5.2.5.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me.

5.2.6 INTERAKSI ANTAR OBJE CT


Pada bab ini, akan menjabarkan tentang bagaimana interaksi hero dengan object
yang ada di map. Kode berikut ini hanyalah mengubah dari fungsi movePlayer(),
misalnya pada bagian x = -1, kita rubah menjadi kode dibawah tanpa mengubah
susunan kode sebelum dan sesudahnya. Berikut list interaksi hero dengan objek:
a.

Pertama tama, kita buat interaksi hero dengan wall, dengan kata lain layer
pertama.
LEFT_PRESSED

if ((keyState & GameCanvas.LEFT_PRESSED) != 0) {


if(!buttonHold)

47

{
if (mapLVL1[posY][posX-1] == 5) {
x = -1;
}
buttonHold = true;
arahChar = KIRI;
step++;
}
}

RIGHT_PRESSED
Sama dengan LEFT_PRESSED susunannya. Begitu pula pada DOWN dan
UP_PRESSED
else if ((keyState & GameCanvas.RIGHT_PRESSED) != 0) {
if(!buttonHold)
{
if (mapLVL1[posY][posX+1] == 5) {
x = 1;
}
buttonHold = true;
arahChar = KANAN;
step++;
}
}

UP_PRESSED
else if ((keyState & GameCanvas.UP_PRESSED) != 0) {
if(!buttonHold)
{
if (mapLVL1[posY-1][posX] == 5) {
y = -1;
}
buttonHold = true;
arahChar = ATAS;
step++;
}
}

48

DOWN_PRESSED
else if ((keyState & GameCanvas.DOWN_PRESSED) != 0) {
if(!buttonHold)
{
if (mapLVL1[posY+1][posX] == 5) {
y = 1;
}
buttonHold = true;
arahChar = BAWAH;
step++;
}
}

Kode di atas maksudnya, kita akan mengecek terlebih dahulu, apakah tile
disampingnya bernilai 5, jika YA maka hero akan bergerak ke arah tersebut. Gambar
dibawah merupakan tampilan dari emulator setelah kita rubah.

49

b.

Karena pada pembuatan obstacle, kita berbeda layer dengan map, maka kita
juga harus membuat interaksi hero dengan obstacle, khususnya obstacle meja.
Pada pengecekan ini, kita coba dengan cara yang berbeda, yaitu menggunakan
fungsi untuk mengecek dengan parameter masukan posisi dari hero.

public boolean cekObs(int x, int y) {


if (obsLVL1[x][y] == 1 || obsLVL1[x][y] == 2
|| obsLVL1[x][y] == 3 || obsLVL1[x][y] == 4) {
return true;
}else

50

return false;
}

Fungsi di atas akan mengecek apakan lokasi dari hero (x, y) berisi obstacle (1, 2,
3, 4) atau tidak. Jika ya, kembalikan nilai true, dan sebaliknya. Untuk cara
pengecekannya, kita letakkan didalam IF sebelumnya. Sehingga pada game
input, pengecekannya menjadi seperti ini.
Oh iya, pengecekan dibawah kita update dari pengecekan sebelumnya.
LEFT_PRESSED
if ((keyState & GameCanvas.LEFT_PRESSED) != 0) {
if(!buttonHold)
{
if (mapLVL1[posY][posX-1] == 5 && !cekObs(posY, posX-1)) {
x = -1;
}
buttonHold = true;
arahChar = KIRI;
step++;
}
}

RIGHT_PRESSED
Sama dengan LEFT_PRESSED susunannya. Begitu pula pada DOWN dan
UP_PRESSED
else if ((keyState & GameCanvas.RIGHT_PRESSED) != 0) {
if(!buttonHold)
{
if (mapLVL1[posY][posX+1] == 5 && !cekObs(posY, posX+1)) {
x = 1;
}
buttonHold = true;
arahChar = KANAN;
step++;
}
}

UP_PRESSED

51

else if ((keyState & GameCanvas.UP_PRESSED) != 0) {


if(!buttonHold)
{
if (mapLVL1[posY-1][posX] == 5 && !cekObs(posY-1, posX)) {
y = -1;
}
buttonHold = true;
arahChar = ATAS;
step++;
}
}

DOWN_PRESSED
else if ((keyState & GameCanvas.DOWN_PRESSED) != 0) {
if(!buttonHold)
{
if (mapLVL1[posY+1][posX] == 5 && !cekObs(posY+1, posX)) {
y = 1;
}
buttonHold = true;
arahChar = BAWAH;
step++;
}
}

Lalu kita coba run pada emulator.

52

c.

Lalu kita buat interaksi hero dengan tuas. Ketika kita berada di atas tuas, maka
akan terjadi sesuatu. Misalkan ada meja yang hilang, atau muncul kunci, dan lain
lain. Pada stage pertama ini, kita akan membuat 1 tuas, ketika hero kita berada
pada tile yang sama dengan tuas tersebut, maka akan ada sebuah meja yang
hilang. Tentu saja ketika kita menonaktifkan tuas tersebut, maka meja pun akan
kembali muncul.
Pertama tama kita tentukan dahulu variable yang akan digunakan. stateTuas
digunakan untuk mendefinisikan keadaan dari tuas, apakah sedang terbuka atau
tertutup. Ini berguna untuk mengubah gambar tuas ketika sedang terbuka atau
tertutup.

53

int stateTuas = 0;

Lalu kita buat fungsi openObstacle(), untuk melakukan pengecekan terhadap


posisi hero kita. Karena posX dan posY merupakan posisi dari hero kita (yang
merupakan posisi dalam array juga), maka dapat kita gunakan untuk mengecek
nilai dari array obstacle. Lalu ada SWITCH CASE, untuk memeriksa apakah tuas
berada dalam posisi ON atau OFF. Jika bernilai 0, maka obstacle pada baris ke 13
kolom ke 1 kita rubah menjadi 0, yang artinya meja pun hilang dan stateTuas
pun berubah nilainya menjadi 1. Jika kita menginjaknya lagi, maka meja pun
muncul lagi dan stateTuas berubah menjadi 0 kembali.
public void openObstacle(int x, int y) {
//fungsi ngebuka obstacle
if (obsLVL1[posY][posX] == 5) {
switch (stateTuas) {
case 0:
obsLVL1[x][y] = 0;
stateTuas = 1;
break;
case 1:
obsLVL1[x][y] = 3;
stateTuas = 0;
break;
}
}
}

54

55

d.

Setelah itu kita buat interaksi hero dengan kunci. Ketika kita berada di atas objek
kunci, maka kunci akan pindah ke inventory hero.
Pertama tama kita buat variable invent, yang berguna untuk menyimpan
jumlah kunci. Disini kita simpan dalam bentuk int, sehingga ketika hero
mengambil kunci, maka invent hanya kita tambah 1 saja.

int invent = 0;

Lalu kita tambahkan kode berikut pada fungsi openObstacle(). Caranya sama
dengan pengecekan tile wall dan obstacle, kita cek apakah posisi berdiri hero ini
bernilai 9 (obstacle kunci). Jika YA, maka invent kita tambah nilainya, lalu object
kunci kita hilangkan dari canvas dengan cara mengubah nilai array menjadi 0.
public void openObstacle(int x, int y) {
//fungsi ngebuka obstacle

56

if (obsLVL1[posY][posX] == 5) {
switch (stateTuas) {
case 0:
obsLVL1[x][y] = 0;
stateTuas = 1;
break;
case 1:
obsLVL1[x][y] = 3;
stateTuas = 0;
break;
}
}
//fungsi ngambil kunci
else if (obsLVL1[posY][posX] == 9) {
invent++;
obsLVL1[posY][posX] = 0;
}
}

Setelah itu kita jalankan kembali emulatornya.

57

e.

Dan yang terakhir, kita buat interaksi hero dengan pintu. Dimana pintu ini adalah
tempat tujuan dari hero kita. Ketika hero kita tidak membawa kunci, maka kita
tidak bisa melalui pintu tersebut. Ketika kita membawa kunci, maka hero kita
pun akan memenangkan stage itu.
Kita buat fungsi cekPintu(), untuk mengecek apakah disekeliling hero terdapat
pintu atau tidak. Jika YA, maka periksa lagi, apakah invent tidak bernilai 0, jika YA
baru kita rubah obstaclenya menjadi 8, yaitu gambar pintu terbuka. Dan jangan
lupa, kita rubah juga map kita menjadi 5, agar hero kita bisa masuk ke dalam
pintu (cek fungsi pada movePlayer()). Lalu kita kurangi jumlah invent. Parameter
masukan x dan y berguna untuk menentukan posisi pintu yang akan dibuka
(berdasarkan array).

public void cekPintu(int x, int y) {


if (obsLVL1[posY-1][posX] == 7
|| obsLVL1[posY+1][posX] == 7
|| obsLVL1[posY][posX-1] == 7
|| obsLVL1[posY][posX+1] == 7) {
if (invent != 0) {
obsLVL1[x][y] = 8;
mapLVL1[x][y] = 5;
invent--;
}
}
}

Susunan terakhir dari game loop kita,

58

public void run() {


System.out.println("getWidth: " + getWidth());
System.out.println("getHeight: " + getHeight());
init();
while (true) {
g.setColor(115, 130, 147);
g.fillRect(0, 0, getWidth(), getHeight());
drawMapLv1();
drawObsLv1();
drawChar();
movePlayer();
openObstacle(13,1);
cekPintu(6,12);
flushGraphics();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

59

Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code
& Assets\chapt5.2.6.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me.

5.2.7 STATE MANAGEMENT


State management pada sebuah game sering dipakai dalam membuat alur screen
flow dari sebuah game. Misalkan pada game ada splash screen, menu utama, level
selection, level 1, level 2, highscore, dan lain lain. Agar susunan kode program
lebih rapih dan lebih mudah melakukan debugging jika terjadi error.
Pada buku ini, kita akan membuat main menu yang memiliki 3 pillihan, yaitu Play,
About, dan Exit. Play akan membuka ke Level Selection setelah itu baru kita dapat
bermain. Sedangkan About akan menuju ke menu about, dan terakhir Exit, tentu saja
untuk keluar dari game.
Pada tutorial kali ini, kita akan membuat 5 state:
a. Menu Utama
Pertama tama kita buat dulu variable lokal yang dibutuhkan. Variable pilih
digunakan sebagai key pada SWITCH CASE statement. Lalu kita buat variable
int statis, untuk menyimpan nilai pilihan. Lalu kita buat variable curMenu, untuk
membuat posisi pointer kita nanti (cursor menu). Jangan lupa kita buat variable
untuk memuat image yang dibutuhkan.
//state management
int pilih = 0;
protected final static int LVL_SEL = 1;
protected final static int ABOUT = 2;
protected final static int EXIT = 3;
protected final static int LVL1 = 11;
protected final static int LVL2 = 12;
protected final static int LVL3 = 13;
protected final static int MENU = 0;
protected final static int GAMEOVER = 99;
int curMenu = 1;
Image b_about_off, b_about_on, b_exit_off,
b_exit_on, b_play_off, b_play_on,
bg_about, bg_mainmenu, hand_pointer;

60

Sekarang kita inisialisasi image, letakkan kode berikut pada fungsi init()
//main menu
b_about_off = Image.createImage("/mainMenu/b_about_off.png");
b_about_on = Image.createImage("/mainMenu/b_about_on.png");
b_exit_off = Image.createImage("/mainMenu/b_exit_off.png");
b_exit_on = Image.createImage("/mainMenu/b_exit_on.png");
b_play_off = Image.createImage("/mainMenu/b_play_off.png");
b_play_on = Image.createImage("/mainMenu/b_play_on.png");
bg_about = Image.createImage("/mainMenu/bg_about.png");
bg_mainmenu = Image.createImage("/mainMenu/bg_mainmenu.png");
hand_pointer = Image.createImage("/mainMenu/hand_pointer.png");

Setelah memuat inisialisasi gambar, mari kita buat fungsi mainMenu(), ini akan
kita panggil sebagai menu utama pada game ini. Pertama tama, kita
drawImage terlebih dahulu bg_mainmenu. Lalu kita buat SWITCH CASE
statement dengan curMenu sebagai kuncinya. Ini berguna ketika kita menekan
atas atau bawah, maka nilai curMenu akan ikut berubah. Sehingga CASE nya
pun akan selalu berubah. Misalkan pada case 1, maka button Play yang sedang
aktif, dan hand_pointer pun berada pada posisi Play. Lalu kita buat fungsi input
agar ketika kita menekan atas atau bawah, akan merubah nilai dari curMenu.
Dan jika tombol FIRE ditekan, maka variable pilih pun akan berubah. Variable
pilih digunakan untuk menentukan menu mana yang akan dijalankan, apakah
menu utama, about, level selection, atau yang lain lain.
public void mainMenu() {
g.drawImage(bg_mainmenu, 0, 0, 0);
switch (curMenu) {
case 1:
g.drawImage(b_play_on, getWidth()/2, 180,
Graphics.HCENTER | Graphics.VCENTER);
g.drawImage(b_about_off, getWidth()/2, 220,
Graphics.HCENTER | Graphics.VCENTER);
g.drawImage(b_exit_off, getWidth()/2, 260,
Graphics.HCENTER | Graphics.VCENTER);
g.drawImage(hand_pointer, getWidth()/5+5, 180,
Graphics.HCENTER | Graphics.VCENTER);
break;
case 2:
g.drawImage(b_play_off, getWidth()/2, 180,
Graphics.HCENTER | Graphics.VCENTER);
g.drawImage(b_about_on, getWidth()/2, 220,
Graphics.HCENTER | Graphics.VCENTER);

61

g.drawImage(b_exit_off, getWidth()/2, 260,


Graphics.HCENTER | Graphics.VCENTER);
g.drawImage(hand_pointer, getWidth()/5+5, 220,
Graphics.HCENTER | Graphics.VCENTER);
break;
case 3:
g.drawImage(b_play_off, getWidth()/2, 180,
Graphics.HCENTER | Graphics.VCENTER);
g.drawImage(b_about_off, getWidth()/2, 220,
Graphics.HCENTER | Graphics.VCENTER);
g.drawImage(b_exit_on, getWidth()/2, 260,
Graphics.HCENTER | Graphics.VCENTER);
g.drawImage(hand_pointer, getWidth()/5+5, 260,
Graphics.HCENTER | Graphics.VCENTER);
break;
default:
break;
}
int keyState = getKeyStates();
if ((keyState & UP_PRESSED) != 0) {
if (curMenu <= 1) {
curMenu = 3;
}else
curMenu--;
}else if ((keyState & DOWN_PRESSED) != 0) {
if (curMenu >= 3) {
curMenu = 1;
}else
curMenu++;
}
if ((keyState & FIRE_PRESSED) != 0) {
pilih = curMenu;
}
}

Lakukan sedikit perubahan pada game loop kita (yang terdapat di fungsi run()).
Pada awalnya, kita langsung menulis kode pada level 1 di game loop. Sekarang
kita buat SWITCH CASE statement sehingga kita bisa mengatur menu mana
yang akan dijalankan. Variable pilih menjadi kunci pada pemilihan CASE. MENU
memiliki nilai 0, maka dari itu, pilih kita inisialisasikan dengan nilai 0 terlebih
dahulu.
62

public void run() {


System.out.println("getWidth: " + getWidth());
System.out.println("getHeight: " + getHeight());
init();
while (true) {
switch (pilih) {
case MENU:
mainMenu();
break;
case LVL_SEL:
menuLevel();
break;
case ABOUT:
menuAbout();
break;
case EXIT:
parent.notifyDestroyed();
break;
case LVL1:
g.setColor(115, 130, 147);
g.fillRect(0, 0, getWidth(), getHeight());
initMap();
drawChar();
movePlayer();
openObstacle(13,1);
cekPintu(6,12);
if (obsLVL[posY][posX] == 8) {
pilih = 99;
if (openLevel < 12) {
openLevel = 12;
}
}
break;
case GAMEOVER:
menuResult();
break;
}
flushGraphics();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

63

b.

Menu Level Selection


Buat variable curLevel dan openLevel. curLevel untuk cursor ktia pada level
selection. Karena pada sebelumnya LVL1 kita inisialisasikan dengan nilai 11,
maka diusahakan ketika kita menekan tombol FIRE, maka variable pilih harus
berubah menjadi 11. openLevel merupakan level yang dibuka. Pada awalnya,
kita inisialisasikan 11, sehingga ketika pertama kali memainkan game ini, maka
level 1 sudah pasti terbuka. Nantinya akan kita tambahkan fitur load, sehingga
akan melihat level mana yang sudah terbuka atau belum.

int curMenu = 1;
int curLevel = 11;

Lalu seperti biasa, buat variable untuk image.

64

Image bg_tileset, b_level1, b_level2, b_level3,


b_locked, levelselection_mid,
levelselection_down, levelselection_top;

Dan inisialisasikan image sebelum game loop.


//level selection
b_locked =
Image.createImage("/mainMenu/b_locked.png");
bg_tileset =
Image.createImage("/mainMenu/bg_tileset.png");
b_level1 =
Image.createImage("/mainMenu/b_level1.png");
b_level2 =
Image.createImage("/mainMenu/b_level2.png");
b_level3 =
Image.createImage("/mainMenu/b_level3.png");
levelselection_mid =
Image.createImage("/mainMenu/levelselection_mid.png");
levelselection_down =
Image.createImage("/mainMenu/levelselection_down.png");
levelselection_top =
Image.createImage("/mainMenu/levelselection_top.png");

Kemudian kita buat fungsi menuLevel(), kita drawImage bg_tileset sebagai


lapisan paling belakang. Lalu levelselection_top, levelselection_mid, dan
levelselection_down sesuaikan posisinya pada canvas. Baru setelah itu kita
masukkan gambar level dan pointer dengan menggunakan SWITCH CASE
statement menggunakan openLevel sebagai kuncinya. Lalu kita buat SWITCH
CASE statement 1 lagi untuk menggambar pointer, dengan menggunakan
curLevel sebagai kuncinya. Kita juga membuat input handle difungsi ini, yang
menangani tombol atas, bawah dan fire. Tombol atas dan bawah secara fungsi
sama seperti sebelumnya. Pada tombol FIRE, akan terjadi pengecekan, apakah
curLevel lebih kecil atau sama dengan openLevel, jika YA berarti level tersebut
sudah bisa dimainkan.
public void menuLevel() {
g.drawImage(bg_tileset, 0, 0, 0);
g.drawImage(levelselection_top, 0, 0, 0);
g.drawImage(levelselection_mid,
getWidth()/2, getHeight()/3,

65

Graphics.HCENTER | Graphics.VCENTER);
g.drawImage(levelselection_down,
0, getHeight(),
Graphics.LEFT | Graphics.BOTTOM);
switch (openLevel) {
case 11:
g.drawImage(b_level1,
Graphics.HCENTER |
g.drawImage(b_locked,
Graphics.HCENTER |
g.drawImage(b_locked,
Graphics.HCENTER |
break;

getWidth()/2, 150,
Graphics.VCENTER);
getWidth()/2, 200,
Graphics.VCENTER);
getWidth()/2, 250,
Graphics.VCENTER);

case 12:
g.drawImage(b_level1,
Graphics.HCENTER |
g.drawImage(b_level2,
Graphics.HCENTER |
g.drawImage(b_locked,
Graphics.HCENTER |
break;

getWidth()/2, 150,
Graphics.VCENTER);
getWidth()/2, 200,
Graphics.VCENTER);
getWidth()/2, 250,
Graphics.VCENTER);

case 13:
g.drawImage(b_level1,
Graphics.HCENTER |
g.drawImage(b_level2,
Graphics.HCENTER |
g.drawImage(b_level3,
Graphics.HCENTER |
break;

getWidth()/2, 150,
Graphics.VCENTER);
getWidth()/2, 200,
Graphics.VCENTER);
getWidth()/2, 250,
Graphics.VCENTER);

}
switch (curLevel) {
case 11:
g.drawImage(hand_pointer, getWidth()/3, 150,
Graphics.LEFT | Graphics.VCENTER);
break;
case 12:
g.drawImage(hand_pointer, getWidth()/3, 200,
Graphics.LEFT | Graphics.VCENTER);
break;
case 13:
g.drawImage(hand_pointer, getWidth()/3, 250,
Graphics.LEFT | Graphics.VCENTER);
break;
}

66

int keyState = getKeyStates();


if ((keyState & UP_PRESSED) != 0) {
if (curLevel <= 11) {
curLevel = 13;
}else
curLevel--;
}else if ((keyState & DOWN_PRESSED) != 0) {
if (curLevel >= 13) {
curLevel = 11;
}else
curLevel++;
}
if ((keyState & FIRE_PRESSED) != 0) {
if(curLevel <= openLevel){
pilih = curLevel;
level = curLevel - 10;
}
}
}

67

Untuk mengubah nilai dari openLevel, kita tambahkan kode berikut pada case
LVL1.
case LVL1:
g.setColor(115, 130, 147);
g.fillRect(0, 0, getWidth(), getHeight());
initMap();
drawChar();
movePlayer();
openObstacle(13,1);
cekPintu(6,12);
if (obsLVL[posY][posX] == 8) {
pilih = 99;
if (openLevel < 12) {
openLevel = 12;
}
score = (56*100)/step;
}
break;

Ketika posisi hero berada pada pintu yang terbuka, maka kita rubah nilai pilih
menjadi 99 dan rubah nilai dari openLevel menjadi 12. Lakukan hal yang sama
pada LVL2 dan LVL3.
Menu About
Menu about sangat simple, hanya drawImage lalu input handle ketika kita
menekan tombol FIRE, yang akan merubah nilai pilih menjadi 0.

c.

public void menuAbout() {


g.drawImage(bg_about, 0, 0, 0);
int keyState = getKeyStates();
if ((keyState & FIRE_PRESSED) != 0) {
pilih = 0;
}
}

68

d.

Menu Result
Seperti biasa, tentukan variable yang dibutuhkan. curResult untuk cursor selama
di menu result. Variable step dan score untuk pemberian penilaian tentang
berapa step yang telah dilalui dan berapa persen kemenangan dari player. Lalu
variable image beserta inisialisasinya.

int step = 0;
int score = 0;
int curResult = 1;
Image bg_result, b_nextlevel_off, b_nextlevel_on,
b_tryagain_off, b_tryagain_on;

Inisialisasi,
//win condition
bg_result =
Image.createImage("/mainMenu/bg_result.png");

69

b_nextlevel_off =
Image.createImage("/mainMenu/b_nextlevel_off.png");
b_nextlevel_on =
Image.createImage("/mainMenu/b_nextlevel_on.png");
b_tryagain_off =
Image.createImage("/mainMenu/b_tryagain_off.png");
b_tryagain_on =
Image.createImage("/mainMenu/b_tryagain_on.png");

Dan seperti sebelumnya kita buat SWITCH CASE statement dengan kunci
curResult, untuk memberi tanda pointernya. Ketika menekan FIRE,
menggunakan SWITCH CASE statement lagi.
public void menuResult() {
g.drawImage(bg_result, 0, 0, 0);
switch (curResult) {
case 1:
g.drawImage(b_nextlevel_on,
getWidth()/2, getHeight()/2+45,
Graphics.HCENTER | Graphics.VCENTER);
g.drawImage(b_tryagain_off,
getWidth()/2, getHeight()/2+75,
Graphics.HCENTER | Graphics.VCENTER);
g.drawImage(hand_pointer,
getWidth()/5, getHeight()/2+45,
Graphics.LEFT | Graphics.VCENTER);
break;
case 2:
g.drawImage(b_nextlevel_off,
getWidth()/2, getHeight()/2+45,
Graphics.HCENTER | Graphics.VCENTER);
g.drawImage(b_tryagain_on,
getWidth()/2, getHeight()/2+75,
Graphics.HCENTER | Graphics.VCENTER);
g.drawImage(hand_pointer,
getWidth()/5, getHeight()/2+75,
Graphics.LEFT | Graphics.VCENTER);
break;
}
g.setColor(0, 0, 0);
g.drawString(" " + step, getWidth()/2, getHeight()/3 + 3, 0);
g.drawString(" " + score + " %", getWidth()/2, getHeight()/3 + 23, 0);
int keyState = getKeyStates();

70

if ((keyState & UP_PRESSED) != 0) {


if (curResult <= 1) {
curResult = 2;
}else
curResult -= 1;
}else if ((keyState & DOWN_PRESSED) != 0) {
if (curResult >= 2) {
curResult = 1;
}else
curResult += 1;
}
if ((keyState & FIRE_PRESSED) != 0) {
switch (curResult) {
case 1:
pilih = LVL_SEL;
posX = 2;
posY = 2;
stateTuas = 0;
invent = 0;
step = 0;
score = 0;
arahChar = BAWAH;
break;
case 2:
pilih = MENU;
posX = 2;
posY = 2;
stateTuas = 0;
invent = 0;
step = 0;
score = 0;
arahChar = BAWAH;
break;
}
}
}

Berikut tampilan setelah kita run pada emulator.

71

e.

Menu Exit
Buat variable parent, untuk mengakses MIDlet dari gamecanvas.
Lalu pada konstruktor, buat parameter masukan MIDlet. Pada bagian bawahnya
kita tambahkan this.parent = parent agar kita bisa mengakses fungsi fungsi dari
MIDlet dengan menggunakan parent.

public GameScreen(MIDlet parent) {


super(false);
t = new Thread(this);
g = getGraphics();
setFullScreenMode(true);
this.parent = parent;
}

72

Tentu saja, pada game MIDlet kita juga ada yang sedikit dirubah, dengan
menambahkan this sebagai parameter masukan.
protected void startApp() throws MIDletStateChangeException {
if (gScreen == null)
{
gScreen = new GameScreen(this);
}
gScreen.startGame();
Display.getDisplay(this).setCurrent(gScreen);
}

Pada case EXIT kita tambahkan fungsi parent.notifyDestroyed() agar memaksa


MIDlet menutup program kita.
case EXIT:
parent.notifyDestroyed();
break;

f.

Menu 1, 2, 3
Untuk mempermudah dalam membuat level dan menghemat resource memory,
kita akan membuat fungsi initMap(). Dimana fungsi ini akan digunakan untuk
memuat array dan menggambarnya. Pada tutorial ini aka dijelaskan bagaimana
cara memuat level map dengan 1 variable, yaitu variable mapLVL dan obsLVL.
Pada fungsi menuLevel(), pada bagian tombol FIRE ditekan, ada kode program
level = curLevel 10; kode itu berfungsi untuk memberikan nilai pada variable
level yang nantinya akan digunakan sebagai kunci pada SWITCH CASE
statement dibawah. Pada bagian akhir CASE, level akan dirubah nilainya menjadi
0, sehingga pada perulangan selanjutnya, CASE akan menjalankan kode di
DEFAULT.

public void initMap() {


switch (level) {
case 1:
mapLVL = new int[][] {
{ 1, 2, 2, 2, 2, 2, 2,
{ 4, 5, 5, 5, 5, 5, 5,
{ 4, 5, 5, 5, 5, 5, 5,
{ 4, 5, 5, 5, 5, 5, 5,

3,
6,
6,
6,

0,
0,
0,
0,

0,
0,
0,
0,

0,
0,
0,
0,

0,
0,
0,
0,

0,
0,
0,
0,

0,
0,
0,
0,

0},
0},
0},
0},

73

{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 7,
};

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

obsLVL = new int[][] {


{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 3, 3, 3, 0, 3, 3,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 3, 3, 3, 4, 0, 0,
{ 0, 0, 0, 0, 2, 0, 0,
{ 0, 0, 0, 0, 2, 0, 0,
{ 0, 0, 0, 0, 2, 0, 0,
{ 0, 9, 0, 0, 1, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
};
level = 0;
break;
case 2:
mapLVL = new int[][] {
{ 1, 2, 2, 2, 2, 3, 0,
{ 4, 5, 5, 5, 5, 6, 0,
{ 4, 5, 5, 5, 5, 6, 0,
{ 4, 5, 5, 5, 5, 6, 0,
{ 4, 5, 5, 5, 5, 6, 0,

74

6,
6,
2,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

0,
0,
2,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

0,
0,
2,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

0,
0,
2,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

0,
0,
2,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

0,
0,
2,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

0,
0,
2,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

0},
0},
3},
6},
6},
6},
6},
6},
6},
6},
6},
6},
6},
6},
9}

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
5,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
4,
2,
2,
2,
2,
2,
2,
1,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
7,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},

0,
0,
0,
0,
0,

0,
0,
0,
0,
0,

0,
0,
0,
0,
0,

0,
0,
0,
0,
0,

0,
0,
0,
0,
0,

0,
0,
0,
0,
0,

0,
0,
0,
0,
0,

0},
0},
0},
0},
0},

{ 7,
{ 0,
{ 0,
{ 0,
{ 0,
{ 1,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 7,
};

8,
0,
0,
0,
0,
2,
5,
5,
5,
5,
5,
5,
5,
8,

8,
0,
0,
0,
0,
2,
5,
5,
5,
5,
5,
5,
5,
8,

8,
4,
4,
4,
4,
2,
5,
5,
5,
5,
5,
5,
5,
8,

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

6,
6,
6,
6,
6,
6,
6,
6,
2,
5,
6,
6,
6,
6,

0,
0,
0,
0,
0,
0,
0,
0,
2,
5,
8,
0,
0,
0,

obsLVL = new int[][] {


{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 5, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 3, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 9, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
};
level = 0;
break;
case 3:
mapLVL = new int[][] {
{ 1, 2, 2, 2, 2, 2, 2,
{ 4, 5, 5, 5, 5, 5, 5,
{ 4, 5, 5, 5, 5, 5, 5,
{ 4, 5, 5, 5, 5, 5, 5,
{ 4, 5, 5, 5, 5, 5, 5,
{ 4, 5, 5, 5, 5, 5, 5,
{ 4, 5, 5, 5, 5, 5, 5,
{ 4, 5, 5, 5, 5, 5, 5,

0,
0,
0,
0,
0,
0,
0,
0,
2,
5,
8,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
2,
5,
8,
0,
0,
0,

0,
0,
0,
1,
4,
4,
4,
4,
2,
5,
8,
0,
0,
0,

0,
0,
0,
2,
5,
5,
5,
5,
5,
5,
8,
0,
0,
0,

0,
0,
0,
2,
5,
5,
5,
5,
5,
5,
8,
0,
0,
0,

0,
0,
0,
2,
5,
5,
5,
5,
5,
5,
8,
0,
0,
0,

0,
0,
0,
2,
5,
5,
5,
5,
5,
5,
8,
0,
0,
0,

0},
0},
0},
3},
6},
6},
6},
6},
6},
6},
9},
0},
0},
0}

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
7,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},

2,
5,
5,
5,
5,
5,
5,
5,

2,
5,
5,
5,
5,
5,
5,
5,

2,
5,
5,
5,
5,
5,
5,
5,

2,
5,
5,
5,
5,
5,
5,
5,

2,
5,
5,
5,
5,
5,
5,
5,

2,
5,
5,
5,
5,
5,
5,
5,

2,
5,
5,
5,
5,
5,
5,
5,

3},
6},
6},
6},
6},
6},
6},
6},

75

{ 4,
{ 4,
{ 4,
{ 4,
{ 4,
{ 7,
{ 0,
{ 0,
{ 0,
{ 0,
{ 0,
{ 0,
};

5,
5,
5,
5,
5,
8,
0,
0,
0,
0,
0,
0,

5,
5,
5,
5,
5,
8,
0,
0,
0,
0,
0,
0,

5,
5,
5,
5,
5,
8,
0,
0,
0,
0,
0,
0,

5,
5,
5,
5,
5,
8,
0,
0,
0,
0,
0,
0,

5,
5,
5,
5,
5,
8,
4,
4,
4,
4,
4,
7,

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

obsLVL = new int[][] {


{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 4, 0,
{ 0, 0, 0, 0, 0, 2, 0,
{ 0, 0, 0, 0, 0, 2, 0,
{ 0, 0, 0, 0, 0, 2, 0,
{ 0, 0, 0, 0, 0, 2, 0,
{ 0, 0, 0, 0, 0, 2, 0,
{ 0, 0, 0, 0, 0, 2, 0,
{ 0, 3, 3, 3, 3, 1, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
{ 0, 0, 0, 0, 0, 0, 0,
};
break;

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
5,
8,

6},
6},
6},
6},
6},
6},
6},
6},
6},
6},
9},
9}

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0,
4,
2,
2,
2,
2,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
4,
2,
1,
0,

0,
0,
0,
0,
0,
0,
3,
0,
0,
0,
0,
0,
0,
0,
0,
0,
3,
0,
0,
0,

0,
0,
0,
0,
0,
0,
3,
0,
0,
0,
0,
0,
0,
0,
0,
0,
3,
0,
0,
0,

0,
0,
0,
0,
0,
0,
3,
0,
0,
0,
0,
0,
0,
0,
0,
0,
3,
0,
0,
0,

0,
0,
0,
6,
0,
0,
3,
0,
0,
0,
0,
0,
0,
0,
0,
0,
3,
0,
0,
7,

0,
0,
0,
0,
0,
0,
3,
0,
0,
0,
0,
0,
0,
0,
0,
0,
3,
0,
0,
0,

0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0},
0}

Pada bagian DEFAULT dari SWITCH CASE di atas, kita akan memindahkan kode
di dalam fungsi drawMapLvl1() dan drawObsLvl1().
default:
for (int i = 0; i < mapLVL.length; i++) {
for (int j = 0; j < mapLVL[0].length; j++) {
switch (mapLVL[i][j]) {

76

case 1:
g.drawImage(tile1,
break;
case 2:
g.drawImage(tile2,
break;
case 3:
g.drawImage(tile3,
break;
case 4:
g.drawImage(tile4,
break;
case 5:
g.drawImage(tile5,
break;
case 6:
g.drawImage(tile6,
break;
case 7:
g.drawImage(tile7,
break;
case 8:
g.drawImage(tile8,
break;
case 9:
g.drawImage(tile9,
break;
}

j*16, i*16, 0);


j*16, i*16, 0);

j*16, i*16, 0);


j*16, i*16, 0);
j*16, i*16, 0);
j*16, i*16, 0);
j*16, i*16, 0);
j*16, i*16, 0);

j*16, i*16, 0);

}
}
for (int i = 0; i < obsLVL.length; i++) {
for (int j = 0; j < obsLVL[0].length; j++) {
switch (obsLVL[i][j]) {
case 1: //obs down
g.drawImage(obs_down, j*16, i*16, 0);
break;
case 2: //obs mid
g.drawImage(obs_mid, j*16, i*16, 0);
break;
case 3: //obs single
g.drawImage(obs_single, j*16, i*16, 0);
break;
case 4: //obs up
g.drawImage(obs_up, j*16, i*16, 0);
break;
case 5: //switch off
g.drawImage(switch_off, j*16, i*16, 0);
break;
case 6: //switch on

77

g.drawImage(switch_on, j*16, i*16, 0);


break;
case 7: //door close
g.drawImage(door_close, j*16, i*16, 0);
break;
case 8: //door open
g.drawImage(door_open, j*16, i*16, 0);
break;
case 9: //kunci
g.drawImage(key, j*16, i*16, 0);
break;
}
}
}
break;
}
}

Dengan begini, mapLVL dan obsLVL penggunaannya akan reuseable, seiring


dengan penambahan level. Cara penggunaannya, hanya panggil initMap() di
level utama. Kode diatas hanya membuat 3 level saja.
case LVL1:
g.setColor(115, 130, 147);
g.fillRect(0, 0, getWidth(), getHeight());
initMap();
drawChar();
movePlayer();
openObstacle(13,1);
cekPintu(6,12);
if (obsLVL[posY][posX] == 8) {
pilih = 99;
if (openLevel < 12) {
openLevel = 12;
}
score = (56*100)/step;
}
break;
case LVL2:
g.setColor(115, 130, 147);
g.fillRect(0, 0, getWidth(), getHeight());
initMap();
drawChar();
movePlayer();

78

openObstacle(9,4);
cekPintu(8,12);
if (obsLVL[posY][posX] == 8) {
pilih = 99;
if (openLevel < 13) {
openLevel = 13;
}
score = (42*100)/step;
}
break;
case LVL3:
g.setColor(115, 130, 147);
g.fillRect(0, 0, getWidth(), getHeight());
initMap();
drawChar();
movePlayer();
//openObstacle(9,4);
//cekPintu(8,12);
if (obsLVL[posY][posX] == 8) {
pilih = 99;
if (openLevel < 13) {
openLevel = 13;
}
score = (42*100)/step;
}
break;

Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code
& Assets\chapt5.2.7.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me.

5.2.8 SAVE DAN LOAD GAME


Penyimpanan data pada J2ME dapat kita lakukan dengan menggunakan Record
Management Store (RMS). Sebenarnya masih banyak teknik penyimpanan data yang
bisa digunakan, salah satunya dengan penyimpanan ke eksternal file. Tapi pada
tutorial kali ini, kita akan mempelajari bagaimana penyimpanan data dengan
menggunakan RMS.
Sekarang waktunya membuat game ini bisa di save dan di load. Pada kesempatan ini,
save dan load hanya sebatas penyimpanan data level saja, dimana dia terakhir

79

berada di level berapa. Kita akan menyimpan nilai dari openLevel yang berfungsi
untuk menentukan level mana yang sudah mainkan.
Buat variable untuk menyimpan string. Nanti akan kita gunakan sebagai nama
database kita.
//save dan load
public static final String STORE = "simpanLevel";

Lalu kita buat fungsi ambilData() untuk load data yang sudah ada di dalam RMS.
Jangan lupa kita harus meng-import package RMS. Nanti fungsi ini akan
mengembalikan nilai integer. Variable retVal untuk menyimpan nilai dari database.
static int ambilData(){
int retVal = 0;
RecordStore store = null;
try {
store = RecordStore.openRecordStore(STORE, true);
int numRecords = store.getNumRecords();
if (numRecords > 0) {
byte[] rec = store.getRecord(1);
retVal = rec[0];
}
} catch (Exception e) {
}finally{
try {
store.closeRecordStore();
} catch (Exception e) {
}
}
return(retVal);
}

Setelah itu kita akan membuat fungsi simpanData(), untuk menyimpan data
openLevel ke database. Dengan parameter masukan bertipe integer yang berfungsi
sebagai nilai yang akan disimpan ke database.
static void simpanData(int size){
RecordStore store = null;
try {
if (size > 127) {
size = 127;
}

80

store = RecordStore.openRecordStore(STORE, true);


byte[] record = new byte[1];
record[0] = (byte)(size);
int numRecord = store.getNumRecords();
if (numRecord > 0) {
store.setRecord(1, record, 0, 1);
}else {
store.addRecord(record, 0, 1);
}
} catch (Exception e) {
} finally{
try {
store.closeRecordStore();
} catch (Exception e) {
}
}
}

Lalu lakukan penambahan kode sebelum game loop (sebelum while (true)). Kita
ambil datanya terlebih dahulu lalu simpan pada variable openLevel. Cek dahulu
apakah ada datanya, kalau belum pasti bernilai 0.
if (ambilData() != 0)
openLevel = ambilData();

Kemudian untuk menyimpan openLevel, dapat kita simpan pada setiap level.
Contohnya pada kasus ini, kita akan menyimpannya pada CASE LVL1.
case LVL1:
g.setColor(115, 130, 147);
g.fillRect(0, 0, getWidth(), getHeight());
initMap();
drawChar();
movePlayer();
openObstacle(13,1);
cekPintu(6,12);
if (obsLVL[posY][posX] == 8) {
pilih = 99;
if (openLevel < 12) {
openLevel = 12;
}
score = (56*100)/step;
simpanData(openLevel);
}

81

break;

Sekarang coba jalankan emulatornya, kode diatas akan menyimpan data level yang
telah diselesaikan sebelumnya.
Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code
& Assets\chapt5.2.8.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me.

5.2.9 EFEK SUARA


J2ME menyediakan API untuk menangani sound FX dan BGM, yang sering disebut
dengan Mobile Media API (MMAPI). MMAPI di desain untuk jalan di setiap virtual
machine berbasis J2ME, termasuk CDC dan CLDC virtual machine.
MMAPI memiliki fitur:

Mendukung untuk Tone Generation, Playback, and Recording of Time-Based


Media: paket ini mendukung setiap isi audio dan video yang berbasis waktu
(time-based).
Small Footprint: MMAPI bekerja pada batas-batas memori yang tegas dari alatalat CLDC.
Protocol- and Content-Agnostic: API tidak dibiaskan ke tipe atau protokol yang
spesifik.
Subsettable: pengembang dapat mendukung tipe tertentu.
Extensible: Fitur baru dapat ditambahkan dengan mudah tanpa menghilangkan
kemampuan sistem yang sebelumnya. Lebih penting lagi, format-format
tambahan dapat dengan mudah didukung, dikbuat framework dan ditempatkan
untuk kontrol tambahan.
Options for Implementers: API menawarkan fitur untuk tujuan-tujuan yang
tertentu. API didesain dan dirancang untuk mengizinkan developer
meninggalkan beberapa fitur jika mereka tidak bisa didukung.

Ada dua bagian untuk mengolah multimedia:


82

Protocol Handling: membaca data dari suatu sumber seperti sebuah file atau
suatu streaming server ke dalam sistem media-processing.
Content Handling: menguraikan (parsing) atau decoding data dan merendernya
menjadi sebuah keluaran seperti spiker audio atau layar video.

Agar dapat memfasilitasi operasi di atas, API menyediakan dua tipe obyek high-level:

DataSource mengencapsulasi penanganan protokol dengan penyembunyian


detail bagaimana data itu dibaca dari sumbernya. Method obyek mempunyai
berguna yang memungkinkan obyek Player menangani isi.
Player membaca data dari DataSource, memprosesnya merendernya untuk
ditampilkan di piranti. Obyek ini menyediakan metoda untuk mengendalikan
media untuk playback, termasuk metoda untuk mengendalikan tipe tertentu
mengakses fitur tipe media tertentu..

MMAPI menetapkan obyek ketiga, sebuah mekanisme yang dikenal sebagai Manager
yang memungkinkan aplikasi membuat Players dari DataSources, dan dari
InputStreams.

Obyek Manager menyediakan method createPlayer(), yang merupakan level tertinggi


API untuk memasukkan input. Berikut contoh penggunaannya:

83

Player player = Manager.createPlayer(String url);


Keterangan:

url : berisi protokol dan isi url


format pengisian url <protokol>:<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

Pertama tama kita tentukan variable yang dibutuhkan.


//suara
InputStream media;
Player ambil_kunci, buka_kunci, menang, tuas;
boolean stateTuasSFX = false;

Lalu inisialisasi Player dan InputStream, tambahkan kode berikut pada fungsi init().
//Initialize sound fx
media =
getClass().getResourceAsStream("/sfx/ambil_kunci.wav");
ambil_kunci =
Manager.createPlayer(media, "audio/X-wav");
ambil_kunci.realize();
ambil_kunci.prefetch();
media =
getClass().getResourceAsStream("/sfx/buka_kunci.wav");
buka_kunci =
Manager.createPlayer(media, "audio/X-wav");
buka_kunci.realize();
buka_kunci.prefetch();
media =
getClass().getResourceAsStream("/sfx/switch.wav");
tuas =
Manager.createPlayer(media, "audio/X-wav");
tuas.realize();
tuas.prefetch();

Setelah itu, kita sisipkan pemanggilan Player pada event yang ingin kita tambahkan
suara. stateTuas berguna untuk menghandle agar sfx tidak loop terus menerus,
85

karena hero akan berada pada tile tersebut sampai dia pindah. Cek terlebih dahulu
nilai dari stateTuasSFX, jika bernilai false artinya kita akan memainkan tuas.start().
Lalu kita rubah nilai dari stateTuasSFX menjadi true. Sehingga tuas.start(0 hanya akan
di akses 1x, sampai hero pindah tile, yang akan mengakibatkan nilai dari
stateTuasSFX menjadi false kembali. Juga lakukan hal yang sama ketika hero
mengambil kunci. bedanya, tidak perlu menggunakan state, karena object kunci akan
langsung hilang dari canvas ketika hero mengambilnya.
public void openObstacle(int x, int y) {
//fungsi ngebuka obstacle
if (obsLVL[posY][posX] == 5) {
if (!stateTuasSFX) {
try {
tuas.start();
} catch (MediaException e) {
e.printStackTrace();
}
switch (stateTuas) {
case 0:
obsLVL[x][y] = 0;
stateTuas = 1;
break;
case 1:
obsLVL[x][y] = 3;
stateTuas = 0;
break;
}
stateTuasSFX = true;
}
}
//fungsi ngambil kunci
else if (obsLVL[posY][posX] == 9) {
invent++;
try {
ambil_kunci.start();
} catch (MediaException e) {
e.printStackTrace();
}
obsLVL[posY][posX] = 0;
}
else
{
if (stateTuasSFX) {
stateTuasSFX = false;

86

}
}
}

Terakhir kita tambahkan suara pada fungsi cekPintu(), yaitu ketika kita berhasil
membuka pintu.
public void cekPintu(int x, int
if (obsLVL[posY-1][posX] == 7
|| obsLVL[posY+1][posX] ==
|| obsLVL[posY][posX-1] ==
|| obsLVL[posY][posX+1] ==
if (invent != 0) {
try {
buka_kunci.start();
} catch (MediaException e)
e.printStackTrace();
}
obsLVL[x][y] = 8;
mapLVL[x][y] = 5;
invent--;
}
}
}

y) {
7
7
7) {

Jalankan emulator, coba mengambil kunci.


Untuk mengakses kode yang sampai akhir bab ini, Anda bisa load isi file "Source Code
& Assets\chapt5.2.9.zip" pada CD atau akses di http://bit.ly/agatebooks-game-j2me.

5.2.10 ENCHANCE YOUR GAME!


Selesai sudah tutorial pembuatan game ini. Game tersebut masih sangat simple.
Masih banyak hal yang harus dilakukan untuk membuat game tersebut menarik. Mari
kita buat agar game ini lebih menyenangkan. Ada beberapa aspek yang membuat
sebuah game akan menyenangkan, yaitu:

Desain level yang menarik dan menantang. Apa yang akan kurang dari game kita
sekarang? Yup, setiap level terlalu mudah. Belum ada musuh, atau teka teki
yang lebih menantang. Dan tentu saja, media sharing ke facebook dan twitter.
87

Silahkan coba kembangkan dan masukkan ide ide yang lebih fresh pada game

5.3

Pemberian background music yang tepat dan sound effect yang menarik. Tentu
saja game tanpa suara rasanya hambar.
Tentu saja, harus bugfree

LEVEL DESIGN

5.3.1 TOOLS UNTUK MEMBUAT LEVEL


Untuk membuat level, kita dapat menggunakan berbagai macam tool yang di desain
khusus untuk merancang map tile. Salah satunya adalah Tile Map Editor (TME)
v1.2.2. Aplikasi ini free dan termasuk mudah untuk penggunaannya.
Berikut tampilan awal dari TME.

88

Kita akan mencoba membuat map baru. Pilih File > New TileMap akan ada window
baru, masukkan nama map dan ukuran dari map. Map Width dan Map Height
merupakan jumlah tile secara lebar dan tinggi. Sedangkan Tile Width dan Tile Height
merupakan lebar dan tinggi dari suatu tile. Pada contoh dibawah, akan di isi Map
Width 20, Map Height 15, Tile Width 16, dan Tile Height 16. Artinya width dari map
akan 20 x 16 = 320 dan heightnya akan 15 x 16 = 240 (sesuai dengan ukuran device).

Setelah itu kita akan diminta untuk mencari file image yang akan jadi map kita.
Contohnya seperti dibawah ini:

89

Ketika TME selesai me-load, maka akan muncul tampilan seperti dibawah.

Tekan OK, maka kita dapat segera memulai mendesain map. Klik tile yang ingin
dipasangkan lalu klik pada canvasnya.
Desain sedemikian rupa sehingga menjadi seperti gambar dibawah.

90

Lalu coba export ke array, File > Export Array > Java , jika berhasil maka akan
muncul array yang dapat kita gunakan di game kita.

Dengan menggunakan tool ini, maka akan memudahkan desainer untuk merancang
level.
91

5.3.2 TIPS DALAM MEMBUAT LEVEL


Menjadi game designer adalah hal yang cukup sulit, karena seorang game designer
harus orang yang kreatif dan inovatif. Beberapa tips untuk mendesain level:
a.
b.
c.
d.

Buat level mudah dipelajari tapi sulit untuk mencapai best score. Juga buat
level tutorial untuk memudahkan player untuk mengerti cara bermainnya.
Jangan terlalu monoton, tambahkan tingkat kesulitan yang bertahap dari
mudah ke susah.
Tambahkan sound effect dan background music agar player tidak bosan.
Setiap level selalu ada tantangan tersendiri. Misalkan pada game yang telah
kita buat di bab sebelumnya, agar lebih menantang kita dapat
menambahkan AI di levelnya.

Masih banyak aspek yang dapat mempengaruhi agar sebuah game dapat menarik.
Misalnya kita ingin menargetkan gamer yang hanya ingin mengisi waktu, kita dapat
membuat game simple tapi dengan menambahkan fitur share highscore, atau game
yang kompleks seperti game RPG untuk gamer yang memang hobi bermain game
RPG.

92

93

Agate Studio is now hiring!

6
6.1

RELEASE
DIMANA KITA BISA RILIS GAME KITA?

Banyak app store yang siap untuk menampung game kita. Salah satu yang terbesar
dan terbanyak usernya adalah Nokia Store.
Dengan hanya membayar 1 euro, kita akan memiliki akun di Nokia Ovi Store.
Keunggulan Ovi Store dibandingkan app store lainnya:
a.
b.
c.
d.

6.2

Pengguna Nokia yang cukup banyak dari Indonesia khususnya.


Banyak tipe handphone Nokia yang telah beredar dipasaran, sehingga
memperbesar peluang kita.
Pengguna Nokia dimudahkan dalam membeli aplikasi di Ovi Store, sehingga
memperbesar kemungkinan pengguna Nokia untuk membeli game kita.
Sistem QA yang bagus, game kita akan melalui serangkaian tes sehingga game
kita benar benar siap dipasarkan.

CARA PUBLISH GAME DI NOKIA STORE

Untuk mempublish game ke Nokia Store, caranya cukup mudah, berikut adalah step
by step:
1.

2.

94

Daftarkan diri atau tim anda di Nokia Store sebagai publisher, melalui tautan
berikut https://publish.ovi.com/register/country_and_account_type masukkan
jenis akun yang ingin didaftarkan, corporate ketika kita ingin mempublish game
atas nama perusahaan atau tim. Lalu isi informasi yang dibutuhkan selanjutnya
sampai pada bagian pembayaran dengan menggunakan credit card. Biaya untuk
pendaftaran sebagai publisher hanya 1 Euro atau sekitar Rp. 12.000,00.
Setelah mendaftar menjadi publisher, kita dapat login ke http://publish.ovi.com/
untuk mensubmit game kita. Kita juga diberi page oleh Nokia untuk
menampilkan game game yang telah kita publish. Contohnya page Agate

3.

Studio http://store.ovi.com/publisher/Agate Studio/ disana terdapat berbagai


macam game yang telah dikembangkan oleh Agate Studio.
Mari kita mencoba membuat content baru, pilih Java Application ketika
ditanyakan tipe content kita.

4.

Lalu kita akan diberi pertanyaan tentang kelegalan dari game kita.

5.

Setelah itu kita akan diminta untuk mengisi metadata. Mulai dari category, nama
aplikasi, harga aplikasi, dan sebagainya.

95

6.

Kita juga akan diminta screenshot dan logo dari game kita.
Setelah berhasil, kita akan ke halaman berikut.

7.

Setelah itu kita pilih Content Files untuk mengunggah file game kita.

96

Ada beberapa ketentuan sebelum kita mengunggah game kita, diantaranya


game kita sizenya harus kurang dari atau sama dengan 2MB. Lalu nama filenya
harus 5 48 character. Gambar dibawah adalah ketentuan dalam mengunggah
game kita. Jangan lupa untuk mengubah file Application Descriptor pada project
game kita. Beberapa yang perlu dirubah yaitu Overview dan MIDlets. Untuk
membuild game kita, klik kanan pada project kita. Lalu pilih Mobile Tools for Java
> Create Package. Setelah itu, kita dapat mengambil file JAR kita di folder project
kita > deployed.

97

8.

98

Setelah selesai mengunggah, kita akan berada pada page Distribution. Disini kita
akan memasukkan kompatibilitas game pada tipe handphone yang tersedia
(usahakan ada 1 device yang fully tested, jika fully tested tidak lolos QA maka
device lain pun akan gagal). Lalu memasukkan Negara dimana game kita akan
dipasarkan, terakhir kita akan memasukkan bahasa yang digunakan pada game
tersebut.

9.

Langkah terakhir, kita Submit to QA. Disini kita akan menunggu tim Quality
Assurance Nokia untuk memeriksa game kita. Setelah submit, kita dapat melihat
progress QA kita.

10. Jika berhasil lolos pada QA, kita dapat melihat game kita di Nokia Store.
Contohnya seperti ini, http://bit.ly/KWUhBh, game Sexy Witch dari Agate Studio.
99

100

PENUTUP

Kami harap penjelasan yang ada di buku ini bisa membantu pembaca dalam
membuat mobile game, dan semoga bisa memberi pengalaman yang bermanfaat.
Jika Anda tertarik untuk mengetahui lebih lanjut tentang game development, baik
teknis atau non-teknis, Anda bisa kunjungi blog Agate Studio di
www.agatestudio.com/blog. Lalu jika tertarik untuk mengikuti berbagai pelatihan
Game Development, Anda bisa kunjungi website Agate Academy via
http://bit.ly/agateacademy.

101

LAMPIRAN A. MEMBUKA PROJECT PADA ECLIPSE PULSAR


Buku ini disertai dengan sebuah CD yang berisi file-file yang dibutuhkan untuk
mengikuti tutorial pada buku ini. Jika CD tidak tersedia, Anda bisa akses
http://bit.ly/agatebooks-game-j2me untuk mengunduh file-file yang dibutuhkan.
Pada folder Source Code & Assets terdapat beberapa file *.zip untuk setiap bab
pada bagian pemrograman.

Untuk membuka project di tiap file Anda perlu ekstrak file zip yang bersangkutan.

102

Buka Eclipse, lalu pilih File > New > MIDlet Project. Isi nama project sesuai kebutuhan
Anda. Pada bagian Contents, pastikan Anda memilih Create project from existing
source.

103

Lalu pilih folder hasil ekstrak yang sudah dibuat.

104

Jika berhasil, pada Eclipse akan muncul project baru dengan folder res dan src
yang sudah berisi file yang dibutuhkan.

Untuk menjalankan project, klik kanan pada nama project > Run As > Emulated Java
ME MIDlet.
105

Berikut adalah contoh hasil akhir dari bab 5.2.2.

106

Anda mungkin juga menyukai