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.

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

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

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

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

break.separatorChar + mapFolder + name. switch(ch) { case ‘F’: index = 0. line. int y. “tileWater. public Image getImage(int x. “tileForest.png”).close().length. loadImage(mapFolder.getCause()).max(width. } String path = System.add(line). int height) { tileImage = new Image[width][height]. } while (true) { String line = reader. “castleGray. “tileSand. case ‘S’: index = 2.length()). x < width.println(iOException. Image[] tileImage = { loadImage(mapFolder. } try { BufferedReader reader = new BufferedReader( new FileReader(new File(path))). //1 import java.charAt(x).out. case ‘W’: index = 3. case ‘C’: . “tileGrass. break.png”). case ‘G’: index = 1. int index. } } } } } width = Math.length. int y) { return tileImage[x][y]. loadImage(mapFolder. Listing g-Load Map public HexaMap loadMap(String name) { ArrayList lines = new ArrayList(). int width = 0.png”).awt. for(int y = 0. public int getHeight() { return tileImage[0].png”) //4 public HexaMap(int width.size(). int height = 0. }. //2 loadImage(mapFolder. } } break. public int getWidth() { return tileImage. y++) { String line = (String)lines. HexaMap newMap = new HexaMap(width.getProperty(“user.png”). y < height.05/2009 CODING KNOW-HOW 95 Listing e-HexaMap 1 package devilishchildren.readLine().dir”) + File. 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. height). break. public void setImage(int x. //0 loadImage(mapFolder. for(int x = 0.startsWith(“#”)) { lines. height = lines.get(y).Image. Image img) { tileImage[x][y] = img. //3 public class HexaMap { private Image[][] tileImage. if (line == null) { reader. if (!line. x++) { char ch = line. break.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful