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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful