05/2009

CODING

KNOW-HOW

89

MEMBUAT GAME STRATEGI SEDERHANA MENGGUNAKAN JAVA DAN NETBEANS
BAGIAN 1 DARI 3 ARTIKEL
Dalam tiga seri ini, kita akan belajar untuk membuat game strategi “Devilish Children” dengan menggunakan bahasa pemrograman Java.
Windra Swastika & M. Fauzil Haqqi

Sekilas Java dan NetBeans
Java adalah bahasa pemrograman tingkat tinggi yang dikembangkan oleh Sun Microsystems. Bagi Anda yang sudah pernah menggunakan bahasa C atau C++, tentu tidak akan asing dengan sintaks bahasa Java. Hanya saja, pemrograman bahasa Java berorientasi pada object atau yang biasa disebut dengan OOP (Object Oriented Programming). Kelebihan dari program yang dibuat dengan menggunakan bahasa Java adalah dapat berjalan pada platform yang berbeda, seperti mottonya, “Write Once, Run Anywhere”. Dalam artikel ini, kita akan menggunakan Java SE versi 6. Versi terbarunya dapat di-download di http://www.java.com. NetBeans adalah IDE (integrated development environment), yaitu sebuah program yang berfungsi sebagai alat bantu bagi programer untuk mempermudah pembuatan program. Semua proses membuat program mulai dari menulis, compiling, debugging, dan packing program dilakukan dengan IDE ini. Dalam artikel ini, penulis mengunakan NetBeans 6.5. Versi terbaru NetBeans dapat diunduh di http://www.netbeans.org.

memiliki atribut dan method masing-masing. Sebagai contoh, dalam game yang akan Anda buat ini akan ada karakter atau monster yang dijalankan. Setiap monster memiliki HP, attack, dan status lainnya. Itulah yang disebut sebagai atribut. Monster tersebut juga bisa berpindah tempat atau menyerang. Kemampuan itu yang disebut method.

Konsep Pemrograman pada Java
Seperti yang telah disinggung sebelumnya, Java adalah bahasa pemrograman OOP. Artinya, semua bagian yang terlibat dalam program direpresentasikan sebagai object. Setiap object akan
Tampilan Final Fantasy Tactic 2, salah satu game strategi pada platform Nintendo DS.

90

KNOW-HOW

CODING

05/2009

Object sendiri merupakan representasi dari suatu class. Ibaratnya class adalah cetak biru (blue print) dari suatu object. Artinya, class akan mendefinisikan atribut dan method yang dimiliki, tanpa harus menentukan nilainya. Class inilah yang akan kita buat dan kita simpan dalam bentuk file java.

Devilish Children
Jika Anda suka bermain game sejenis Grandia, Final Fantasy Tactic, dan Romance of Three Kingdom, pasti tidak akan asing dengan game yang akan Anda coba buat ini. Persamaan dari game tersebut sistem battle antara dua pemain dalam suatu arena yang berpetak-petak. Setiap pemain memiliki setidaknya satu buah karakter yang dapat dimainkan. Karakter tersebut dapat berpindah tempat, menyerang, ataupun menunjukkan kemampuan lainnya. Arena pertarungannya pun bisa bergeser mengikuti kursor yang digerakkan. Bagaimanakah cara pembuatannya? Dalam kesempatan kali ini, penulis akan mencoba membahas salah satu game sejenis yang dibuat oleh penulis, yaitu Devilish Children. Game ini dibuat dengan tujuan sebagai dasar pembuatan game strategi tingkat yang lebih tinggi. Bagian yang dibuat pun hanya pada sesi battle saja. Penulis akan berusaha menjelaskan cara pembuatannya sedetail mungkin, namun penulis menyarankan bahwa sebaiknya Anda mengetahui dasar-dasar pemrograman bahasa Java terlebih dahulu. Semua source termasuk file gambar yang akan digunakan dalam artikel ini juga dapat di-download di blog penulis, dengan alamat http://fauzilhaqqi.blogspot.com.

Untuk membuat project baru, klik menu File à New Project atau klik icon New Project pada Toolbar. Pada jendela New Project, pilih kategori Java dan project Java Application, lalu klik Next. Isi Project Name dengan nama DevilishChildren lalu atur lokasi project dan klik Finish. Secara otomatis, NetBeans akan membuat beberapa subfolder dalam folder DevilishChildren, yaitu nbproject, src, dan test. Folder yang akan sering Anda buka melalui explorer adalah folder src, sebab folder inilah yang menjadi tempat semua file source Anda di simpan. Untuk membuat struktur penempatan lokasi file yang baik, sebaiknya Anda menambahkan beberapa folder lagi. Folderfolder tersebut adalah chara, images, dan map yang akan diletakkan di dalam folder src/devilishchildren. Semua folder tersebut digunakan untuk menyimpan file gambar yang akan digunakan dalam pembuatan game. Folder chara untuk gambar karakter, folder images untuk gambar-gambar umum, dan folder map untuk gambar petak-petak map. Selain itu, buat juga folder chara dan map langsung di bawah folder utama, DevilishChildren. Gunanya adalah untuk menyimpan file yang berisi detail dari sebuah karakter dan map. File ini akan dijelaskan kemudian. Untuk lebih jelasnya tentang penempatan folder, lihatlah pada gambar.
Catatan: Anda bisa membuat sendiri file gambar yang akan digunakan, namun Anda juga bisa menggunakan semua file gambar yang ada dalam blog penulis untuk memudahkan pembuatan game ini.

Class Dasar yang Digunakan
Game ini membutuhkan beberapa class dasar yang harus ada. Masing-masing class memiliki fungsinya sendiri. Untuk membuat class baru, klik File à New File atau klik icon New File pada Toolbar. Pada jendela New File, pilih kategori Java dan tipe file Java Class. Setelah itu, klik Finish. Secara umum, hierarki sebuah class adalah seperti pada listing a. Untuk mempermudah pembacaan, sebaiknya Anda letakkan semua atribut dan variable global di atas constructor. Constructor adalah method yang memiliki nama sama dengan class-nya dan yang akan dijalankan pertama kali saat Anda membuat object dari class tersebut. Sedangkan, method-method lainnya diletakkan di bagian bawah. Sebagai langkah awal, buatlah semua class yang ada di bawah ini.

Aturan Main Game (Game Rules)
Karena game ini hanya sebagai dasar pembuatan game strategi yang sesungguhnya, kita tidak akan membahas terlalu dalam masalah game rule. Sama dengan game tactic yang lain, game ini lebih ke arah Turn-Based Strategy. Artinya, setiap pemain bergantian memainkan karakter masing-masing yang ada dalam arena permainan. Arena permainannya sendiri berupa petakpetak hexagonal (segi-6). Game ini didesain untuk 2 orang pemain. Setiap pemain memiliki satu karakter yang disebut Hero. Pemain pertama menggunakan Hero bernama Frost (warna biru), sedangkan pemain kedua menggunakan Flare (warna merah). Hero ini memiliki kemampuan untuk memanggil (summon) monster ke arena battle. Untuk memanggil setiap monster membutuhkan Mana yang cukup dari hero tersebut. Jumlah maksimal monster yang dapat dipanggil sesuai dengan jumlah kastil yang dimiliki. Pemain yang mengalahkan Hero lawan terlebih dahulu adalah pemenangnya. Game rules lainnya akan dijelaskan lebih lanjut.

Main
Class ini akan secara otomatis dibuat oleh NetBeans saat Anda membuat project baru. Semua program pasti harus memiliki class ini karena class ini adalah class yang kali pertama dijalankan saat Anda menjalankan program.

Membuat Project Baru dan Mengatur Folder
Sebelum mulai membuat program dengan NetBeans, pertama-tama Anda harus membuat project baru dan mengatur folder-folder yang menjadi tempat penyimpanan semua file Anda.

GameManager
Class ini merupakan inti dari game ini karena class ini menyimpan semua informasi umum yang berhubungan dengan game serta mempunyai method-method dasar yang banyak digunakan dalam game.

Penempatan folder source.

maka program tidak akan mengenali class yang sedang digunakan. images. Anda bisa melakukannya lebih cepat tanpa perlu menuliskan statement import terlebih dahulu. Sehingga Anda cukup mengeset beberapa atribut dan menambahkan kode program yang Anda inginkan. sebenarnya adalah object yang dibuat dari class Toolkit dari library Java. Semua hal yang terdapat di panel info akan diletakkan di class ini. akan muncul gambar lampu di sebelah kiri baris tersebut.05/2009 CODING KNOW-HOW 91 TIPS: IMPORT LIBRARY JAVA DENGAN NETBEANS Pada saat anda menuliskan public Toolkit. Semua hal yang terdapat di panel map akan diletakkan di class ini. kursor. maka akan muncul beberapa pilihan seperti pada gambar di bawah ini: dapat mengenal lokasi tersebut. method akan mengembalikan nilai ke pemanggilnya. Anda harus membuat object dari class GameManager. Sebuah jendela tentu memiliki atribut panjang dan lebar. Anda tidak perlu menggunakan class ini secara langsung. Agar program Tombol Run Main Project. HexaMap Class ini menyimpan informasi dari suatu map. Jika tidak. Setelah itu. Di antaranya. GameFrame juga bertindak sebagai penghubung kedua panel ini. imagesFolder.Toolkit. GameFrame Class ini adalah class yang menjadi window dari program tempat semua hal ditampilkan. Pada method loadImage(). karena class JFrame terletak pada library Java. dan map. buatlah variable yang berisi alamat folder tersebut. dengan ukuran 800x600. Anda cukup mengatur agar class GameFrame milik Anda inherit pada class JFrame. Seperti yang telah dijelaskan sebelumnya. Pada listing c. Informasi yang disimpan antara lain adalah gambar setiap petaknya dan karakter yang ada di tiap petak. . class ini berisi variable dan method yang akan digunakan secara global. Membuat Jendela Game Tahap berikutnya adalah membuat jendela game menggunakan class GameFrame. objek img menyimpan alamat tempat file gambar disimpan. Dalam library Java telah tersedia class JFrame yang merupakan class dasar untuk membuat jendela game. Nilai tersebut berupa data gambar yang telah di-load. Chara Class ini sebagai representasi dari sebuah karakter. informasi karakter dan menu-menu yang digunakan. Catatan: Semua penggunaan library Java harus diikuti proses import. Klik pada pilihan Add import sesuai library yang ingin di-import. kita perlu meng-import class tersebut dengan cara menuliskannya di bagian atas class. Setelah itu. Dengan menggunakan NetBeans. Class GameManager Kita mulai penulisan program pada class GameManager. Caranya adalah dengan menambahkan kata “extends JFrame” di sebelah kanan nama class GameFrame. Informasi yang disimpan tentu saja meliputi atribut dan method yang dimiliki tiap karakter. Karena game ini nantinya tidak hanya berjalan InfoPanel Bagian window ini terletak di bagian bawah. Variable yang terletak paling atas. lakukan pengaturan ukuran dengan memanggil method setSize() di dalam constructor. Pada langkah sebelumnya Anda telah membuat folder yang nantinya akan menjadi lokasi penyimpanan gambar. yaitu panel map dan panel info. dan mapFolder dengan tipe data String. Pembuatan jendela ini sangat penting karena semua bagian game akan ditampilkan dalam jendela ini. yaitu: import java.awt. atribut tersebut didefinisikan dalam variable frameWidth dan frameHeight. MapPanel Bagian dari window yang menampilkan area permainan dibuat dengan class ini. Cara ini juga bisa digunakan untuk proses import yang lain. dan monster atau karakter yang digunakan. Langkah Awal. Dalam class ini akan diletakkan 2 buah panel. Setelah itu. Coba Anda lihat pada listing b. Klik gambar lampu itu. Tapi. Anda perlu melakukan sedikit penghitungan. Jangan lupa untuk melakukan proses import. Agar file tetap dapat dibaca meskipun folder utama berpindah-pindah. Dalam listing b. Karena termasuk dalam library Java. Secara otomatis NetBeans akan menuliskan statement import di bagian atas class. yang nantinya digunakan untuk load gambar. yaitu chara. salah satunya adalah method untuk membuka file gambar. diantaranya adalah untuk load file gambar yang akan Anda lakukan dengan method loadImage(). Untuk mengatur agar lokasi munculnya jendela tepat di tengah layar.getResource() dengan parameter folder dan nama file. class anda akan mewarisi semua atribut dan method dari class JFrame. Bacalah tips cara import library java dengan netbeans. Cara cepat import dengan NetBeans. Anda bisa menggunakan method getClass(). variable-nya ditulis dengan nama charaFolder. Artinya. Kegunaan class Toolkit ini sangat banyak. seperti yang terlihat pada listing c. diantaranya adalah map.

Ketikkan definisi tersebut dalam Setelah membuat object. Seperti yang telah dijelaskan sebelumnya. Setelah class GameFrame sederhana telah dibuat. apa saja yang ada dalam arena pertarungan. Anda perlu membuat tengah layar. Tapi. berarti program Anda masih kurang disimpan. method untuk load map tersebut. Method setImage() digunakan untuk menyimpan gambar. Untuk memperindah tampilan jendela. maka nilai default-nya adalah folder utama. Anda juga harus memanggil text editor. Class MapPanel Class ini akan menampilkan semua hal yang terjadi di arena pertarungan. Anda tinggal membuat method untuk akan berhenti. Kedua panel tersebut memegang peranan penting. Setelah mendapatkan ukuran layar. agar ukuran jendela tidak dapat diubah. Anda perlu untuk mengetahui ukuran layar monitor tempat game ini dijalankan. Anda harus mengatur definisi dari sebuah dari class GameFrame di dalam class Main. panel-panel tersebut dibuat dengan class MapPanel dan InfoPanel. yang dibutuhkan. Anda bisa menggunakan array dua dimensi dari object tileImage yang dibuat dari class Image dari library Java sebagai tempat penyimpanan gambar. membaca file tersebut dan membuatnya menjadi Untuk menjalankan program. setidaknya menyimpan informasi gambar dari map tersebut. Pembagian panel dalam . Index dari array tersebut yang nantinya digunakan sebagai koordinat. yaitu sebagai container atau tempat semua object lain berinteraksi. Coba Anda berpikir sejenak. Pemanggilan method setTitle() digunakan untuk mengganti judul jendela. Definisi ini sebaiknya diletakkan Untuk lebih jelasnya. Anda tinggal menghitung lokasi dengan rumus seperti pada listing. Setelah itu. false dan jendela tidak akan ditampilkan lalu program Setelah itu. Anda bisa mengganti icon jendela dengan memanggil method setIconImage(). Jadi. Parameter yang dibutuhkan adalah sebuah gambar. Sebuah map sederhana. arena pertarungannya berupa petak-petak heksagonal. Jika dalam folder map yang langsung berada di bawah tidak mengeset true. seperti notepad. bagaimana object dari class HexaMap nantinya tepat. Karena map ini berjenis tile map. Sebelum menjalankan program. songan. lihat listing d. Anda juga bisa menggunakan shortcut Ukuran dari tile segi-6 dibuat oleh penulis. berarti Anda sudah melakukannya dengan benar.92 KNOW-HOW CODING 05/2009 Membuat Panel Secara garis besar. Listing g di bawah Project berbentuk segitiga berwarna hijau yang ada ini adalah salah satu contoh method loadMap() yang pada toolbar. bagian atas yang menampilkan arena pertarungan dan bagian bawah yang menampilkan infoinfo sesuai event yang terjadi. Anda harus membuat arena pertarungannya dahulu. Anda perlu membuat object Sebelum itu. bukan di dalam folder src. sebelum Anda membuat MapPanel. Untuk membuat kedua class dari panel tersebut. Anda bisa Class HexaMap yang Anda buat masih dalam bentuk komulai melakukan tes sederhana dengan menjalankan program. tempat menampilkan gambar dalam game ini terbagi menjadi dua bagian. Sebagai contoh. Untuk itu. panggil juga method setResizeable() dengan parameter false. Artinya. Class HexaMap . Hal terpenting yang harus ada adalah arena pertarungan itu sendiri. Atribut lain yang dimiliki adalah panjang dan lebar arena yang bisa didapatkan dengan method getWidth() dan get Height() yang mengemMencoba Menjalankan Program balikan ukuran array. lihatlah method setVisible() dari object tersebut dengan papada listing f. Untuk mendapatkan ukuran tersebut.dc” di ini akan membuat jendela dapat ditampilkan. kita menggunakan object kit dari class Toolkit yang ada pada object gm. sedangkan method getImage digunakan untuk mengambil gambar yang ada pada koordinat tertentu. Jika jendela berhasil ditampilkan dan terletak tepat di mendapatkan gambar tersebut? Untuk itu. di satu komputer saja. termasuk castle. terpisah dalam sebuah file. sedangkan method setDefaultCloseOperation() akan membuat program otomatis berhenti jika jendela tertutup. anggap saja ada 5 lah yang akan dijalankan kali pertama saat program dijalankan. Seperti yang dijelaskan sebelumnya. Anda perlu membuat dua buah panel yang berbeda. klik icon Run Main data dari object class HexaMap. Dengan parameter true artinya method Simpan file tersebut dengan nama “Map. Sebab class Mainmap. artinya sama sekali belum ada informasi gambar yang Jika jendela tidak tampil. Untuk lebih jelasnya. Anda tinggal inherit pada class JPanel yang tersedia dalam library Java. Arena pertarungan dibuat dengan menggunakan class HexaMap. jenis tile. Anda perlu memanggil method loadImage() dari object gm untuk mengambil gambarnya. yaitu dengan memanggil method getScreenSize() yang diikuti dengan mengambil nilai panjang dan lebarnya. Buatlah method loadMap() tersedengan menekan tombol F6. Lalu aturlah lokasi jendela dengan memanggil method setLocation(). rameter true. coba Anda lihat pada listing e.

Method tersebut harus Override dengan method aslinya yang dimiliki class JPanel. tapi terletak pada satuan p. di dalam constructor Anda harus memanggil method loadMap() dari object gm untuk inisialisasi object map. lokasi pixel X. cari method paintComponent() pada kategori JComponent. Teknik ini akan dijelaskan kemudian. Anda tinggal mengubah nilai variable tersebut.getProperty(“user. Method ini menggunakan metode pembacaan file yang sudah Anda ketik sebelumnya. Setelah itu. Untuk mendapatkan lokasi Y sangat mudah. pertama-tama deklarasikan object dari masing-masing panel. Java menampilkan gambar dengan satuan pixel. Parameter standar yang dibutuhkan berturutturut adalah gambar. setelah itu buatlah class MapPanel seperti pada listing h. Anda perlu mengerti bagaimana konsep menampilkan arena yang berupa segi-6 beserta sistem koordinatnya. Tapi untuk apa nilai p tersebut? Setelah mendapatkan nilai p. Pada baris kedua. Tulislah seperti listing h dan pahami sampai Anda mengerti. untuk mulai menggambar petak pada panel. Coba Anda lihat pada gambar segi-6 dengan lebar sebesar r. Tapi hati-hati. karena di dalam method ini banyak mengandung library Java yang belum Anda import sebelumnya. untuk melihat apakah hasilnya sesuai Anda perlu memasang panel-panel itu ke dalam class GameFrame. Tapi tidak demikian dengan lokasi X seperti yang telah Anda lihat pada gambar sistem koordinat sebelumnya. sehingga akan terasa lebih mudah. setiap huruf dalam file tersebut mewakili setiap petak sesuai koordinatnya. yaitu 180 pixel. Dalam class ini. Sebelum itu. Baris berikutnya tidak tepat di bawah batas tinggi hexagonal di atasnya. dan observer. Menampilkan Arena Langkah berikutnya adalah menampilkan arena pada jendela. karena bentuk map segi-6 tidak simetris seperti halnya map persegi. Gambar kursor disimpan dalam object curImg. Terlihat ada perbedaan letak baris pertama dengan berikutnya. itulah guna dari nilai p. Gunanya adalah menangkap kesalahan-kesalahan program yang disebabkan masalah inputoutput. dan lokasi kursor disimpan dalam variable curPosX dan curPosY. Caranya. Tambahkan kode di bawah . Untuk itu kita menggunakan method tileXToPixel() untuk mendapatkan nilai lokasi X sesuai nilai Y-nya. Method System. Setelah itu.05/2009 CODING KNOW-HOW 93 Sistem koordinat map segi-6. Shorcut-nya adalah tekan Alt+Insert. Anda harus menentukan tinggi panel sesuai ukuran gambar dari panel info. lokasi pixel Y. ada sedikit perhitungan kecil untuk mendapatkan nilai p yang nanti akan digunakan untuk mengatur letak petak-petak sesuai koordinatnya. Variable r adalah lebar dari gambar tersebut. Jangan lupa juga untuk mengatur warna background dari panel ini. Anda perlu membuat method paintComponent(). selama file yang digunakan masih lengkap. but di dalam class GameManager. Anda tinggal meletakkannya sesuai koordinat tiap petak. Bentuk awal class ini adalah seperti pada listing i. Jangan lupa juga untuk melakukan proses import seperti sebelumnya. Gambar yang perlu ditampilkan tidak terlalu banyak. Anda perlu memberikan statement try dan catch IOException di bagian terluar. Coba Anda lihat pada gambar sistem koordinat map segi-6. kita bisa mendapatkan nilai tinggi dan lebar gambar dengan mudah.dir”) akan mengembalikan nilai berupa alamat folder dimana program berjalan. Untuk menampilkan gambar pada panel. lokasi X sedikit berubah. Namun. Listing h tersebut sekaligus menampilkan kursor yang nantinya akan digunakan dalam permainan. Class InfoPanel Cara pembuatan panel ini tidak jauh berbeda dengan panel sebelumnya. Menambahkan Panel ke GameFrame Setelah Anda membuat kedua panel tersebut. Setelah jendela Override Method muncul. ganjil atau genap. Anda cukup memanggil method drawImage() dari object g yang diletakkan dalam perulangan for. cukup mengalikan koordinat dengan nilai p. Dari suatu file gambar. dalam hal ini contohnya adalah file yang digunakan tidak dapat ditemukan atau dibuka. Coba Anda pahami terlebih dahulu sistem koordinat tersebut. pilih Insert Code lalu klik pada pilihan Override Method. Cara mudah untuk membuatnya adalah klik kanan pada editor. Object yang digunakan untuk membaca file adalah reader dari class BufferedReader. Perbedaan dengan class MapPanel adalah pengaturan ukuran panel. Sebelum mulai menampilkan gambar. Dengan sedikit perhitungan trigonometri. Variable tileOffsetX dan tileOffsetY digunakan sebagai titik awal penggambaran pada jendela. object dari class HexaMap harus dibuat terlebih dahulu. Setelah itu. Gunanya adalah untuk membuat program bisa dijalankan di folder manapun meskipun dipindah-pindah. object infoSize digunakan untuk menyimpan dimensi ukuran yang akan dimasukkan sebagai parameter saat memanggil method setPreferredSize(). Nah. Gunanya adalah pada saat Anda membutuhkan proses scrolling (penggeseran) map. Jadi. maka akan ditemukan nilai p adalah ¾ dari tinggi segi-6.

} } GameFrame gf = new GameFrame(). import java. scrolling map.awt. //variabel lainnya Listing c package devilishchildren. ip = new InfoPanel(). } setDefaultCloseOperation(JFrame . karena class itu baru saja digunakan pada penambahan kode ini. BorderLayout. inisialisasi object tersebut pada bagian paling atas dari constructor class GameFrame dengan menambahkan kode di bawah ini: mp = new MapPanel(). public static final int frameHeight = 600. public static final String imagesFolder = “images/”.kit. Panel map diletakkan pada layout tengah. add(ip. Method setLayout() akan mengatur layout dari jendela. Pada bagian selanjutnya penulis akan membahas tentang bagaimana cara menjalankan kursor. setLayout(new BorderLayout()).URL.getScreenSize(). } } //nama class public class Coba { //tempat atribut String nama. import java.getImage(img).loadImage(GameManager . setLocation((scrWidth . setIconImage(gm. . public static final String mapFolder = “map/”.imagesFolder. BorderLayout. (scrHeight . sedangkan panel info pada layout bawah. gf. serta menambahkan karakter dan menjalankannya.94 KNOW-HOW CODING 05/2009 ini pada tempat variable global di class GameFrame. public class GameManager { public final Toolkit kit = Toolkit.height. setResizable(false).width. private GameManager gm = new GameManager().net. dalam hal ini kita menggunakan BorderLayout.awt. import javax. setTitle(“Devilish Children”). “icon. add(mp. String name) { URL img = getClass() . private static MapPanel mp.SOUTH).frameWidth)/2.getResource(folder + name). import java. public GameFrame() { setSize(frameWidth. //method public void serang() { } //method lainnya } private final int scrWidth = gm. return kit. public class GameFrame extends JFrame { //constructor public Coba() { } public static final int frameWidth = 800. private static InfoPanel ip. Listing d package devilishchildren.getScreenSize().getDefaultToolkit().CENTER).JFrame. jika berhasil maka akan muncul tampilan seperti pada contoh sebelumnya.EXIT_ON_CLOSE). } public static final String charaFolder = “chara/”. private final int scrHeight = gm.setVisible(true).Toolkit.frameHeight)/2).Image.kit. Jangan lupa untuk melakukan proses import pada class BorListing a //nama package package devilishchildren. frameHeight). Coba Anda jalankan program Anda. public class Main { public static void main(String[] args) { public Image loadImage( String folder.swing. Penulis juga akan membahas pembuatan program untuk menerapkan aturan main dalam game ini. derLayout. Setelah itu.png”)). Listing b package devilishchildren.

line. y++) { String line = (String)lines. public void setImage(int x.length()).charAt(x). //1 import java. public int getHeight() { return tileImage[0]. for(int x = 0. x++) { char ch = line. for(int y = 0. Listing g-Load Map public HexaMap loadMap(String name) { ArrayList lines = new ArrayList(). height). “tileForest.05/2009 CODING KNOW-HOW 95 Listing e-HexaMap 1 package devilishchildren.size(). catch (IOException iOException) { Listing f-Definisi Map Map percobaan Devilish Children #Baris yang diawali tanda # adalah komentar #Detail: #F = Forest #G = Grass #S = Sand #W = Water #C = Castle FFFFGGGGGFFFFSSSSSWFWWWFFF FFCGGGGFFFCSSSSSWWWWWGGGCS GGGGSSSSFFWWWWSSSSFWWGGFFF GFSSSSSCFFFWFSCSSGGCGGGFFF GSSFFFFFGGFFFFSSSSSSFGGFFF SSSCSFFFFFFFFFFGGGGGGGCGFG WFWWFFFFFFFWCWWWSSSSFGGFFF WWSSSFFFGGCGWCFFSSSGGGGFFF WSSWWWFFFFGGCSGGGGSSWWWSSF WWWSFFFGFGWWWWSSSSSSSSSFFS FFFFGGGCWSSSSGGGGGCWFWWSSF } System. int width = 0. public int getWidth() { return tileImage. Image img) { tileImage[x][y] = img. break. “tileWater. case ‘W’: index = 3.Image.png”). } while (true) { String line = reader. loadImage(mapFolder. loadImage(mapFolder. } String path = System.startsWith(“#”)) { lines. int y. “tileSand.length. public Image getImage(int x.getCause()). //0 loadImage(mapFolder. Image[] tileImage = { loadImage(mapFolder. int y) { return tileImage[x][y].length. “tileGrass.get(y). //3 public class HexaMap { private Image[][] tileImage.readLine(). int height) { tileImage = new Image[width][height]. case ‘G’: index = 1.png”). if (line == null) { reader.println(iOException. } } } } } width = Math.png”).max(width. case ‘S’: index = 2. int index. break. “castleGray. } try { BufferedReader reader = new BufferedReader( new FileReader(new File(path))).dir”) + File.separatorChar + mapFolder + name. break.getProperty(“user. HexaMap newMap = new HexaMap(width. y < height. if (!line.png”). case ‘C’: .awt. x < width. } } break. height = lines. }.add(line). //2 loadImage(mapFolder.close(). int height = 0. break. switch(ch) { case ‘F’: index = 0.out.png”) //4 public HexaMap(int width.

} } return newMap. public int tileXToPixel(int x.paintComponent(g).awt.getImage(x.Color. private int tileOffsetY = 0.swing. import java. import javax. private Image curImg = gm. public static final int infoHeight = 180.Dimension. private final int r = 56. LEBIH LANJUT Davidson.awt.JPanel. tileXToPixel(curPosX. public InfoPanel() { public MapPanel() { map = gm.png”). } } @Override protected void paintComponent(Graphics g) { super. } newMap. private final int p = 64 * 3 / 4. if(y % 2 != 0) { width += r / 2.96 KNOW-HOW CODING 05/2009 index = 4. private final int margin = 20.BLACK).Image. for(int x = 0. y. May 2005. infoHeight). private HexaMap map. package devilishchildren. curPosY * p + tileOffsetY * p import java.dc”). public class InfoPanel extends JPanel { private GameManager gm = new GameManager(). this). import java. g. y) + tileOffsetX * r + margin.JPanel. pixelY.net Java Game Programming Tutorial. private Dimension infoSize = new Dimension(). y). 0.drawImage(map.loadImage( GameManager.drawImage(curImg. Andrew.getWidth(). x++ ) { int pixelX = tileXToPixel(x. } @Override protected void paintComponent(Graphics g) { super.Graphics. Killer Game Programming in Java.awt. “info. break.awt. setBackground(Color.loadImage( private int tileOffsetX = 0. Listing h-Map Panel package devilishchildren. } } } + margin. int y) { int width = x * r. break.imagesFolder.net Forum.paintComponent(g).awt.awt.de for(int y = 0. import java. import java. 0. gmxhome. private int curPosY = 0.javacooperation.frameWidth.png”). default: index = 0.setSize(GameFrame. O’Reilly. Java. www. import java. g. http://forums.swing. this). } } + margin . private int curPosX = 0. “cursor. Listing i-Info Panel public class MapPanel extends JPanel { private GameManager gm = new GameManager().Image. import javax.setImage(x.java. pixelX. y < map.2.2. this). } } infoSize. curPosY) + tileOffsetX * r + margin .Graphics. } return width. y++) { int pixelY = y * p + tileOffsetY * p .getHeight(). x < map. setPreferredSize(infoSize).imagesFolder. GameManager. tileImage[index]). g.drawImage(infoImg.loadMap(“Map. private Image infoImg = gm.

. variable tersebut di-switch untuk menentukan aksi apa yang harus dijalankan program. tapi cukup diletakkan dalam class yang membutuhkan event dari keyboard tersebut. library Java memudahkan kita untuk membuat class tersebut. keyPressed(). Devilish children hanya menggunakan input dasar saja. Anda hanya perlu menangkap kode dari panah atas.. Kita akan menamai class tersebut dengan nama MapControl. Artinya. Jadi. yaitu dari keyboard. sama dengan cara melakukan import pada NetBeans. maka tidak akan dilakukan apa-apa 3. serta. posisi kursor dipindah sesuai arahnya dengan mengubah nilai variable curPosX atau curPosY. Variable keyCode menyimpan nilai berupa kode tombol yang sedang ditekan. Jika ya. Cek apakah kursor ada di tepi map. dan keyRe- Gambar 1.106 KNOW-HOW CODING 06/2009 MEMBUAT GAME STRATEGI SEDERHANA MENGGUNAKAN JAVA DAN NETBEANS BAGIAN 2 DARI 3 ARTIKEL Di seri kedua ini. Setelah itu. Jika tidak. Anda harus membuat inner class tersebut di dalam class MapPanel. Cara cepat implement abstract method. sampai dengan beberapa method penting dalam game Devilish Children ini. . Class tersebut tidak perlu dibuat terpisah. kanan. Sebuah class yang diletakkan dalam class dinamakan inner class. Logika yang perlu dilakukan program setiap program mendapatkan keyCode akibat user menekan tombol adalah: 1. Cek lagi apakah kursor yang dipindah keluar dari ukuran tampilan panel map. Untuk melakukannya lebih cepat. 2. kita akan berurusan dengan tokoh utama game ini. untuk menjalankan event dalam MapPanel. Tulis listing a ke dalam class MapPanel. Anda dapat mengklik pada gambar bola lampu. Caranya adalah dengan implements pada class KeyListeners. leased(). bawah. Menggerakkan Kursor Untuk menggerakkan kursor. Anda juga harus melakukan implements ke semua abstract method yang dimiliki class KeyListener. Sekali lagi. Kita akan memulainya dari bagaimana cara menggerakkan kursor. yaitu Frost dan Flare. Anda harus membuat class yang berfungsi untuk menangkap event dari keyboard anda. Fauzil Haqqi SETELAH berhasil membuat tampilan dasar. dan kiri saja. yaitu keyTyped().monster dan kastilnya! Windra Swastika & M. class tersebut harus mampu mendeteksi tombol-tombol apa yang ditekan pengguna pada keyboard. pada bagian kedua ini penulis akan membahas mulai dari bagaimana cara membuat scrolling map. Setelah proses import class KeyListener.

Langkah program menggerakkan kursor. tidak tergantung pada lokasi di mana object tersebut dibentuk. Dalam listing di atas. jika kursor bergerak saat Anda menekan tombol panah. Karakter utama yang digunakan. dan monster. Variable tersebut diletakkan di dalam class GameManager untuk lebih memudahkan. castle akan menyimpan jumlah kastil yang dimiliki. . Setelah itu. Untuk mendeteksi apakah kursor ada di tepi map. Simpan dengan nama Player. bject mc tersebut belum menjadi Listener dari class MapPanel. Setelah Anda membuat inner class MapControl. posX dan posY adalah posisi hero pemain. Jika informasi tersebut berubah. pemain pertama mengGambar 3. maka semua titik awal penggambaran diubah dengan mengubah nilai variable tileOffsetX atau tileOffsetY. Jika ya. mana untuk memanggil monster. Di antaranya adalah jumlah monster yang dimiliki pemain. Begitu pula sebaliknya. Untuk itu. tambahkan kode di bawah ini dalam constructor class MapPanel: mc = new MapControl(). Untuk membuatnya. Kemudian Anda juga bisa membuat method untuk mengganti giliran bermain dengan menambahkan listing c ke dalam class GameManager juga. Gambar 2. Semua informasi tersebut harus statis. Untuk mengaturnya. tambahkan baris di bawah ini di lokasi atribut dalam class GameManager: public static Player turn. panggil repaint() untuk mengulang proses penggambaran terhadap object gambar yang berubah secara otomatis. castle. tulis listing b di dalam file enumeration tersebut. Pemain yang Mendapat Giliran Setelah membuat enum dari pemain. dengan memanggil method getWidth() atau getHeight pada object map. langkah berikutnya adalah membuat object tersebut pada class MapPanel sendiri. Jadi. 4. 5. serta monster menyimpan jumlah monster yang dimiliki pada arena. Anda bisa menggunakan enumeration pada Java. Mengatur Informasi Pemain Dalam game ini ada dua orang pemain. hasilnya selalu sama. Anda bisa membuat variable yang menandakan pemain mana yang sedang mendapat giliran bermain. Method tersebut akan mengecek siapakah pemain yang sedang mendapat giliran. maka giliran berganti ke Frost. dengan menambahkan kode di bawah ini: private MapControl mc. Membuat Monster Langkah berikutnya adalah mengisi class Chara yang telah Anda buat sebelumnya. maka semua class juga membaca hasil perubahan yang terjadi. dan yang lain menggunakan Flare. Coba Anda jalankan program buatan Anda. Setelah itu. sesuai ide awal bahwa semua hal yang bersifat global akan diletakkan pada class ini. addKeyListener(mc). berarti program Anda sudah benar. Enumeration ini hampir sama seperti class. Sebuah monster yang dibuat dari class Meskipun sudah membuat object untuk mendeteksi event keyboard. Jika Flare sedang mendapat giliran kemudian memanggil method ini. posY. Anda perlu mengecek ulang apakah yang Anda lakukan sudah sesuai. Anda tinggal mengecek koordinat kursor apakah ada di antara 0 sampai ukuran map. Variable mana akan menyimpan jumlah mana yang sedang dimiliki pemain. setFocusable(true). dan jumlah kastil yang dimiliki. posX. hanya saja dia berfungsi layaknya tipe data buatan Anda sendiri. lokasi hero pemain. Jika tidak. Frost dan Flare memiliki atribut berupa mana. Jika tidak.06/2009 CODING KNOW-HOW 107 gunakan hero Frost. maka langsung menuju poin 5. Setiap pemain memiliki jenis informasi yang sama. klik New File lalu pilih Java Enum. Artinya kapanpun dan apapun class yang mengakses informasi dari tiap player.

Anda akan memerlukan method untuk me-reset area menjadi false semua setiap monster selesai melakukan aksinya. Variable canMove menandakan apakah monster bisa bergerak. atribut dan method semua monster sama persis. Sebab lokasi kastil tersimpan pada file detail map. Anda perlu menambahkan beberapa variable ke dalam class tersebut. dalam game ini Anda membutuhkan suatu file yang berisi informasi dari monster tersebut dan method untuk me-load isi file ke dalam game. Seperti biasa. x++) { TABEL KOORDINAT RELATIF SISI-SISI PETAK SEGI-6 SISI Kanan Atas Kanan Kanan Bawah Kiri Bawah Kiri Kiri Atas Y = GENAP X Y 0 -1 +1 0 0 +1 -1 +1 -1 0 -1 -1 Y = GANJIL X Y +1 -1 +1 0 +1 +1 0 +1 -1 0 0 -1 Menambahkan Kastil dan Area Kembali ke class HexaMap. y. x < getWidth(). Dalam pembuatan sebuah game. Listing d adalah contoh class Chara yang sederhana yang digunakan dalam pembuatan game ini. private boolean[][] area. Seperti yang dijelaskan sebelumnya. karena setiap monster tidak memiliki atribut-atribut unik yang tidak dimiliki monster lain. Method tambahan lain adalah isAlive() yang nanti akan digunakan untuk mengecek apakah monster tersebut masih hidup atau sudah mati. attack. jangkauan berpindah. Yang harus Anda tambahkan adalah baris di bawah ini ke dalam switch dengan case ‘C’ (kastil). Variable chara menyimpan informasi monster sesuai koordinatnya. Seperti sebelumnya.png untuk file gambar face-nya.chara. y. Nama file gambar tersebut sebaiknya mengandung nama monster itu sendiri. Variable-variable tersebut akan berfungsi pada saat mengontrol monster menggunakan keyboard. Penulis membuat nama tTiamat. defend. Sampai dengan langkah ini. Setelah itu. Begitu juga dengan file gambar yang nanti digunakan oleh setiap monster. AP (Attack Poin).setOwner(x. pemiliknya. Constructor class ini mengambil parameter sebuah array String. name adalah nama monster. null). dan jangkauan menyerang. private Player[][] owner. dan status-status yang lain. Anda bisa membuat file informasi tersebut seperti pada listing e untuk setiap monster. Setelah itu. variable area akan digunakan untuk proses aksi berpindah atau menyerang yang dilakukan monster. dalam contoh di atas adalah Tiamat. Anda bisa menggunakan perulangan sederhana seperti baris di bawah ini: public void setAreaFalse() { for(int y = 0. Selain itu juga dibutuhkan informasi ada tidaknya monster dalam . Kita tidak perlu membuat subclass yang berbeda untuk setiap monster. chara. Method Khusus Class HexaMap Berikutnya. yaitu: private Chara[][] chara. Array tersebut berisi informasi masing-masing monster yang dipanggil. suatu area serta status area apakah boleh dipilih saat monster berpindah tempat atau melakukan serangan. Variable castle dan owner harus terdefinisi sejak map di-load untuk pertama kali. penulis cukup menggabungkan parameter nama monster yang dipanggil dengan ekstensi chara untuk mencari file informasi. kemudian berturut-turut adalah maximum HP. Anda dapat menyimpannya sesuai nama monster dengan ekstensi . Untuk itu. HP saat ini. ada method yang berfungsi untuk mengubah nilai dan ada yang berfungsi untuk mendapatkan nilai dari masing-masing variable. face berisi gambar wajah monster yang akan ditampilkan di panel info. Artinya. Sehingga penulis lebih mudah untuk membuat method loadChara() seperti listing f. castle = new boolean[width][height]. Anda juga harus membuat method set dan get dari variable di atas seperti pada listing g. HP (Health Poin). Variable owner berisi pemilik monster tersebut. y < getHeight(). private boolean[][] castle. true). tile berisi gambar monster yang akan ditampilkan di map. newMap. y++) { for(int x = 0.108 KNOW-HOW CODING 06/2009 Chara setidaknya memiliki beberapa atribut. inisialisasi variable di atas dalam constructor dengan menambahkan baris berikut ke dalam constructor. meskipun nama dan gambar monster tersebut berbeda satu sama lain. Anda harus banyak berpikir tentang apa saja yang mungkin terjadi dalam game dan bagaimana algoritma pemecahan masalahnya.png untuk file gambar tile monster bernama Tiamat. Untuk method setArea akan dibuat dan dijelaskan tersendiri karena ada metode khusus untuk menentukan area jangkauan perpindahan monster maupun area serangan monster tersebut. kemudian fTiamat. dan menggabungkan dengan huruf f atau t dengan ekstensi png untuk file gambarnya. kita menemukan kasus bahwa dalam class HexaMap harus terdapat informasi tentang di manakah kastil berada dan milik siapa. serta variable owner menentukan siapa pemilik kastil tersebut. chara = new Chara[width][height]. Memanggil Monster Sama seperti load map sebelumnya. di antaranya adalah nama.setCastle(x. owner = new Player[width][height]. variable castle untuk menandakan ada tidaknya kastil dalam koordinat tertentu. newMap. Anda bisa melihatnya lagi pada bagian 1 artikel ini. Anda akan memasuki tahap yang lebih spesifik. area = new boolean[width][height]. gambar yang digunakan. dan canSelected apakah monster masih bisa dipilih. canAttack apakah bisa menyerang.

Berikut tabel koordinat relatif sisi-sisi petak segi-6. Setiap sisi petak dicek apakah bisa ditempati atau tidak. Anda perlu mendapatkan lokasi hero yang sedang mendapat giliran. y)) { chara[x][y]. untuk melakukan pengecekan. Variable pX dan pY menyimpan lokasi tersebut. lokasi pemanggilan adalah di sekitar hero yang sedang mendapat giliran. Anda cukup mencari dalam radius jangkauan serangan saja. Dengan sedikit analisa sederhana.changeMonster(-1).setCanMove(true). Sebelum melakukan pengecekan.isArea(x. pixelX. Mereset Status Monster Setiap pergantian giliran. Pada method ini. Ada 4 sisi dengan rumus sama dan 2 sisi dengan rumus berbeda.getTile(). Perbedaan tersebut adalah. } if(map. Kemudian dicek lagi apakah dalam radius tersebut terdapat monster milik lawan. dan menyerang. Caranya adalah dengan menggunakan metode rekursif. x++) { if(isChara(x. kemudian jika bisa. Artinya. Penghapusan tersebut juga akan menambah jatah monster yang dapat dipanggil oleh pemain yang kehilangan monster tersebut. status monster harus dikembalikan lagi. Anda harus menghitung koordinat petak-petak di ke-6 sisi tersebut relatif terhadap petak utama. maka dicek lagi sisi petak tersebut sampai dengan jangkauan si monster. y++) { for(int x = 0. Cara yang digunakan hampir sama dengan cara mengatur area . Karena monster memiliki kemampuan berpindah tempat sesuai jangkauan langkahnya. int y) { if(!chara[x][y]. Dalam game ini.06/2009 CODING KNOW-HOW 109 area[x][y] = false.isChara(x. Anda bisa menemukan sesuai kode di listing h sebelumnya. int y) { if(chara[x][y] != null) { return true. Anda harus mencari rumus lokasi sisi-sisi suatu petak. Setiap petak mempunyai 6 sisi. Untuk itu Anda cukup menambahkan method seperti baris di bawah ini: public void setDeath(int x. sampai dengan jangkauan monster yang akan dipindah.getChara(x. y) . jika area perpindahan maka yang dihitung adalah jumlah langkah yang dilakukan monster. Seperti yang telah dijelaskan sebelumnya. Untuk saat ini. Menghapus Monster yang Mati Monster dengan HP nol harus dikeluarkan dari arena. if(map. Permasalahannya adalah terdapat rumus sisi-sisi yang berbeda antara baris ganjil dan genap. Anda bisa menggunakan listing h. pixelY. Anda tinggal menambahkan kode untuk menampilkannya di dalam method paintComponent() yang ada di class MapPanel. method ini menggunakan metode rekursif.isAlive()) { chara[x][y]. public boolean isChara(int x. artinya method ini memanggil dirinya sendiri dan akan berhenti pada kondisi tertentu. melakukan perpindahan. Untuk itu. Namun ada sedikit perbedaan dalam menangkap kondisi yang terjadi. maka area perpindahannya harus diatur sedemikian rupa. jika sisi petak utama bisa ditempati. maka dia memanggil dirinya sendiri dan menganggap sisinya sebagai petak utama lagi. y < getHeight(). } } } } Area Pemanggilan Monster Sebelum menampilkan menu pemanggilan monster. Namun untuk area serangan. Method ini akan dipanggil setiap kali proses penyerangan yang dilakukan suatu monster berjalan. tanpa perlu menghitung langkah berbelok seperti pada area perpindahan. di dalam map belum terdapat satupun monster atau area yang sudah di-set sebagai area perpindahan maupun area serangan. Tapi mau tidak mau Anda harus bisa menampilkan monster dan area tersebut pada langkah-langkah berikutnya. chara[x][y]. x < getWidth(). Untuk itu.setCanAttack(true). y)) { g.drawImage(map. Anda perlu membuat method seperti di bawah ini di dalam class HexaMap juga. monster harus bisa dipilih. chara[x][y].setCanSelected(true). chara[x][y] = null. Jika ada. Setelah itu. public void resetAllChara() { for(int y = 0. } return false. Coba Anda lihat pada listing j di bawah ini.getOwner(). } } Area Perpindahan Monster Rumus koordinat relatif sebelumnya juga digunakan untuk menentukan area perpindahan monster ini. Anda bisa menggunakan method setMoveArea() seperti pada listing I. this). Anda tentu perlu mengecek apakah kursor ada di lokasi pemanggilan yang ditentukan. y)) { Area Serangan Monster Berikutnya adalah bagaimana cara mengatur area serangan monster yang sedang dalam kondisi akan menyerang. } } } Anda juga memerlukan method sederhana untuk mengetahui apakah dalam koordinat tertentu terdapat monster atau tidak. Menampilkan Monster dan Area Kembali ke class MapPanel. maka monster lawan tersebut termasuk dalam area serangan. } perpindahan monster.

Artinya. Pada bagian berikutnya.getHeight()) { ++curPosY. private int castle. Flare. begitu seterusnya.VK_LEFT: if(curPosX .VK_RIGHT: if(curPosX + 1 < map.InfoPanel. private int posX. public void setPos(int posX. public void changeMana(int mana) { this. } } break.mapFolder.1 >= 0) { --curPosX. public enum Player { Frost. if((curPosY + tileOffsetY + 2) * p > GameFrame. } repaint(). case KeyEvent. int posY) { this. } } break. this.posX = posX.110 KNOW-HOW CODING 06/2009 g. } } break.mana += mana. private int monster. } } break.drawImage(gm.getKeyCode(). if(curPosX + tileOffsetX == -1) { ++tileOffsetX. } } } } } } public void keyReleased(KeyEvent e) { } Listing b package devilishchildren.png”). penggambarlisting a private class MapControl implements KeyListener { an dimulai dari baris paling atas sehingga baris tersebut terletak pada layer paling bawah. private int posY. Baris berikutnya akan diletakkan pada layer di atasnya. pixelX.infoHeight) { --tileOffsetY.frameWidth) { --tileOffsetX. public int getPosY() { return posY. switch(keyCode) { case KeyEvent. public void changeCastle(int castle) { this. pixelY. default: break. if(curPosY + tileOffsetY == -1) { ++tileOffsetY.getWidth()) { ++curPosX. private int mana.1 >= 0) { --curPosY. “tileArea. case KeyEvent.frameHeight . case KeyEvent.VK_DOWN: if(curPosY + 1 < map. if((curPosX + tileOffsetX + 2) * r > GameFrame.loadImage(GameManager . public int getPosX() { return posX. karena proses penggambaran dalam method ini linear. } public void keyTyped(KeyEvent e) { } } public void keyPressed(KeyEvent e) { int keyCode = e.posY = posY. } Kode tersebut harus diletakkan di dalam perulangan dan di bawah baris yang menampilkan petak-petak map.castle += castle. public int getMana() { return mana. penulis akan menjelaskan pembuatan sistem pertarungan dan method-method lanjutan yang digunakan dalam pertarungan dalam game Devilish Children ini. public int getCastle() { . this).VK_UP: if(curPosY .

canSelected = canSelected. canMove = false. ATK = Integer.Image. public void setCanMove(boolean canMove) { this. private Image face. } } public void setFace(Image face) { public void changeMonster(int monster) { this.parseInt(detail[5]). private boolean canSelected. public int getHP() { return HP. canSelected = false. . private int ATKr.face = face. } Listing d package devilishchildren.parseInt(detail[6]). private Image tile. } public int getMOVr() { return MOVr. } import java.Flare. private boolean canAttack.Frost. } this. private int mana. public boolean isCanAttack() { } public Image getFace() { return face. public Chara(String[] detail) { mana = Integer.06/2009 CODING KNOW-HOW 111 return castle. } public void setTile(Image tile) { public int getMonster() { return monster. private int DEF.parseInt(detail[0]).canAttack = canAttack. } this.Flare) { turn = Player. private int ATK. } } public void changeHP(int HP) { this. public void setCanSelected(boolean canSelected) { public class Chara { private Player owner. } public int getATK() { return ATK. DEF = Integer.owner = owner.parseInt(detail[3]). name = detail[1]. private String name. public int getDEF() { return DEF. private boolean canMove. Listing c public static void changeTurn() { if(turn == Player.parseInt(detail[2]). private int HP. private int maxHP. MOVr = Integer. ATKr = Integer.awt. } else { turn = Player.monster += monster. canAttack = false. } public void setCanAttack(boolean canAttack) { this. } } } public void setOwner(Player owner) { this. } this. maxHP = HP = Integer.parseInt(detail[4]).canMove = canMove.HP += HP. } } } public int getATKr() { return ATKr.tile = tile. private int MOVr.

newChara. } public void setCastle(int x. int y) { return chara[x][y]. int y. Player owner) { this. break.setFace(loadImage( charaFolder. max HP. newChara. “t” + name + “. ATK. int y) { return castle[x][y]. int y) { . } int count = -1. public boolean isAlive() { if(HP > 0) { return true. Move Range. int y. } } Listing g public Chara getChara(int x. while(true) { String line = reader.println(iOException.chara #Order: mana needed.getCause()). boolean castle) { this. } return false.chara”. } public boolean isCastle(int x. name.readLine(). DEF. } public Player getOwner(int x. } Listing e #Tiamat. } } if(!line. “f” + name + “. public Player getOwner() { return owner. public boolean isCanMove() { return canMove.112 KNOW-HOW CODING 06/2009 return canAttack.dir”) + File. int y.getProperty(“user.png”)). } } detail[++count] = line. reader. Attack Range #Monster Data 30 Tiamat 850 110 65 5 4 public void setOwner(int x.castle[x][y] = castle.startsWith(“#”)) { public int getMaxHP() { return maxHP. } } charaFolder. public boolean isCanSelected() { return canSelected. } } catch(IOException iOException) { System. return newChara. } try { BufferedReader reader = new BufferedReader( new FileReader(new File(path))).setTile(loadImage( public Image getTile() { return tile. Chara c) { chara[x][y] = c. } } public String getName() { return name.png”)). } String path = System.close(). } public void setChara(int x.owner[x][y] = owner.separatorChar + charaFolder + name + “.out. } Chara newChara = new Chara(detail). Listing f public Chara loadChara(String name) { String[] detail = new String[7]. if(line == null) { public int getMana() { return mana.

y-1) || chara[x][y-1] . x+1.getPosX(). if(isChara(x-1. if(y-1 >= 0) { if(!isChara(x. x.turn) { area[x+1][y-1] = true. } if(y % 2 == 0) { if(x-1 >= 0 && y+1 < getHeight()) { if(!isChara(x-1. int x. step). } } } return false. y. int y.06/2009 CODING KNOW-HOW 113 return owner[x][y]. y+1) || chara[x][y+1] . x-1. setMoveArea(range.getPosY(). setMoveArea(range. y)) { if((x == pX+1 && y == pY) || (x == pX-1 && y == pY) || (x == pX && y == pY-1) || (x == pX && y == pY+1)) { return true. setMoveArea(range. } Listing i public void setMoveArea(int range. x-1.turn) { area[x][y-1] = true.turn) { area[x-1][y] = true. y-1)) { area[x][y-1] = false. if(isChara(x.turn) { Listing h public boolean isSummonArea(int x. y-1. setMoveArea(range.getOwner() == GameManager. y-1)) { area[x+1][y-1] = false. x. } } public boolean isArea(int x.getOwner() == GameManager. setMoveArea(range. y+1)) { area[x][y+1] = false. } } } area[x+1][y] = false. int step) { if(step < range) { step++. setMoveArea(range. y+1. } if(pY % 2 == 0) { if((x == pX-1 && y == pY+1) || (x == pX-1 && y == pY-1)) { return true. if(isChara(x. y-1) || chara[x-1][y-1] . } } } } } area[x][y+1] = true. y-1)) { area[x-1][y-1] = false. y)) { } } } } if(x-1 >= 0 && y-1 >= 0) { if(!isChara(x-1. y) || chara[x-1][y] .turn) { area[x+1][y] = true.getOwner() == GameManager. if(isChara(x-1. } } else { if(x+1 < getWidth() && y-1 >= 0) { if(!isChara(x+1. y) || chara[x+1][y] .turn) { area[x-1][y-1] = true. int y) { return area[x][y]. } if(x-1 >= 0) { if(!isChara(x-1.turn.getOwner() == GameManager. if(!isChara(x. x+1.getOwner() == GameManager. step). . int y) { int pX = GameManager. setMoveArea(range. x-1. y+1)) { area[x-1][y+1] = false. y-1) || chara[x+1][y-1] . if(isChara(x+1. y-1. step). if(y+1 < getHeight()) { if(!isChara(x. } } else { if((x == pX+1 && y == pY-1) || (x == pX+1 && y == pY+1)) { return true.turn) { area[x-1][y+1] = true. step).getOwner() == GameManager. step). y+1. y. if(isChara(x-1. y+1) || chara[x-1][y+1] . step). int pY = GameManager. if(isChara(x+1. y)) { area[x-1][y] = false. step).getOwner() == GameManager. } } } if(x+1 < getWidth()) { if(!isChara(x+1. y-1.turn.

if(x-1 >= 0 && y-1 >= 0) { if(isChara(x-1. y. O’Reilly.getOwner() != GameManager. x-1.turn) { area[x-1][y] = true.java. int step) { if(step < range) { step++. if(y % 2 == 0) { if(x-1 >= 0 && y+1 < getHeight()) { if(isChara(x-1. y)) { if(chara[x-1][y].getOwner() != GameManager. step). y+1)) { if(chara[x][y+1].getOwner() == GameManager. step). step).turn) { area[x+1][y+1] = true. y+1)) { if(chara[x-1][y+1]. y-1)) { if(chara[x][y-1]. } } setAttackArea(range.getOwner() != GameManager. if(x+1 < getWidth() && y+1 < getHeight()) { if(isChara(x+1. step). } } setAttackArea(range.getOwner() != GameManager.turn) { area[x-1][y-1] = true. y+1) || chara[x+1][y+1] . } } } } } } } } } } else { } } } GameManager.getOwner() != GameManager. setAttackArea(range.net Forum.javacooperation. int x. step).getOwner() != GameManager.turn) { area[x-1][y+1] = true. y+1)) { area[x+1][y+1] = false. gmxhome. x. } } setAttackArea(range. if(isChara(x+1. if(x+1 < getWidth() && y-1 >= 0) { if(isChara(x+1. x-1. y+1. setAttackArea(range. } if(x-1 >= 0) { if(isChara(x-1.turn) { area[x+1][y] = true. y+1.turn) { area[x][y-1] = true. setMoveArea(range. step). Java. x+1. x+1. } if(y+1 < getHeight()) { if(isChara(x. y+1.net Java Game Programming Tutorial.turn) { area[x][y+1] = true. May 2005. } if(x+1 < getWidth()) { if(isChara(x+1. Andrew. x+1. x.turn) { area[x+1][y+1] = true.getOwner() != GameManager. } } setAttackArea(range.de .getOwner() != Listing j public void setAttackArea(int range. y-1. x+1. step). y-1. y. y-1. y+1)) { if(chara[x+1][y+1]. step). setAttackArea(range.114 KNOW-HOW CODING 06/2009 } } } if(x+1 < getWidth() && y+1 < getHeight()) { if(!isChara(x+1. LEBIH LANJUT Davidson. y)) { if(chara[x+1][y]. x-1. y-1)) { if(chara[x-1][y-1]. Killer Game Programming in Java. y+1. int y. y-1)) { if(chara[x+1][y-1]. step). } } } } } } } } } } setAttackArea(range. http://forums. if(y-1 >= 0) { if(isChara(x. www.turn) { area[x+1][y-1] = true.

public static State map. Anda perlu mengaturnya dalam constructor class MapPanel. Coba Anda jalankan program. Pertama-tama. Kondisi tersebut dinamakan state. Windra Swastika & M. dan sisa mana yang . Jika mengikuti semua langkah dengan benar. jumlah kastil. State tersebut dapat berupa state dalam panel map maupun untuk menu dalam panel info. Proses penambahan hero sama dengan pemanggilan monster. Penggunaan enum state ini hampir sama dengan enum Player yang telah Anda buat sebelumnya. Anda harus pikirkan apa saja kondisi-kondisi yang akan terjadi. adalah menampilkan informasi dari pemain pada panel info. Fauzil Haqqi PADA BAGIAN ketiga kali ini. fungsi tombol Z saat melakukan proses pemanggilan monster berbeda dengan saat melakukan perpindahan monster. Listing a adalah salah satu enum state sederhana. Menambahkan Hero ke dalam Arena Hero tiap pemain harus sudah ada di dalam arena sejak permainan dimulai. game ini menggunakan 2 tombol utama. Dalam game ini. terdapat kondisi-kondisi tertentu yang berbeda satu sama lain. Untuk itu. public static State menu. kita akan membahas kelanjutan proses pembuatan game “Devilish Children” sampai akhir. State ini dapat Anda tentukan sendiri dengan membuat enumeration seperti saat membuat enum Player. Membuat State Dalam suatu game. State menentukan apa yang akan terjadi saat tombol utama ditekan. Deklarasikan kedua object tersebut dalam area variable di class GameManager. Menampilkan Info Player pada Panel Info Langkah berikutnya. Selain 4 tombol arah. Event yang disebabkan penekanan tombol tersebut harus ditangkap menggunakan inner class MapControl yang telah Anda buat sebelumnya. Tombol-tombol tersebut adalah huruf Z yang digunakan untuk memilih dan huruf X yang digunakan untuk membatalkan. hanya saja tidak perlu memenuhi kondisi-kondisi tertentu. Anda akan berhasil menampilkan hero di bagian pojok arena.112 KNOW-HOW CODING 07/2009 MEMBUAT GAME STRATEGI SEDERHANA MENGGUNAKAN JAVA DAN NETBEANS BAGIAN 3 DARI 3 ARTIKEL Di seri ketiga ini. Mulai dari penampilan menu pada panel info. penulis akan membahas satu demi satu proses event yang terjadi dalam game. ada beberapa state yang mungkin terjadi. Informasi yang perlu ditampilkan adalah berapa jumlah monster. sampai dengan event-event yang dilakukan monster seperti berpindah dan menyerang. Caranya adalah tambahkan listing c ke dalam constructor class MapPanel. Anda akan lebih mengerti kegunaan state tersebut pada langkah-langkah selanjutnya. yaitu untuk map dan menu. Anda perlu mendeklarasikan 2 object state. Sebagai contoh.

Sama seperti sebelumnya. Menggerakkan Kursor Menu Sama seperti pada class MapPanel. private final int curPos = 13. Menampilkan Info Monster Pada panel info. } Setelah itu. Setelah itu. Anda harus membuat inner class yang Anda juga harus membuat method yang mengeset nilai dari actChara. Maksud dari monster yang sedang aktif adalah monster tersebut sedang dipilih oleh pemain. atau End. . koordinat y. Anda hanya perlu teliti mengatur lokasi koordinat Gambar 2. untuk menggerakkan kursor pada class InfoPanel. Selain itu. Anda cukup menambahkan variable di bawah ini ke dalam lokasi variable global dari class InfoPanel: private Image curImg = gm. maka yang ditampilkan adalah gambar Frost. Deklarasikan baris di bawah ini pada lokasi variable di dalam class InfoPanel: Chara actChara. Menampilkan Menu Menu dalam game ini akan ditampilkan pada bagian kanan panel info. juga tersedia space untuk menampilkan informasi monster yang sedang aktif. koordinat x. Variable curImg menyimpan gambar kursor. Anda bisa membuat kode penggambaran sederhana seperti listing e. menu yang ditampilkan sesuai dengan kondisi yang ada. perlu ditangkap lagi siapakah pemain yang sedang mendapat giliran untuk menentukan menu pemanggilan.imagesFolder. Pada saat kondisi state menu = summon. State Show adalah kondisi saat pemain memilih monster milik lawan atau miliknya sendiri dengan atribut canSelected = false. saat state map dalam kondisi normal. maka akan muncul menu aksi monster. Informasi giliran pemain. Koordinat tersebut adalah lokasi diletakkannya bagian paling kiri bawah dari kata yang ditampilkan.07/2009 CODING KNOW-HOW 113 Gambar 1. Anda juga memerlukan kursor yang berfungsi untuk memilihnya. State Move adalah saat monster melakukan aksi berpindah pada map. class InfoPanel tidak memiliki hak untuk mengakses lokasi kursor pada class MapPanel maupun mengakses monster yang sedang ada pada object map dengan koordinat lokasi kursor. Misalnya. State Summon adalah saat pemain memilih monster yang akan dipanggil ke arena. Namun. Listing c adalah kode untuk menampilkan info pemain dan giliran pemain. Contoh informasi monster saat state menu = Show. bagaimana class InfoPanel dapat menampilkan informasi monster tersebut? Yang perlu Anda lakukan adalah membuat object Chara yang bersifat temporary. Informasi giliran hanya ditampilkan pada saat State dari object menu sama dengan Normal. Selain menu yang ditampilkan. sedangkan state Attack adalah saat monster melakukan aksi menyerang. dilakukan pengecekan giliran pemain. state End adalah saat pemain akan menghentikan gilirannya.loadImage(GameManager . Begitu pula sebaliknya. Anda bisa memanggil method drawString() milik object g dengan parameter: kata yang ditampilkan. curMov berisi besar perpindahannya. Lalu. private final int curMov = 40. Artinya monster miliknya sudah tidak dapat melakukan aksi lagi. Letakkan listing e ini di bagian paling bawah method paintComponent() pada class InfoPanel. pada state tertentu Anda sebaiknya menampilkan info tersebut. public void setActChara(Chara chara) { actChara = chara. State Normal adalah saat tidak ada event tertentu terjadi. agar lebih mudah untuk mengetahui siapa pemain yang sedang mendapat giliran. Seperti yang telah dijelaskan sebelumnya. Menumenu tersebut akan muncul sesuai event yang terjadi. Informasi tersebut tampil sesuai pemain yang sedang mendapat giliran. Coba Anda lihat pada listing d. Ada beberapa menu yang bisa ditampilkan. Penulis menampilkannya pada area penampilan wajah monster. Begitu juga dengan event lainnya. jika pemain memilih monster. Sedangkan. penggambaran informasinya dengan satuan pixel. Karena menu yang ditampilkan tergantung pada kondisi state menu yang berjalan. private int curIndex = 0. letakkan di dalam method paintComponent() di class panelInfo. “menuCur. Informasi monster hanya ditampilkan saat state dari menu adalah Show atau Monster. State Monster adalah saat pemain memilih monsternya sendiri dan monster tersebut bisa melakukan aksi. atau saat state dari map adalah Move atau Attack. Anda tinggal mengatur lokasi penampilan informasi tersebut. Anda harus meletakkan listing d di dalam method paintComponent() dari class InfoPanel. dan curIndex menyimpan lokasi kursor relatif terhadap menu yang ada. variable curPos berisi koordinat vertikal awal penggambarannya dalam satuan pixel. dimiliki. Karena kursor ini masih sederhana.png”). Sebab monster yang dapat dipanggil oleh Frost berbeda dengan yang dipanggil Flare. Jika pemain yang menggunakan Frost sedang mendapat giliran. Summon. Anda tidak perlu membuat class khusus untuk kursor ini.

class InfoPanel. Namun sebelum itu.setFocusable(true). lokasi awal monster bisa dikosongkan. perlu Tapi jika lokasi tersebut tidak ada monster dan juga ditangkap kondisi state menu yang terjadi. Lalu inisialisasi object dan masukkan sebagai KeyListener dari class InfoPanel dengan menambahkan kode di bawah ini ke dalam constructor InfoPanel: ic = new InfoControl().setFocusable(false). ada dua kemungkinan awal yang bisa ditangkap. private int startX. ip. Coba Anda lihat pada Gambar 4. Sedangkan karena jumlah menu untuk monster. maka state menu menjadi Monster dan focus kursor berpindah ke class InfoPanel. yang kursor hanya bisa bergerak sampai menu ketiga. akan dilakukan pengecekan lagi apakah monster tersebut milik pemain yang sedang mendapat giliran dan monster tersebut dapat melakukan aksi. Satu-satunya class yang memiliki hak akses tersebut adalah class GameFrame. Sedangkan pada saat pemain menekan tombol X. terdapat 4 menu. yang perlu Anda tangkap kedua kondisi tersebut. Summon dan focus kursor berpindah juga ke class Kursor bisa bergerak ke atas jika kursor tidak berada InfoPanel. sebelum pemanggilan method toInfo Anda perlu memdeklarasikan variable yang akan menyimpan koordinat sementara dari monster yang dipilih. menu aksi Pada penggunaannya. addKeyListener(ic). perlu dibuatkan method khusus pada class yang memiliki hak mengakses object ip dari class InfoPanel. Gambar 5. Menu pengJangan lupa untuk mendeklarasikan object dari inner class gantian giliran akan ditampilkan. Contoh menu pada posisi teratas. Focus kursor berpindah ke InfoControl ini dalam area variable class InfoPanel. maka state menu menjadi cukup saat pemain menekan panah atas atau Show. Memindahkan Fokus ke InfoPanel Untuk memindahkan focus ke class InfoPanel. Jelas bahwa penempatan kode yang mengaturnya akan berada di inner class MapControl yang telah Anda buat sebelumnya. Anda harus membuatnya di dalam class tersebut. Sedangkan untuk state Monster. Sebut saja method-method tersebut adalah action. Artinya class InfoPanel hanya menampilkan bawah saja. Listing f di atas adalah salah satu contoh inner Jika lokasi tersebut merupakan area pemanggilan class yang digunakan. private int startY. . maka tidak akan dilakukan apa-apa. Kode yang perlu Anda buat cukup sederhana. hanya seperti baris di bawah ini: public static void toInfo(Chara actChara) { ip. Summon. ketika pemain menekan tombol Z. method ini akan variable ini akan diisi dengan lokasi kursor. Pada saat state ini. Gunanya adalah agar lokasi awal monster tersimpan. atau lokasi tersebut adalah area pemanggilan. } Event pada MapPanel Kita masuk ke masalah event pada MapPanel. Deklarasikan variable tersebut pada class MapPanel. Z dan X. Pada saat state map = Normal. Tapi jika tidak memenuhi tersebut. ada beberapa hal yang mungkin terjadi. Yang dimaksud event di sini adalah apa yang akan terjadi saat pemain menekan 2 tombol utama. mp.114 KNOW-HOW CODING 07/2009 implements KeyListener di dalam class InfoPanel monster juga ditampilkan. terjadi adalah state menu berubah menjadi End. informasi monster saja. Alur program saat state map = Move. Jika ya. Beberapa kasus terjadi pada setiap state-nya. Jika pada lokasi tersebut terdapat monster.repaint(). Posisi kursor disimpan pada monster. jika pada posisi kursor terdapat monster. Alur program saat state map = Normal. Menu pemanggilan juga akan ditampilGambar 3. maka state menu akan berubah menjadi variable curIndex yang telah Anda buat sebelumnya. ip. Karena pada saat state Normal. koordinat penyerang juga dapat diketahui. yang menggambarkan alur program. pada state Summon dan Monster berbeda. private InfoControl ic. Yaitu. Sehingga pada saat aksi perpindahan. Buatlah juga method setter nilai tersebut untuk memudahkan Gambar 4. Pada saat pemain menekan tombol Z. Untuk state bukan area pemanggilan.setActChara(actChara). Demikian juga pada saat aksi penyerangan. kan. Hanya saja. Jadi. Anda perlu membuat method-method apa yang akan dipanggil saat event itu terjadi.

changeTurn(). } Selanjutnya. Method move() akan memindahkan monster dari titik awal (yang telah disimpan dalam variable startX dan startY) ke titik tempat kursor berada. pengesetan di dalam class MapPanel juga. maka area akan di-reset. Baris awal akan memindahkan monster dari posisi awal ke posisi kursor. GameManager. Tidak lupa untuk mengeset monster untuk tidak bisa berpindah lagi pada giliran itu.Buatlah juga variable showDamage yang merupakan kondisi apakah damage perlu ditampilkan atau tidak. Setelah itu.map = State. tileXToPixel(curPosX. Untuk lebih jelasnya.turn. startY = curPosY. Kemudian informasi jumlah kastil yang dimiliki ditambah. Font.repaint(). Method ini akan mendapatkan nilai ATK dari monster penyerang. public void setStart() { startX = curPosX. HP dari monster yang bertahan akan dikurangi dengan damage. Jangan lupa untuk mengubah state map menjadi Normal kembali. Lalu status canAttack dari penyerang diset false agar monster tersebut tidak bisa menyerang lagi pada giliran itu. Perbedaannya adalah adanya pengecekan apakah state map berubah menjadi End atau tidak.ORANGE). ada event yang berbeda saat state map = Attack. curPosY) + tileOffsetX * r + margin + 15.getPosY()) == null) { GameManager. dicek apakah monster tersebut telah mati atau tidak.turn.toString(damage). private int damage. Pada saat state map = Move. Tapi jika belum game over. Anda bisa menampilkan informasi damage yang terjadi. Setelah itu dicek lagi.End. dan hero tersebut mati.setColor(Color. artinya pemain sedang memilih lokasi untuk memindahkan monster. apakah tempat perpindahan monster adalah sebuah kastil. posisi hero pada object turn juga harus diubah. area perpindahan di-reset menjadi false dengan memanggil method setAreaFalse() dari object map. Rumus penyerangan ini dibuat sederhana saja. Buat method tersebut dan letakkan pada class GameFrame. method ini memanggil method infoUpdate(). fungsi tombol Z dan X berubah juga. Artinya. g. Listing g adalah contoh method move() sederhana yang digunakan dalam game ini. Jika ya dan kastil tersebut bukan milik pemain yang sedang mendapat giliran. Jika ya. state menu menjadi Monster yang akan menampilkan menu monster kembali. Jika ya. informasi jumlah kastil yang dimiliki lawan juga akan berkurang.07/2009 CODING KNOW-HOW 115 Gambar 6. lalu state map menjadi Normal. maka kepemilikan kastil beserta gambarnya akan diubah. if(map. coba perhatikan listing h. State map End menandakan permainan sudah berakhir karena salah satu hero sudah mati (game over). lalu dikalkulasikan dalam variable damage yang menjadi nilai serangan monster. Method attack() ini juga memanggil method isGameOver().getChara(GameManager. Alur programnya dapat Anda lihat pada Gambar 6. Method move() juga diletakkan dalam class MapPanel. dibuat nilai acak yang akan menambah atau mengurangi serangan antara 0 sampai 1/8 dari nilai serangan awal. jika lokasi kursor adalah area penyerangan. pada method paintComponent(). Pada state ini. } Kembali ke masalah event pada MapPanel. Setelah itu. Method ini akan mengurangi HP dari monster yang diserang. Alur tersebut hampir sama dengan saat state map = Move. maka program akan memanggil method attack(). Alur program saat state map = Attack. Jika kastil tersebut milik lawan. private boolean showDamage. akan dicek apakah lokasi kursor terletak pada area perpindahan. maka monster dihilangkan dari arena. } Menampilkan Damage dan Game Over Pada saat penyerangan. State Attack adalah saat monster akan melakukan penyerangan. Setelah itu. Setelah itu. Jika pemain menekan tombol Z. Jika ya. Anda bisa membuat method sederhana tersebut seperti pada baris di bawah ini: public void isGameOver() { GameManager.drawString(Integer.changeTurn(). public static void infoUpdate() { ip. . maka method move() akan dipanggil.setFont(new Font(Font. nilai DEF dari monster yang diserang juga diambil. Method ini hanya akan memanggil method repaint() pada object ip dari class InfoPanel. Jika pemain menekan tombol X. maka state akan diubah menjadi Normal kembali. } GameManager. Anda harus menambahkan baris di bawah ini: if(showDamage) { g. Method isGameOver() akan mengecek apakah setelah penyerangan hero lawan menghilang dari arena. Coba Anda lihat alurnya pada Gambar 5.SANS_SERIF. maka permainan berakhir. Variable damage pada method attack() dideklarasikan global di area variable class MapPanel. 20)). jika monster yang diserang adalah hero lawan. g.BOLD. setelah itu fokus dipindahkan kembali ke InfoPanel. Kemudian dicek apakah yang dipindahkan adalah hero. Setelah itu.getPosX(). Pada baris terakhir.

tidak ada menu yang ditampilkan. Memindahkan Fokus ke MapPanel Pada listing di atas terdapat pemanggilan method static toMap(). mode menunggu. Inner Class InfoControl Langkah berikutnya Anda tinggal mengatur switch pada inner class InfoControl yang telah Anda buat. tampilan damage tersebut menghilang.showSplash(). attack (menyerang).SANS_SERIF. Aksi attack akan melakukan pengesetan area penyerangan. Seperti alur yang telah kita bahas sebelumnya. SplashScreen ss = new SplashScreen(4000). Event pada InfoPanel Setelah selesai mengatur event pada MapPanel. sehingga monster tersebut tidak dapat dipilih lagi. . Kemudian method showSplash() akan menampilkan method tersebut sesuai waktu yang diinginkan. Inner Class MapControl Setelah membuat semua method yang akan digunakan saat penekanan tombol Z dan X. Jadi Anda harus membuat method tersebut di class GameFrame. maka giliran akan berganti. dan end. State menu ada 4 macam. Cara membuatnya. yaitu state menu.Pergantian giliran juga diikuti dengan penambahan mana dari pemain. summon. Sesuai kondisi state menu-nya. Contoh tampilan splash screen. ada yang dengan parameter. Anda harus membuat method dari tersebut di dalam class MapPanel. Method toMap() dengan parameter akan menangkap posisi kursor yang kemudian akan di-switch sesuai state menu yang terjadi. 80)). Method ini akan menangkap tombol yang diketikkan. dan ada yang tanpa parameter. mode perpindahan.116 KNOW-HOW CODING 07/2009 curPosY * p + tileOffsetY * p + margin). Dalam method main. Sedangkan.ORANGE). Tampilan ini hanya akan muncul beberapa detik.drawString(“Game Over”. Yang terjadi adalah pemain yang mendapat giliran akan berhasil memanggil monsternya jika memenuhi kondisi sesuai game rule. Parameter dari constructor class ini adalah waktu penampilan splash screen tersebut dalam satuan milisecond. yaitu untuk mengakhiri giliran. } if(GameManager. Kali ini. Saat state menu = Monster. buatlah class baru bernama SplashScreen. Listing l adalah contoh method tersebut. Anda bisa mengisi alur yang terjadi pada inner class MapControl yang telah Anda buat sebelumnya. g. g. Jika pemain menekan tombol Z. Sejenis seperti method toInfo(). Anda perlu menambahkan baris ini pada bagian paling atas.End) { g. Setelah itu. Saat state menu = End. show. method showSplash() dipanggil untuk menampilkannya. Method tersebut terbagi 2. dan wait (menunggu). di InfoPanel ini event yang terjadi sesuai dengan state-nya. pengaturan ini sedikit berbeda dengan MapControl. Sedangkan pada saat state menu = Show. Lalu. Class tersebut dibuat inherit pada class JWindow milik library Java. Begitu kursor digerakkan. Perbedaan dengan KeyPressed() adalah method ini hanya menjalankan aksi dari satu tombol saja. Jika pemain menekan tombol Z. menu yang ditampilkan hanya satu. 300). yang dilakukan bisa pemanggilan. Listing j adalah contoh method-method yang digunakan. Listing m berikut ini salah satu contoh class SplashScreen sederhana. Aksi-aksi tersebut adalah move (berpindah). Semua status monster diubah Lebih Menarik dengan SplashScreen Untuk memperkuat kesan pertama pemain.map == State. showDamage = false. Sama seperti pada MapPanel. letakkan dalam method KeyTyped(). yaitu normal. karena semua aksi akan berdampak pada object di MapPanel. } menjadi true agar dapat melakukan aksi pada giliran berikutnya. setelah itu akan hilang dan berganti program utama. ss. 200. Anda tinggal mengatur tampilannya saja. Masing-masing aksi itu menjalankan method tertentu. Anda bisa menambahkan splash screen yang akan tampil saat game Anda dijalankan kali pertama.setColor(Color. Saat state menu = Summon. menu yang ditampilkan adalah aksi yang dapat dilakukan monster. menu yang ditampilkan adalah menu pemanggilan monster. Gambar 7. Penulis membuatnya seperti pada listing k. lalu membuat state map menjadi Attack agar monster bisa menyerang. sekarang saatnya Anda mengatur event pada InfoPanel. mode penyerangan. Font. Anda bisa menuliskan listing i di dalam inner class tersebut. atribut canSelected diubah menjadi false. Aksi move akan melakukan pengesetan area perpindahan. lalu membuat state map menjadi Move agar monster bisa melakukan perpindahan.BOLD. KeyPressed() akan terus berjalan selama tombol ditekan. Jadi penampilan damage hanya pada saat monster. Gunanya adalah untuk membuat object dari class tersebut dengan waktu penampilan selama 4 detik.setFont(new Font(Font. monster. Dalam program. atau mengakhiri giliran. Namun. method toMap() akan memindahkan fokus ke MapPanel. maka nilai curIndex yang merupakan lokasi kursor akan ditangkap dan di-switch sesuai monster yang dipanggil. Tambahkan juga baris di bawah ini ke dalam method keyPressed() pada inner class MapControl. Aksi wait akan membuat monster menunggu sampai giliran berakhir.

turn) { case Frost: turn = gm.setOwner(GameManager. Untuk melakukannya. coba Anda klik ganda file jar tersebut.Normal. map. Chara Frost = gm.SANS_SERIF. sehingga Winrar akan secara otomatis berjalan saat Anda mengeksekusi file jar. g. game yang Anda buat telah selesai. Tombol Clean and Build. 315. test.End) { Image turn = null.turn. Sebab tentunya akan sangat merepotkan jika Anda meminta teman Anda untuk mencoba sementara teman Anda tidak mempunyai NetBeans. Anda juga harus meng-copy juga folder map dan chara.resetAllChara(). Malang. seperti Winrar. Dengan ini. //untuk map Move. 159).PLAIN.drawString(Integer. dan build. Flare). Fungsi tombol tersebut adalah menghapus semua file hasil compile sebelumnya. dalam folder Anda hanya akan berisi file jar.getPosX().getCastle()).turn. File tersebut secara otomatis ditempatkan pada folder dist yang terletak di dalam folder project Anda. GameManager. . copy file berekstensi jar tersebut ke dalam folder Anda.1).turn).turn. Listing a package devilishchildren. folder map.menu = State. Jangan lupa untuk menghilangkan asosiasi file jar dengan software kompresi lain. //untuk map dan menu Attack.setChara(GameManager. GameManager.turn.Summon || GameManager.getPosY(). klik tombol Clean and Build Main Project pada toolbar NetBeans. 124). Anda akan menemukan file dengan nama yang sama seperti nama project Anda.getPosY(). game yang telah Anda buat telah selesai.setChara(GameManager.setOwner(GameManager. Fauzil Haqqi adalah Mahasiswa Teknik Informatika.getWidth() . g.menu == State. GameManager. Coba Anda mainkan bersama teman.BLACK).drawString(Integer. //untuk menu } GameManager.changeTurn().setPos(0. Anda juga bisa mengembangkan dasar pembuatan game strategi ini ke tahap yang lebih lanjut.setColor(Color. 220. GameManager.setFont(new Font(Font . map.Normal || GameManager. map.getPosX(). 124). //untuk menu End.getMana()). Di dalam folder dist tersebut. GameManager.turn. Windra Swastika adalah Dosen Teknik Informatika dan M. 260. g.loadImage(GameManager .turn). Tapi Anda perlu melakukan sesuatu agar game tersebut dapat dimainkan tanpa harus membuka source-nya menggunakan NetBeans. kemudian meng-compile-nya ulang dan menempatkannya dalam sebuah file kompresi Java berekstensi jar. 0). di level folder yang sama dengan folder src.map = State.turn. 18)). dan folder chara. Sebab Winrar akan membaca file jar sebagai file kompresi biasa. Buatlah folder baru dengan lokasi dan nama sesuka Anda.turn = Player.getHeight() . Chara Flare = gm.1.setPos(map.turn. Listing c g. Semua komputer dapat menjalankannya asalkan komputer tersebut telah terinstal JRE (Java Runtime Environment). Jika semua yang Anda lakukan sudah benar. Flare.drawString(Integer. Jika masih ada pertanyaan. Font.07/2009 CODING KNOW-HOW 117 Mendistribusikan Hasil Secara garis besar.toString(GameManager . Keduanya dari Universitas Ma Chung. switch(GameManager.changeTurn().toString(GameManager . if(GameManager. Anda bisa menghubungi blog milik penulis yang terletak pada kolom lebih lanjut.Frost. GameManager.loadChara(“Flare”). Frost. //untuk map Summon.loadChara(“Frost”).getMonster()).turn. Setelah selesai mengatur semuanya. maka program akan berjalan dengan tampilan awal splash screen milik Anda. g. dengan skenario yang lebih baik dan perhitungan statistik yang lebih mantap dalam pembuatan sistemnya.menu == State. Gambar 8. Jadi.Normal. Caranya adalah Anda harus membuat versi jar dari program Anda. Frost). //untuk menu Monster. //untuk menu Show.turn. Tidak hanya itu. Setelah itu.toString(GameManager Listing b GameManager. yang berisi file-file teks informasi map dan chara Anda (bukan folder yang berisi gambar) ke dalam folder milik Anda tersebut. GameManager. map. public enum State { Normal. Folder tersebut akan Anda gunakan sebagai folder utama tempat game Anda.menu == State.

g. } } } g. Font. g. this).menu == State.turn) { case Frost: menu = gm.PLAIN. } g. 13.VK_UP: if(curIndex > 0) { --curIndex. 480.Show || GameManager.drawImage(curImg.menu == State. break.drawString(Integer.End) { g. if(GameManager. 124).Attack) { g. g. if(GameManager.getMaxHP()). public void keyReleased(KeyEvent e) { } Listing g public void move() { . 30)). break. case Flare: menu = gm.map == State. this). } break. g. 10. switch(keyCode) { case KeyEvent.drawString(Integer.drawImage(menu.menu) { case Summon: if(curIndex < 3) { Listing e if(GameManager.imagesFolder.png”).getDEF()).drawImage(menu. 54). “menuMonster. “turnFlare.loadImage(GameManager . case KeyEvent. “menuEnd.loadImage(GameManager .SANS_SERIF.getName().Summon) { Image menu = null.Summon || Listing d if(GameManager.getFace().drawString(Integer.SANS_SERIF. 18)).toString( actChara.png”). 89).menu == State.toString( actChara. 159).loadImage(GameManager .drawImage(turn.118 KNOW-HOW CODING 07/2009 . curPos + curIndex * curMov.Monster || GameManager. } } } ++curIndex.png”). this). 190. g.imagesFolder. this).map == State.drawString(Integer.setFont(new Font(Font .imagesFolder.png”). } } if(GameManager.getHP()) + “ / “ + Integer.setFont(new Font(Font .loadImage(GameManager .getATK()). Listing f private class InfoControl implements KeyListener { public void keyTyped(KeyEvent e) { } public void keyPressed(KeyEvent e) { int keyCode = e. 13. Font.getMOVr()).Monster || GameManager.png”). break. break. switch(GameManager. break.menu == State. 553.toString( actChara.imagesFolder.Move || GameManager. 10.Monster) { Image menu = gm. g. 89). 480. 553.PLAIN. “menuFrost.toString( actChara.drawString(actChara.imagesFolder. 553.getATKr()).png”). 480. case Monster: if(curIndex < 2) { ++curIndex. 10. g.loadImage(GameManager .drawString(Integer. } } } GameManager. g.toString( actChara. 480.menu == State.imagesFolder. g. “menuFlare.menu == State. } } repaint(). this). 10.toString( actChara. 44). 553. 260. this). case Flare: turn = gm. } g.getKeyCode(). break.End) { Image menu = gm. 13.drawImage(menu. “turnFrost.menu == State.menu == State.drawImage(actChara.VK_DOWN: switch(GameManager.

menu = State. case ‘x’: GameManager. aChara).loadImage( GameManager. isGameOver().menu = State. curPosY). if(actChara. map.isCastle(curPosX.getOwner(curPosX.turn == Player. if(startX == GameManager.getKeyChar().infoUpdate(). Image img = gm.turn && actChara.turn.setCanMove(false).toInfo(null). Chara dChara = map.Show.End. Listing i case Attack: . GameFrame.menu = State. GameFrame. int range = new Random(). case ‘x’: map. if(map.setDeath(curPosX. GameManager. } break. curPosY.changeTurn(). curPosY. curPosY) == GameManager.setChara(startX.isArea(curPosX.png”. map. curPosY.turn.07/2009 CODING KNOW-HOW 119 Chara mChara = map.getChara(curPosX.getChara(startX.nextBoolean() ? range : -range.isUpperCase(keyChar)) { keyChar = Character.Summon.Monster.getChara( startX.setAreaFalse().turn. } map.Normal. break. GameManager. showDamage = true.Monster. curPosY. startY)) { Chara actChara = map . curPosY) != GameManager. map. if(map. if(GameManager. startY).isCanSelected()) { GameManager. String name.changeHP(-damage). Listing h public void attack() { Chara aChara = map.nextInt(damage/8). } GameFrame. img).getDEF().map = State. GameManager. GameFrame. } switch(GameManager.toInfo(null). mChara.Normal.getOwner() == GameManager. } break. map. if(Character. GameManager.setCanAttack(false). range = new Random().dChara. startY.changeCastle(1).setAreaFalse().infoUpdate(). map.map) { case Normal: switch(keyChar) { case ‘z’: setStart(). } map.changeTurn(). if(map.getOwner(curPosX.isChara(startX. } else { GameManager.setChara(curPosX. startY).setImage(curPosX. damage = aChara.getPosY()) { GameManager.toInfo(actChara). startY)) { GameManager. mChara). damage += range. GameManager. dChara.turn) { GameManager.mapFolder.getChara(startX. } if(map. curPosY).menu = State.getATK() * 3 . curPosY) && map.Frost) { name = “castleFrost. name).getChara(startX. curPosY).menu = State.setPos(curPosX. break.turn. case Move: switch(keyChar) { case ‘z’: if(map.turn).turn) { GameManager. dChara).setOwner(curPosX. GameFrame.map = State. startY.changeCastle(-1). null).toLowerCase(keyChar).png”. } else { name = “castleFlare.toInfo(map.isSummonArea( startX. map. } GameManager. GameFrame.setChara(curPosX. } } public void keyTyped(KeyEvent e) { char keyChar = e. } } break. } break. startY)).setChara(startX.getPosX() && startY == GameManager. aChara. startY).turn. curPosY)) { move().

turn. startX. GameManager. break. if(actChara.setAreaFalse().isArea(curPosX.getChara(startX. } } ip. ip. GameManager.setChara(startX.map = State. GameManager.Normal.loadChara(name). break.Attack.changeTurn().getMana() >= mana && GameManager. startX. int mana = newChara. startY).getChara( startX. map.changeMana(100 GameManager.map != State. Listing l public void moveMode() { Chara actChara = map.toMap(curIndex). if(GameManager. if(Character. wChara. startY.getMonster()) { newChara.changeMana(7).setOwner(GameManager. map. } } public void waitTask() { Chara wChara = map. switch(keyChar) { case ‘z’: GameFrame.Normal.map = State.setAreaFalse().map = State. if(GameManager.End) { GameManager.setFocusable(false).turn.getChara(startX. } public static void toMap() { mp.map = State. } public void endTask() { map.Normal. } } break.turn. newChara).isUpperCase(keyChar)) { Listing j public void summonTask(String name) { Chara newChara = gm.Move. wChara). startY)).turn. case ‘x’: GameFrame. curIndex = 0.turn. startY). switch(GameManager. startY. map. public void attackMode() { Chara actChara = map.toInfo(map. } GameManager.Monster. if(GameManager.turn).Normal. } } } } } keyChar = Character.resetAllChara(). GameFrame.getATKr(). } break. .isCanAttack()) { GameManager. break. map.turn. } Listing k public void keyTyped(KeyEvent e) { char keyChar = e.infoUpdate().getMana()).getMana().isCanMove()) { GameManager.toMap(). } else { GameManager.getChara(startX. break.getCastle() > GameManager.turn.menu = State. GameManager. } } public static void toMap(int index) { mp. 0).setChara(startX.menu = State.changeMonster(1).getMOVr().toLowerCase(keyChar). case ‘x’: map.menu) { case Summon: switch(GameManager. curIndex = 0.getMana() > 93) { GameManager. map. if(actChara.getKeyChar().120 KNOW-HOW CODING 07/2009 switch(keyChar) { case ‘z’: if(map. GameFrame.setFocusable(false).setMoveArea(actChara.setFocusable(true). startY.turn.turn.setAttackArea(actChara.turn) { case Frost: switch(index) { case 0: mp.map = State.setFocusable(true).summonTask(“Durandal”).changeMana(-mana). curPosY)) { attack(). GameManager. startY. startY). 0).setCanSelected(false). GameManager.

import java. break.getScreenSize(). width. (scrHeight-height)/2. height). case Monster: switch(index) { case 0: mp.swing. break.kit . “splash. content.println(e.org import java.BorderLayout. gm = new GameManager(). 50. content. case 1: mp. case 3: mp.awt.Normal.imagesFolder. import javax.out. Color ora = new Color(50.Color.menu = State. 50.swing. } break. public class SplashScreen extends JWindow { private int duration.summonTask(“Kraken”). int height = 170. case Flare: switch(index) { case 0: mp.WHITE). int scrHeight = (int)gm. case 2: mp.setBorder(BorderFactory .ImageIcon. break.gamedevid. import javax.getMessage()). import javax.summonTask(“Minotaur”). case 3: mp.swing. } GameManager. break.summonTask(“Tiamat”). } break. } } Listing m package devilishchildren.swing. } import java.swing.07/2009 CODING KNOW-HOW 121 case 1: mp.setBackground(Color.createLineBorder(ora. } break.summonTask(“Phoenix”). content. } catch(Exception e) { System. break. LEBIH LANJUT http://fauzilhaqqi.JWindow. int scrWidth = (int)gm.Image. } break.getHeight(). mp. break.png”). setBounds((scrWidth-width)/2. break. } setVisible(false). case 1: mp.getWidth(). 10)).repaint().summonTask(“Hollow”). try { Thread.repaint().attackMode().awt. Image splash = gm. break.awt.kit .sleep(duration).BorderFactory. import javax.net http://www. import javax.loadImage(GameManager . JLabel logo = new JLabel (new ImageIcon(splash)). private GameManager gm.moveMode(). BorderLayout. public SplashScreen(int d) { duration = d. break.add(logo. case End: mp.com http://www. ip. .CENTER). int width = 620.JLabel. setVisible(true).blogspot. case 2: mp. case 2: mp.gamedev.getScreenSize().summonTask(“Death”). break. 255). } public void showSplash() { JPanel content = (JPanel)getContentPane().JPanel.summonTask(“Succubus”).waitTask().endTask().