Membuat Game Menggunakan Java Dan Netbeans

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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful