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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful