Translate

Thursday, 13 December 2012

Game Java Ular


BAB I
DASAR TEORI


1.        Java Swing
GUI merupakan aplikasi yang memiliki antarmuka grafis yang menarik
dengan komponen-komponen GUI didalamnya.
Contoh komponen GUI adalah : form, button, checkbox, radiobutton, listbox,
textbox dsb. Pada bahasa pemrograman java, komponen GUI dan
pemrograman GUI menggunakan kelas-kelas yang terdapat dalam package
swing. Package swing merupakan pengembangan dari package awt pada java
versi sebelumnya.
2.        Jframe
Berfungsi sebagai layar utama (main window) untuk menjalankan aplikasi java GUI,
Dapat diletakkan komponen GUI sbg interface, antara lain TextField, Button,
CheckBox dan lainnya.
3.        JLabel
Label menampilkan teks pada layar dengan konstruktor sbb :
JLabel();
JLabel(String teks);
JLabel(String teks, int alignment);








BAB II
PEMBAHASAN
Main.java
/**
 *
 * @author APOY
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
               System.out.println("Hello World");
    }

}
Jalankan kelas ini, jika tidak ada error lanjut ke langkah berikutnya. Buatlah kelas SegmentUlar. Kelas ini merepresentasikan Segment dari ular. Saat ular makan sesuatu, pastinya jumlah segment akan bertambah. Semakin Segment bertambah, semakin panjang pula Ular tersebut. Dalam kelas Ular, segment ini berupa ArrayList.




import java.awt.Color;

/**
 *
 * @author APOY
 */
public class SegmentUlar {

    private int x,y;
    private Color color;

    public SegmentUlar() {
    }

    public SegmentUlar(int x, int y, Color color) {
        this.x = x;
        this.y = y;
        this.color = color;
    }

    public Color getColor() {
        return color;
    }

    public void setColor(Color color) {
        this.color = color;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public void setPosition(int x,int y) {
        this.x = x;
        this.y = y;
    }
}
UlarDirection ini untuk merepresentasikan arah dalam bentuk angka / integer. Sebenarnya representasi arah bisa berupa apa saja, seperti char, String, byte, dan sebagainya. Namun untuk mempermudah logika, kita menggunakan angka.
/**
 *
 * @author APOY
 */
public class UlarDirection {
    public static final int KIRI=0;
    public static final int KANAN=1;
    public static final int ATAS=2;
    public static final int BAWAH=3;
}
Kelas ini akan ditampilkan di layar mirip dengan SegmentUlar, oleh karena itu kelas ini meng-extends kelas SegmentUlar. Dalam kelas ini terdapat attribut GameFrame. GameFrame ini adalah frame utama di program ini. Jangan terlalu dirisaukan mengenai kelas ini, dan jangan terlalu heran jika terdapat error, karena memang kita belum membahas mengenai GameFrame.
import java.awt.Color;
import java.util.Random;

/**
 *
 * @author APOY
 */
public class Makanan extends SegmentUlar {
    private GameFrame gameFrame;
    private Random randomPosition;
    private int availablePos[];
    public Makanan() {
        randomPosition = new Random();

        this.setX(-99);
        this.setY(-99);
        this.setColor(Color.RED);
    }
    public Makanan(GameFrame gameFrame) {
        this();
        this.gameFrame=gameFrame;
    }
    public Makanan(int x, int y, Color color) {
        this();
        this.setX(x);
        this.setY(y);
        this.setColor(color);
    }

    public void initAvailablePos() {
        if (this.getGameFrame()==null) return;
        availablePos=new int[2];
        availablePos[0]=(this.getGameFrame().getWidth()/this.getGameFrame().getUlar().getUkuranSegment())-2;
        availablePos[1]=(this.getGameFrame().getHeight()/this.getGameFrame().getUlar().getUkuranSegment())-2;
    }

    public GameFrame getGameFrame() {
        return gameFrame;
    }

    public void setGameFrame(GameFrame gameFrame) {
        this.gameFrame = gameFrame;
    }

    public Random getRandomPosition() {
        return randomPosition;
    }

    public void setRandomPosition(Random randomPosition) {
        this.randomPosition = randomPosition;
    }

    public void updatePosition() {
        int x=0,y=0;
        UlarNagaPanjangnya ular = this.getGameFrame().getUlar();
        boolean selesai=true;
        while (true) {
                       selesai=true;
            x=this.getRandomPosition().nextInt(availablePos[0])*ular.getUkuranSegment();
            y=this.getRandomPosition().nextInt(availablePos[1])*ular.getUkuranSegment();
            // cek posisi xy
            for (SegmentUlar su : ular.getUlarSegments()) {
                if (x==su.getX() && y==su.getY()) {
                    selesai=false;
                    break;
                }
            }
            if (selesai) break;
        }
        this.setX(x);
        this.setY(y);
    }
}
Di kelas makanan terdapat method update position. Makanan akan mencari posisi baru setelah posisi yang sekarang berhasil diraih oleh ular. Tentu posisi yang sekarang tidak boleh keluar boundary, yaitu berada dalam batas jendela program. Selain itu posisi yang baru juga tidak boleh berada di dalam badan ular. Untuk menentukan posisi, kelas makanan pertama-tama harus menentukan berapa banyak posisi yang bisa digunakan. Masalah ini dipecahkan dengan membagi lebar dan tinggi jendela program dengan ukuran segmen ular. Tentu ukuran makanan dan ukuran segmen haruslah sama. Rumus pencarian jumlah posisi menyamping dan meninggi yang bisa digunakan adalah sebagai berikut,
jumlah_pos_x=floor(lebar_layar/ukuran_segmen)
jumlah_pos_y=floor(tinggi_layar/ukuran_segmen)
Implementasi dari rumus di atas adalah
availablePos[0]=(this.getGameFrame().getWidth()/this.getGameFrame().getUlar().getUkuranSegment())-2;
availablePos[1]=(this.getGameFrame().getHeight()/this.getGameFrame().getUlar().getUkuranSegment())-2;
Mengapa perlu dikurangi 2? karena jika tidak dikurangi, posisi makanan bisa berada di luar jendela, karena koordinat makanan ditentukan pada sebelah kiri atas makanan tersebut. Bisa dibayangkan bukan jika tidak dikurangi? Makanan akan di luar batas jika koordinat secara kebetulan berada persis pada garis jendela sebelah kanan atau sebelah bawah.
Kemudian makanan juga harus berada di luar badan ular, jika berada di badan ular, cari lagi posisi yang tepat dengan merandom ulang koordinat makanan.
while (true) {
        selesai=true;
        x=this.getRandomPosition().nextInt(availablePos[0])*ular.getUkuranSegment(); // random pencarian koordinat x
        y=this.getRandomPosition().nextInt(availablePos[1])*ular.getUkuranSegment(); // random pencarian koordinat y
        // cek posisi xy
        for (SegmentUlar su : ular.getUlarSegments()) {
               if (x==su.getX() && y==su.getY()) {   // jika berada di tubuh ular
                       selesai=false; // jangan keluar, random lagi
                       break;
               }
        }
        if (selesai) break;
}
Selanjutnya, definisikan kelas Ular. Kelas ini mengatur jalannya ular, mengatur segment ular, mengatur makanan ular, dan mengatur head collision, yaitu kepala ular terbentur badannya atau ruang batas permainan. Intinya di kelas ini adalah representasi dari ular dan seluruh kegiatan ular. Berikut adalah source code kelas Ular
import java.awt.Color;
import java.util.ArrayList;
import java.util.Random;

/**
 *
 * @author APOY
 */
public class UlarNagaPanjangnya {

    private ArrayList ularSegments;
    private int arah,ukuranSegment,nilai;
    private GameFrame gameFrame;
    private int posisiEkor[];

    public UlarNagaPanjangnya(GameFrame gameFrame) {
        this.gameFrame=gameFrame;
        // tentukan arah awal
        Random r = new Random();
        this.arah=r.nextInt(3)+1;
        // tentukan ukuran segment
        this.ukuranSegment=10;

        refresh();
    }

    public void refresh() {
        if (ularSegments!=null) {
            while (ularSegments.size()>0) {
                SegmentUlar su = ularSegments.remove(0);
                su=null;
            }
        }
        posisiEkor=null;
        ularSegments=null;
        System.gc();

        // persiapkan si ular
        ularSegments = new ArrayList(100);
        SegmentUlar su = new SegmentUlar(100, 100, Color.BLUE);
        ularSegments.add(su);
        for (int i=1; i0; i--) {
            SegmentUlar depan=ularSegments.get(i-1);
            SegmentUlar belakang=ularSegments.get(i);
            belakang.setX(depan.getX());
            belakang.setY(depan.getY());
        }
        // posisi kepalanya
        SegmentUlar kepala=ularSegments.get(0);
        if (this.getArah()==UlarDirection.KANAN) {
            kepala.setX(kepala.getX()+this.getUkuranSegment());
        }
        else if (this.getArah()==UlarDirection.KIRI) {
            kepala.setX(kepala.getX()-this.getUkuranSegment());
        }
        else if (this.getArah()==UlarDirection.BAWAH) {
            kepala.setY(kepala.getY()+this.getUkuranSegment());
        }
        else if (this.getArah()==UlarDirection.ATAS) {
            kepala.setY(kepala.getY()-this.getUkuranSegment());
        }
        makan();
        return !headCollision();
    }

    public void makan() {
        SegmentUlar kepala=ularSegments.get(0);
        if (kepala.getX()==this.getGameFrame().getMakanan().getX() &&
            kepala.getY()==this.getGameFrame().getMakanan().getY()) {
            ularSegments.add(new SegmentUlar(this.getPosisiEkor()[0], this.getPosisiEkor()[1], Color.GREEN));
            // pindahkan posisi makanan
            this.getGameFrame().getMakanan().updatePosition();
            this.setNilai(this.getNilai()+10);
        }
    }

    public boolean headCollision() {
        SegmentUlar kepala=ularSegments.get(0);
        for (int i=4; i=this.getGameFrame().getWidth()-1)
            return true;
        if (kepala.getY()=this.getGameFrame().getHeight()-1)
            return true;
        return false;
    }

    public int getArah() {
        return arah;
    }

    public void setArah(int arah) {
        this.arah = arah;
    }

    public int getUkuranSegment() {
        return ukuranSegment;
    }

    public void setUkuranSegment(int ukuranSegment) {
        this.ukuranSegment = ukuranSegment;
    }

    public ArrayList getUlarSegments() {
        return ularSegments;
    }

    public void setUlarSegments(ArrayList ularSegments) {
        this.ularSegments = ularSegments;
    }

    public GameFrame getGameFrame() {
        return gameFrame;
    }

    public void setGameFrame(GameFrame gameFrame) {
        this.gameFrame = gameFrame;
    }

    public int[] getPosisiEkor() {
        return posisiEkor;
    }

    public void setPosisiEkor(int[] posisiEkor) {
        this.posisiEkor = posisiEkor;
    }

    public int getNilai() {
        return nilai;
    }

    public void setNilai(int nilai) {
        this.nilai = nilai;
    }

}
Sama seperti sebelumnya, jangan terlalu pikirkan attribut GameFrame yang akan menimbulkan error jika di-compile. Di kelas tersebut ada beberapa method penting. Pertama adalah makan. Method ini memeriksa apakah kepala ular menyentuh makanan. Jika menyentuh maka segment ular ditambah, dan posisi makanan selanjutnya dipindahkan. Berikut adalah penjelasannya.
    public void makan() {
        SegmentUlar kepala=ularSegments.get(0);      // ambil bagian kepala
        if (kepala.getX()==this.getGameFrame().getMakanan().getX() &&
// jika posisi kepala == posisi makanan
            kepala.getY()==this.getGameFrame().getMakanan().getY()) {
            ularSegments.add(new SegmentUlar(this.getPosisiEkor()[0], this.getPosisiEkor()[1], Color.GREEN));       // tambahkan badan ular, letakkan di belakang
            // pindahkan posisi makanan
            this.getGameFrame().getMakanan().updatePosition();       // pindahkan posisi makanan
            this.setNilai(this.getNilai()+10);       // update nilai user
        }
    }
Kedua adalah method jalan. Method ini mengubah posisi ular berdasarkan arahnya saat ini. Jika sedang mengarah ke atas, maka nilai y berkurang, jika mengarah ke bawah nilai y bertambah, jika mengarah ke kiri nilai x berkurang, dan jika mengarah ke kanan nilai x bertambah.
    public void jalan() {
        // simpan ekor ular
        SegmentUlar ekor=ularSegments.get(ularSegments.size()-1);
        posisiEkor[0]=ekor.getX();
        posisiEkor[1]=ekor.getY();

        // pindahkan posisi badannya
        for (int i=ularSegments.size()-1; i>0; i--) {
            SegmentUlar depan=ularSegments.get(i-1);
            SegmentUlar belakang=ularSegments.get(i);
            belakang.setX(depan.getX());
            belakang.setY(depan.getY());
        }
        // posisi kepalanya
        SegmentUlar kepala=ularSegments.get(0);
        if (this.getArah()==UlarDirection.KANAN) {
            kepala.setX(kepala.getX()+this.getUkuranSegment());      // jika arah ke kanan nilai x bertambah
        }
        else if (this.getArah()==UlarDirection.KIRI) {
            kepala.setX(kepala.getX()-this.getUkuranSegment());      // jika arah ke kiri nilai x berkurang
        }
        else if (this.getArah()==UlarDirection.BAWAH) {
            kepala.setY(kepala.getY()+this.getUkuranSegment());      // jika arah ke bawah nilai y bertambah
        }
        else if (this.getArah()==UlarDirection.ATAS) {
            kepala.setY(kepala.getY()-this.getUkuranSegment());      // jika arah ke atas nilai y berkurang
        }
        makan();
        return !headCollision();
    }
Di dalam method jalan juga terdapat pemanggilan ke method makan. Jadi setiap ular berjalan akan diperiksa, apakah dia makan atau tidak.
Terakhir adalah method head Collision. Method ini untuk memeriksa apakah terjadi benturan antara kepala ular dengan dinding pembatas maupun dengan badannya sendiri. Jika terjadi benturan akan return true dan permainan selesai. Pemeriksaan benturan dilakukan secara rutin setiap ular berjalan di method jalan.
    public boolean headCollision() {
        SegmentUlar kepala=ularSegments.get(0);
        for (int i=4; i=this.getGameFrame().getWidth()-1)    // jika terjadi benturan dengan pembatas kanan / kiri return true
            return true;
        if (kepala.getY()=this.getGameFrame().getHeight()-1) // jika terjadi benturan dengan pembatas atas / bawah return true
            return true;
        return false;
    }
Sedangkan method refresh hanya untuk inisiasi memulai permainan saja yang terdiri dari inisiasi segmen ular (jumlah segmen ketika mulai permainan adalah 3), kemudian pencatatan posisi ekor pertama kali (ini diperlukan untuk mengetahui posisi segmen baru sesudah makan), dan terakhir adalah menge-set nilai awal peserta = 0.
    public void refresh() {
        if (ularSegments!=null) {
            while (ularSegments.size()>0) {
                SegmentUlar su = ularSegments.remove(0);
                su=null;
            }
        }
        posisiEkor=null;
        ularSegments=null;
        System.gc();

        // persiapkan si ular
        ularSegments = new ArrayList(100);
        SegmentUlar su = new SegmentUlar(100, 100, Color.BLUE);
        ularSegments.add(su);
        for (int i=1; i
Kelas UlarAction digunakan untuk mendeteksi adanya sentuhan pada tombol panah keyboard. Sentuhan ini menentukan arah gerak ular. Namun ular tidak bisa langsung berbelok berlawanan arah. Contoh ketika sedang berjalan ke kanan, kemudian pemain menekan tombol kiri, maka tidak ada pengaruhnya pada arah gerak ular.
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

/**
 *
 * @author APOY
 */
public class UlarAction implements KeyListener {

    private GameFrame gameFrame;

    public UlarAction(GameFrame gameFrame) {
        this.gameFrame=gameFrame;
    }

    public void keyTyped(KeyEvent e) {
        // do nothing
    }

    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode()==KeyEvent.VK_UP) {
            if (this.getGameFrame().getUlar().getArah()==UlarDirection.BAWAH) {
                return;
            }
            this.getGameFrame().getUlar().setArah(UlarDirection.ATAS);
        }
        else if (e.getKeyCode()==KeyEvent.VK_DOWN) {
            if (this.getGameFrame().getUlar().getArah()==UlarDirection.ATAS) {
                return;
            }
            this.getGameFrame().getUlar().setArah(UlarDirection.BAWAH);
        }
        else if (e.getKeyCode()==KeyEvent.VK_RIGHT) {
            if (this.getGameFrame().getUlar().getArah()==UlarDirection.KIRI) {
                return;
            }
            this.getGameFrame().getUlar().setArah(UlarDirection.KANAN);
        }
        else if (e.getKeyCode()==KeyEvent.VK_LEFT) {
            if (this.getGameFrame().getUlar().getArah()==UlarDirection.KANAN) {
                return;
            }
            this.getGameFrame().getUlar().setArah(UlarDirection.KIRI);
        }

    }

    public void keyReleased(KeyEvent e) {
        // do nothing
    }

    public GameFrame getGameFrame() {
        return gameFrame;
    }

    public void setGameFrame(GameFrame gameFrame) {
        this.gameFrame = gameFrame;
    }
}
Kelas ini meng-implements KeyListner agar progarm bisa mendengar sentuhan tombol user.
Sebagai media pengambaran, kelas CanvasUlar yang meng-extends JComponent dibuat. Dalam kelas ini terdapat method paint untuk menggambar ular, makanan, beserta nilai pemain saat ini. Kelas inilah yang akan ditempelkan ke Kelas GameFrame.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;

/**
 *
 * @author APOY
 */
public class CanvasUlar extends JComponent {

    private GameFrame gameFrame;

    public CanvasUlar(GameFrame gameFrame) {
        this.gameFrame=gameFrame;
    }

    public void paint(Graphics gg) {
        Graphics2D g = (Graphics2D)gg;

        // gambar ular
        UlarNagaPanjangnya ular=this.gameFrame.getUlar();
        for (SegmentUlar segment : ular.getUlarSegments()) {
            g.setColor(segment.getColor());
            g.fillRect(segment.getX(), segment.getY(), ular.getUkuranSegment(), ular.getUkuranSegment());
        }

        // tulis nilai
        g.setColor(new Color(0.5f, 0.3f, 0.9f));
        g.drawString("Nilai Anda : "+ular.getNilai(), 10, 30);

        // gambar makanan
        Makanan makanan = this.gameFrame.getMakanan();
        g.setColor(makanan.getColor());
        g.fillRect(makanan.getX(), makanan.getY(), ular.getUkuranSegment(), ular.getUkuranSegment());
    }

    public GameFrame getGameFrame() {
        return gameFrame;
    }

    public void setGameFrame(GameFrame gameFrame) {
        this.gameFrame = gameFrame;
    }
}
Agar bisa bergerak alias ber-animasi, maka perlu menggunakan thread. Thread ini diimplementasikan dengan membuat kelas yang meng-implements Runnable, dimana di dalam method ini terdapat method penting yang wajib ada pada kelas bertujuan thread, yaitu method run.
import javax.swing.JOptionPane;

/**
 *
 * @author APOY
 */
public class RunnableUlar implements Runnable {

    private GameFrame gameFrame;

    public RunnableUlar(GameFrame gameFrame) {
        this.gameFrame=gameFrame;
        this.getGameFrame().getMakanan().initAvailablePos();
    }

    public void run() {
        JOptionPane.showMessageDialog(gameFrame,"Bersiap...");
        JOptionPane.showMessageDialog(gameFrame,"Mulai!!!");
        boolean tetapBermain=true;
        while (tetapBermain) {
            this.getGameFrame().getUlar().refresh();
            this.getGameFrame().getMakanan().updatePosition();
            while (true) {
                //this.getGameFrame().getUlar().jalan();
                //if (this.getGameFrame().getUlar().headCollision()) {
                if (!this.getGameFrame().getUlar().jalan()) {
                    JOptionPane.showMessageDialog(gameFrame,
                            "Game Over!!\n\r Nilai Anda : "+this.getGameFrame().getUlar().getNilai());
                    int mulai=JOptionPane.showConfirmDialog(gameFrame,
                            "Apakah Anda Ingin Memulai Lagi Gamenya?","Mulai Lagi", JOptionPane.YES_NO_OPTION);
                    if (mulai==JOptionPane.NO_OPTION) {
                        tetapBermain=false;
                    }
                    break;
                }
                this.getGameFrame().getCanvasUlar().repaint();
                this.getGameFrame().getCanvasUlar().revalidate();
                try {
                    Thread.sleep(70);
                } catch (InterruptedException e) {
                }
            }
        }
        //throw new UnsupportedOperationException("Not supported yet.");
    }

    public void cekKepalaMakananUlar() {
    }

    public GameFrame getGameFrame() {
        return gameFrame;
    }

    public void setGameFrame(GameFrame gameFrame) {
        this.gameFrame = gameFrame;
    }

}
Dalam method run, terdapat pemanggilan method sleep di kelas Thread. Hal ini dilakukan agar ular bergerak sesuai ukuran waktu tersebut dalam satuan milidetik. “Thread.sleep(70);” artinya gerakan dilakukan setiap 70 mili detik. Fungsi ini harus dilengkapi dengan try-catch jika terjadi interupsi thread, yaitu InterruptedException. Bagaimana dengan while? kenapa harus 2 kali? coba pikirkan sendiri. Kenapa harus ada 2 while.
Kelas terakhir yang harus disiapkan adalah GameFrame. Disinilah semua aktivitas program dijalankan. Di sini pula frame dari program ditaruh.
import java.awt.BorderLayout;
import java.awt.Container;
import javax.swing.JFrame;

/**
 *
 * @author APOY
 */
public class GameFrame extends JFrame {
    private CanvasUlar canvasUlar;
    private UlarAction ularAction;
    private UlarNagaPanjangnya ular;
    private RunnableUlar runnableUlar;
    private Makanan makanan;

    public GameFrame() {
        super.setTitle("Permainan Ular APOY");
        initComponent();
        initAction();

        setLocation(50, 50);
        setSize(500, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        startGame();
    }

    private void startGame() {
        runnableUlar = new RunnableUlar(this);
        Thread thread = new Thread(runnableUlar);
        thread.start();
    }

    private void initComponent() {
        this.ular=new UlarNagaPanjangnya(this);
        this.canvasUlar = new CanvasUlar(this);

        Container con = this.getContentPane();
        con.setLayout(new BorderLayout());
        con.add(canvasUlar,BorderLayout.CENTER);

        // persiapkan makanan ular
        this.setMakanan(new Makanan(this));
    }

    private void initAction() {
        ularAction = new UlarAction(this);
        addKeyListener(ularAction);
    }

    public CanvasUlar getCanvasUlar() {
        return canvasUlar;
    }

    public void setCanvasUlar(CanvasUlar canvasUlar) {
        this.canvasUlar = canvasUlar;
    }

    public UlarAction getUlarAction() {
        return ularAction;
    }

    public void setUlarAction(UlarAction ularAction) {
        this.ularAction = ularAction;
    }

    public UlarNagaPanjangnya getUlar() {
        return ular;
    }

    public void setUlar(UlarNagaPanjangnya ular) {
        this.ular = ular;
    }

    public RunnableUlar getRunnableUlar() {
        return runnableUlar;
    }

    public void setRunnableUlar(RunnableUlar runnableUlar) {
        this.runnableUlar = runnableUlar;
    }

    public Makanan getMakanan() {
        return makanan;
    }

    public void setMakanan(Makanan makanan) {
        this.makanan = makanan;
    }
}
Terakhir kita tinggal menyempurnakan Main.java dengan menambahkan inisialisasi GameFrame sebagai frame utama. Berikut adalah versi lengkap dari Main.java
/**
 *
 * @author APOY
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        GameFrame gameFrame = new GameFrame();
        gameFrame.setVisible(true);
    }

}

Output Program

No comments:

Post a Comment