1. 程式人生 > >【20171210】用java實現簡易貪吃蛇

【20171210】用java實現簡易貪吃蛇

一、各個類之間的關係

之前學軟體工程不努力,現在徒傷悲啊,不會用visio畫類圖…大致的類、方法之間的關係就是這樣。

這裡寫圖片描述

二、遊戲介面

這裡寫圖片描述

三、具體實現:

1.遊戲視窗

要考慮的問題有:
a.視窗的名字,大小,位置;
b.新增的元件有哪些;
c.設定點選關閉視窗之後不僅要關掉視窗還要消除後臺程序;
如何看後臺程序是否還在執行?
開啟工作管理員:
這裡寫圖片描述

//首先對於第一個問題:
//便於以後方便修改設定為static final
public static final int WINDOWWIDTH = 640;
public static final int WINDOWHEIGHT = 480
; public static final int GAMETABLEWIDTH = 400;//遊戲區域,也就是藍色部分; this.setLayout(null);//視窗預設的是邊佈局管理器,要自定義大小等就需要將引數設定為null,然後自己設定setBounds; this.setTitle("貪吃蛇");//用這個函式可以設定視窗名字; this.setBounds(20,20,width,height);//這個函式可以設定視窗大小及其位置; //x,y為左上角在容器(視窗)中的座標,我們設定成20,20; //但是要視窗在螢幕居中顯示的話,就要用到另一個函式獲取螢幕大小; Demension d = ToolKit.getDefaultToolKit().getScreenSize(); //於是,上面的setBounds的引數設定:width = (d.width-WINDOWWIDTH )/2;height = (d.height - WINDOWHEIGHT)/2;
//要新增的元件有:
private JButton btStart;//開始
private JButton btPause;//暫停
private JButton btStop;//結束
private label lbScore;//得分
private GreedySnakeGameTable gsgt;//遊戲區域,畫布(canvas)型別

//新增元件三部曲:
//1.new出來
btStart = new JButton("開始");
//2.設定邊界
btStart.setBounds(500,50,60,20);
//3.新增元件到視窗
this.add(btStart);
//4.新增元件觸發事件(可有可無)
btStart.addActionListener(new
ActionAdapter(){ gsgt.start();//這個start方法在GreedySnakeGameTable類中定義; //gsgt.pause(); //gsgt.stop(); }) //其他兩個按鈕類似就不贅述了; //分數標籤的設定 lbScore = new JLabel("0");//初始為0 lbScore.setBounds(500, 200, 60, 20); this.add(lbScore); //要是鍵盤可控顯示的數字的話就要用textField,例如在控制計算器的運算元; //遊戲區域 gsgt = new GreedySnakeGameTable(); gsgt.setBounds(20, 20, GAMETABLEWIDTH + 1, GAMETABLEWIDTH + 1);//這裡加一我也不記得為什麼了哈哈哈 this.add(gsgt);
//對於後臺執行緒的關閉處理:
//第一種方法:新增一個監聽器,然後覆蓋視窗監聽器裡的closed方法,這裡使用的是匿名類匿名物件的方法,適用於只出現一次的類,還適用於覆蓋接口裡的方法,因為介面的物件不能new;
this.addWindowListener(new WindowAdapter(){
    public void windowClosed(WindowEvent arg0) {
        System.exit(0);             
    }
})
第二種:
 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

2.遊戲的各個組成

要考慮的問題有:
1.蛇如何產生,大小;
2.食物的產生,而且如何保證不長在蛇身上;
3.蛇定義好之後如何顯示;
4.食物的顯示;
5.蛇如何移動;
6.如何使鍵盤可以控制蛇的移動;
7.如何實現多次連續按下鍵盤,蛇的加速;
8.關於蛇的優化,例如撞牆會GAME OVER,自己撞自己會GAME OVER;
9.對了還有遊戲畫布的基本設定,顏色等;

//1.蛇的產生:考慮到蛇是變長的,設定成陣列不好定義陣列大小,因此設定為由點Point組成的集合ArrayList;然後用一個for迴圈,一段一段顯示蛇的身體;

private void initSnakeBody()
{
    sb = new ArrayList<Point>();
    for(int i = 0;i < 4;i++)
    {
        sb.add(new Point(GAMEWIDTH/2+1-i,GAMEWIDTH/2));
        //遊戲畫布400,邏輯上分成20個小格子,每個格子的邊長是GAMEWIDTH = 20,為了讓其居中顯示;
    }
}
//2.食物的產生:食物是隨機產生的,也是一個點,有x和y座標;還要確保食物不長在蛇身上;
private void initFood()
{
    int x = rand.nextInt(GAMEWIDTH);
    int y = rand.nextInt(GAMEWIDTH);//這裡nextInt(n)產生一個0-n的隨機數;
    while(true)
    {
        for(int i = 0;i < sb.size();i++)
        {
            Point p = sb.get(i);//這裡get是Point裡的一個方法,返回點的橫縱座標;
            if(p.x == x&&p.y == y)
            {
                break;
            }
        }
        if(i == sb.size())
        {//如果是正常退出for迴圈
            break;
        }
        //如果是因為if語句退出的迴圈,則重新產生食物點;
        int x = rand.nextInt(GAMEWIDTH);
        int y = rand.nextInt(GAMEWIDTH);
    }
    food = new Point(x, y);
}
//3.蛇的顯示:設定蛇的顏色,主要由兩個函式實現:
public void PaintSnake(Graphics g)
{
    for(int i = 0;i < sb.size();i++)
    {
        g.setColor(new Color(255,0,0));
        //這裡三個引數為RGB值,可以自己指定;
        p = sb.get(i);
        g.fillOval(p.x*SNAKEBODYWIDTH,p.y*SNAKEBODYWIDTH,SNAKEBODYWIDTH,SNAKEBODYWIDTH)
    }
}
//4.畫食物也是一樣的:
private void paintFood(Graphics g) {
    g.setColor(new Color(200, 0, 0));
    g.fillOval(food.x * SNAKEBODYWIDTH, food.y * SNAKEBODYWIDTH, SNAKEBODYWIDTH, SNAKEBODYWIDTH);

    }
//顯示在畫布上
public void paint(Graphics g) {
    g.setColor(new Color(55, 196, 185));
    g.fillRect(0, 0, Game.GAMETABLEWIDTH, Game.GAMETABLEWIDTH);//覆蓋Paint方法,自定義顏色大小;
    paintFood(g);
    paintSnake(g);
    }
//5.關於蛇的移動:思路是這樣的,砍蛇尾,長蛇頭,如果吃到食物了,就不砍尾;還有,關於撞牆Over的問題;
//還需要考慮的問題是,蛇移動的方向的確定,這裡用一個數組來指定;
Private void SnakeMove()
{
    int[] xlnc = {1,0,-1,0};//右下左上座標的變化;
    int[] ylnc = {0,-1,0,1};
    Ponit h = sb.get(0);//得到蛇頭的點的座標;
    if(x >= GAMEWIDTH || y>=GAMEWIDTH || x <0 || y <0)
    {
        timer.stop();
        this.removeKeyListener(mka);
        //遊戲結束後取消鍵盤控制;
    }
    if(x == food.x&&y == food.y)
    {
        initFood();
    }
    else
    {
        sb.remove(sb.size() - 1);
    }
    sb.add(0,new Point(x,y));
}
6.鍵盤控制:設定一個keyListener來轉換方向,還需要考慮的問題是,加速的問題;
private class MyKeyAdapter extends KeyAdapter {
        public void keyPressed(KeyEvent e) {
            switch (e.getKeyCode()) {
            case KeyEvent.VK_RIGHT:
                changeDirection(0);
                break;
            case KeyEvent.VK_UP:
                changeDirection(1);
                break;
            case KeyEvent.VK_LEFT:
                changeDirection(2);
                break;
            case KeyEvent.VK_DOWN:
                changeDirection(3);
                break;
            }
}

private void changeDirection(int dir) {
        if (direction != (dir + 2) % 4) {
            if (direction == dir) {
                snakeMove();//如果重複按下同一個方向鍵,就移動兩倍
                repaint();
            }
        direction = dir;
        }
    }
}
//7.對定時器的設定(控制蛇移動的速度,每移動一次,重繪重新整理一次)
private void initTimer() {
    timer = new Timer(300, new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            snakeMove();
            repaint();
        }
    });
}
//這裡對各個按鈕的事件實現:
public void start()
 {
    this.requestFocus();
    timer.start();
}

public void pause() 
{
    this.requestFocus();
    timer.stop();
}

public void stop() 
{
    this.requestFocus();
    timer.stop();
}