本身動手豐衣足食寫java貪吃蛇

工做愈來愈忙了,沒有多少時間看書學習了,下班擠出時間寫一個貪吃蛇的小遊戲。固然還有許多瑕疵和不足,權當萌新學習交流之用。(吐槽:當年家人爲何要阻擋我學習計算機,報什麼鬼會計。話說有沒有自學編程的一塊兒交流交流)java

這個貪吃蛇有如下幾個類:正則表達式

  1. GameFrame(主遊戲窗口類)
  2. StartPanel(開始界面)

           遊戲開始界面:算法

       3.GameHelp(上圖的幫助)編程

    4.Info(上圖的關於)canvas

           5.OerationList(遊戲運行後右側信息欄,不要在乎類名,打錯了沒改-。-)數組

    遊戲運行畫面以下:網絡

           

             6.Location(座標類,必要時還會記錄信息)數據結構

              7.Snake(蛇的類,裏面包含一個內部類,方向枚舉類OritentionEum)dom

    8.Food(食物類)函數

1.GameFrame類代碼以下:

package 貪吃蛇;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import 貪吃蛇.Snake.OritentionEum;

//遊戲的主窗口
public class GameFrame {
    private Frame f ;             //其實GameFrame直接繼承Frame窗口就好
    private int fLocationX = 300;
    private int fLocationY = 50;
    private int fWidth = 450;        //窗口寬
    private int fHeight=450;        //窗口高
    private StartPanel sp;          //開始界面Panel
    private GameCanvas gc;        //貪吃蛇畫布
    private OerationList ol;    //相關信息
    private int rate = 5; 
    private Snake sk ;
    private Food fd;
        //窗口初始化
    public void frameinit() {
        f = new Frame("遊戲窗口");
        f.setLayout(null);     //不使用佈局管理,Panel用絕對定位
        sp = new StartPanel(this).panelInit();
        //sp =sp.panelInit();
        sp.setBounds(fWidth/3, 100, fWidth/3, fHeight/2);        //這個是相對於sp的上一級的component的
        f.add(sp);
        this.setAtt(fLocationX,fLocationY);
    }
        //窗口的屬性,單獨拿出來,在切換到遊戲運行時還要用
    public void setAtt(int x,int y) {
        if(f!=null) {
        f.addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }
        });
        f.setLocation(x, y);     //設置組件在桌面顯示的位置
        f.setSize(fWidth, fHeight);        //設置組件的大小 ,也可用setBounds同時設置位置和大小
        f.setVisible(true);
        }
    }
        //遊戲開始窗口
    public void createGame() {
        f.setVisible(false);
        int x=f.getX();
        int y = f.getY();
        f = new Frame("貪吃蛇遊戲開始");
        sk = new Snake();        //
        fd = new Food();
        this.setAtt(x,y);        //爲了隨時更新位置
        f.setLayout(null);
        gc = new GameCanvas();
        gc.canvasInit();            //因此
        gc.setBounds(0, 0, fWidth*2/3, fHeight);    //gc寬度爲300,高度爲450
        gc.addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                switch(e.getKeyCode()) {
                case KeyEvent.VK_UP: sk.setOritention(OritentionEum.UP);break;
                case KeyEvent.VK_DOWN: sk.setOritention(OritentionEum.DOWN);break;
                case KeyEvent.VK_LEFT: sk.setOritention(OritentionEum.LEFT);break;
                case KeyEvent.VK_RIGHT: sk.setOritention(OritentionEum.RIGHT);break;
                }
            }
        });
        gc.setFocusable(true);                //畫布獲取焦點
        f.add(gc);
        ol = new OerationList().listInit();
        ol.setBounds(310, 75, fWidth/4, fHeight*2/3);
        f.add(ol);
        new updateTimeThread().start();    //更新時間線程
        new updateCanvasThread().start();//繪畫線程更新
    }
    public int getWidth() {
        return f.getWidth();
    }
    public int getHeight() {
        return f.getHeight();
    }
    public int getX() {            //返回當前窗口的X座標
        return f.getX();
    }
    public int getY() {
        return f.getY();
    }
    //繪畫線程
    class updateCanvasThread extends Thread{
        public void run() {
            while(true) {
                gc.repaint();
                String sp = ol.getSpeed();
                try {
                switch(sp) {
                case "一級" : Thread.sleep(100);break;
                case "二級" : Thread.sleep(300);break;
                case "三級" : Thread.sleep(500);break;
                 }
                } catch(Exception e) {e.printStackTrace();}
            }
        }
    }
    //更新界面線程
    class updateTimeThread extends Thread{
        public void run() {
            while(true) {    
                try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ol.updateTime();
            }
        }
    }
    //畫布
    class GameCanvas extends Canvas{
        public GameCanvas canvasInit() {
            this.setVisible(true);
            return this;
        }
        public void update(Graphics g) {
            Image img = this.createImage(fWidth*2/3,fHeight);    //不能直接建立Image對象
            Graphics gg = img.getGraphics();
            gg.setColor(Color.GRAY);    //灰色
            gg.fillRect(0, 0, fWidth*2/3, fHeight);
            gg.setColor(Color.BLACK);
            gg.drawRect(10, 50, 250, 350);            //起點10,,50,寬 250 高350  70行 50列的矩陣 
            if(!sk.crashed()) {
            if(sk.eatFood(fd)) {        //蛇若是吃到食物就尾部增加
                fd.updateFood();
                sk.growTail();
                ol.updateScore();
            }
            sk.drawSnake(gg,rate);       //畫蛇的時候傳入一個rate來控制大小
            fd.drawFood(gg, rate);
            } else {            //遊戲結束畫面
                gg.setFont(new Font("微軟雅黑",2,15));            //設置字體
                gg.setColor(Color.WHITE);
                gg.fillRect(60, 175, 150, 40);
                gg.setColor(Color.RED);
                gg.drawString("遊戲結束  得分"+ol.getScore(), 80    , 200);
            }
            g.drawImage(img, 0, 0, this);
            paint(g);
        }
        public void paint(Graphics g) {
        }
    }
    public static void main(String[] args) {
        GameFrame game = new GameFrame();
        game.frameinit();
    }

}

2.startpanel代碼以下:

package 貪吃蛇;
import java.awt.Button;
import java.awt.GridLayout;
//panel是容器,默認爲FlowLayout佈局,不能獨立存在
//該Panel主要容納三個button,分別是  開始遊戲  幫助  關於
//Panel最後會被加入Frame
//使用了佈局管理,就不能使用絕對定位了。
import java.awt.Panel;

@SuppressWarnings("serial")
public class StartPanel extends Panel{
    private Button startGame;
    private Button help;
    private Button info;
    private GameHelp gh;    //點擊幫助按鈕彈出的Frame
    private Info ifo;        //關於的窗口
    private GameFrame gf;    //與主窗口關聯,遊戲開始按鈕 的時間監聽 執行的動做 與其相關
    public StartPanel(GameFrame game) {
        super();
        gf = game;
    }
    public StartPanel panelInit() {
        startGame  = new Button("開始遊戲");
        help = new Button("幫助");
        //由於 startPanel已經設置了佈局管理,因此setSize,setLocation,setBounds就失效了。
        //給startGame 按鈕增長監聽,直接用lambda ,ActionListener是個函數式接口
        startGame.addActionListener(e->{
            gf.createGame();
        });
        //給「幫助」按鈕加上點擊監聽
        help.addActionListener(e->{
            if (gh==null) {
            gh = new GameHelp();
            gh.helpInit(gf.getX(),gf.getY(),gf.getHeight());}
            else 
            gh.helpInit(gf.getX(),gf.getY(),gf.getHeight());
        });
        info = new Button("關於");
            //給關於按鈕添加監聽
        info.addActionListener(e->{
            if (ifo==null) {
            ifo = new Info();
            ifo.infoInit(gf.getX(),gf.getY(),gf.getHeight());}
            else 
            ifo.infoInit(gf.getX(),gf.getY(),gf.getHeight());
        });
        this.setLayout(new GridLayout(3,1,25,25));   //網格佈局,三行一列
        this.add(startGame);
        this.add(help);
        this.add(info);

        this.setVisible(true);
        return this;
    }
}

3.help和info

package 貪吃蛇;

import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

@SuppressWarnings("serial")
public class GameHelp extends Frame{

    public void helpInit(int x,int y,int height) {
        this.setBounds(x+height, y, height/2, height/2);
        this.setName("幫助");
        this.addWindowListener(new WindowAdapter() {
                //重寫closing方法
            public void windowClosing(WindowEvent e) {
                setVisible(false);//close();     //匿名內部類也是內部類,能夠直接調用外部類的方法和成員
            }
        });
        this.setVisible(true);
        this.repaint();  //paint方法只能是系統調用,咱們只能經過repaint來間接調用paint
    }
        //這裏其實應該用一個好的圖片(帶幫助)直接畫上最好
    public void paint(Graphics g) {
        g.drawString("貪吃蛇遊戲幫助", 50    , 50);
    }

}
package 貪吃蛇;
//info的窗口
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

@SuppressWarnings("serial")
public class Info extends Frame{
    public void infoInit(int x,int y,int height) {
        this.setBounds(x+height, y+height/2, height/2,height/2 );
        this.setName("關於");
        this.addWindowListener(new WindowAdapter() {
                //重寫closing方法
            public void windowClosing(WindowEvent e) {
                Info.this.setVisible(false);//close();     //匿名內部類也是內部類,能夠直接調用外部類的方法和成員
            }
        });
        this.setVisible(true);
        this.repaint();  //paint方法只能是系統調用,咱們只能經過repaint來間接調用paint
    }
        //這裏其實應該用一個好的圖片(帶幫助)直接畫上最好
    public void paint(Graphics g) {
        g.drawString("做者:CP", 50    , 50);
    }

}

4.location類(若是直接粘貼複製運行的話,注意修改package)

package 貪吃蛇;

import 貪吃蛇.Snake.OritentionEum;

public class Location {
    //座標類,不光記錄座標位置,必要的時候還記錄方向信息
    private int x;
    private int y;
    private OritentionEum oritention;
    public Location(int x,int y) {
            this.x = x;
            this.y= y;
    }
    public Location(int x,int y,OritentionEum oritention) {
        this(x, y);
        this.oritention = oritention;
    }
    public Location(Location la,OritentionEum oritention) {
        this(la.getX(), la.getY());
        this.oritention = oritention;
    }
    public int getX() {
        return x;
    }    
    public int getY() {
        return y;
    }
    public void recordOritention(OritentionEum oritention) {
        this.oritention = oritention;
    }
    public OritentionEum getRecordOritention() {
        return this.oritention;
    }
}

5.OerationList類

package 貪吃蛇;

import java.awt.Choice;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextField;
import java.time.LocalTime;

@SuppressWarnings("serial")
public class OerationList extends Panel{
    private Label l = new Label("時間:");
    private TextField time = new TextField(15);
    private Label l1 = new Label("得分:");
    private TextField score = new TextField("0",15);
    private Label l2 = new Label("速度:");
    private Choice speed = new Choice();
    //private LocalTime lt = LocalTime.now();                //獲取當前時間 ,java8最新時間包Time
    public OerationList listInit() {
        l.setFont(new Font("宋體",Font.PLAIN,15));
        l1.setFont(new Font("宋體",Font.PLAIN,15));
        l2.setFont(new Font("宋體",Font.PLAIN,15));
        time.setFont(new Font("宋體",Font.PLAIN,15));
        score.setFont(new Font("宋體",Font.PLAIN,15));
        speed.setFont(new Font("宋體",Font.PLAIN,15));
            //遊戲速度選擇框
        speed.add("一級");
        speed.add("二級");
        speed.add("三級");
            //時間框顯示
        this.updateTime();
            //得分要根據吃食物數量來定
        //所有加到ol中
        this.add(l);
        this.add(time);
        this.add(l1);
        this.add(score);
        this.add(l2);
        this.add(speed);
        //this.setBounds((int)(grWidth*0.75), 0, (int)(grWidth*0.25), grHeight);
        this.setLayout(new GridLayout(6,1,0,25));    //6行一列,水平間距50 ,垂直間距60
        this.setVisible(true);
        return this;
    }
    public String getScore() {
        return score.getText();
    }
    public String getSpeed() {
        return speed.getSelectedItem();
    }
    public void updateScore() {
        int i = Integer.parseInt(score.getText());
         i  +=5;
        score.setText(String.valueOf(i));
    }
    public void updateTime() {
        time.setText(LocalTime.now().toString().split("\\.")[0]); 
                    //split是用正則表達式的 其中.表示任意字符  因此   .要用兩個轉義字符才能表示.自己  
    }
}

6.snake類

package 貪吃蛇;

import java.awt.Color;
import java.awt.Graphics;
import java.util.LinkedList;

public class Snake {
    //private  int[][] snakeMap = new int[45][30];      45行30列
    //將蛇身的座標加入一個隊列,從蛇尾巴開始加
    public LinkedList<Location> snake = new LinkedList<>();
    //方向數組,用來肯定蛇尾巴的方向  ,每次Frame監聽到有效的方向按鍵,則記錄位置並記錄方向
    public LinkedList<Location> oritention = new LinkedList<>();
    private boolean hasCrawl = true;
    //初始化數組,
     {
        snake.add(new Location(2,3));        //First remove
        snake.add(new Location(2,4));
        snake.add(new Location(2,5));
        snake.add(new Location(2,6));
        snake.add(new Location(2,7));        //Last 蛇頭
    }
    private OritentionEum oe = OritentionEum.RIGHT;    //方向默認開始向右
    private Location snakeHead  = snake.getLast();
    private boolean isEat = false;                        //是否吃到
    //獲取蛇身,將蛇尾是否增加計入裏面
    public LinkedList<Location> getSnake(){
        this.growTail();
        return snake;
    }
    //獲取蛇尾位置
    public Location getSnakeTail() {
        return snake.getFirst();
    }
    //獲取蛇頭位置
    public    Location getSnakeHead() {
        return snake.getLast();
    }
    //設置蛇頭
    public void setSnakeHead(Location loc) {
        this.snakeHead = loc;
    }
        //蛇運動, Location的數組行索引其實對應的是畫布的Y座標
    public void snakeCrawl() {
        OritentionEum oe = this.getHeadOritention();
        Location snakeHead = this.getSnakeHead();
        switch(oe) {
        case DOWN : snake.addLast(new Location(snakeHead.getX()+1,snakeHead.getY()));break;
        case UP : snake.addLast(new Location(snakeHead.getX()-1,snakeHead.getY()));break;
        case RIGHT : snake.addLast(new Location(snakeHead.getX(),snakeHead.getY()+1));break;
        case LEFT : snake.addLast(new Location(snakeHead.getX(),snakeHead.getY()-1));break;
        }
        snake.remove();
        this.hasCrawl = true;            //蛇走完一部才設置爲true,這樣才容許方向的設置
        if(!oritention.isEmpty()) {
        Location tail  = this.getSnakeTail(); 
        Location la  = oritention.getFirst();        //蛇頭轉向的標記點
        if(tail.getX() == la.getX() && tail.getY()==la.getY()) {    //蛇尾走到拐點
            oritention.remove();
        }
      }
    }
    //畫蛇
    public void drawSnake(Graphics g,int rate) {
        g.setColor(Color.YELLOW);
        for(Location i: snake) {
            //g.fillRect(i.getX()*rate, i.getY()*rate, rate, rate);
            g.fillRect(i.getY()*rate+10,i.getX()*rate+50, rate, rate);
        }
        this.snakeCrawl();
    }
    //設置方向,方向爲右的時候只能設置方向爲上下,爲上下的時候只能設置方向爲左右
    //設置方向的時候同時將將方向加入方向隊列用來對蛇尾方向進行標記
    //存在一種狀況,方向已經設置可是因爲線程更新間隔大於按鍵監聽間隔,
    //因此可能存在當蛇在橫着走的時候可能先設置上或者下成功,但這時候繪畫線程卻沒有更新(),在這極短的時間內又從新設置了左或者右
    //這時候就會發現蛇可能在水平方向來回運動的狀況
    //設置一個標識變量 flag 設置方向的時候將其設置爲false,蛇crawl以後設置爲true
    public void setOritention(OritentionEum oe) {
        if(this.hasCrawl) {
        OritentionEum currentOe = this.getHeadOritention();                //獲取當前蛇的方向
        if(currentOe == OritentionEum.RIGHT || currentOe == OritentionEum.LEFT) {
          if(oe==OritentionEum.UP || oe==OritentionEum.DOWN)
              {oritention.add(new Location(this.getSnakeHead(),this.getHeadOritention()));
              this.oe = oe; this.hasCrawl=false;}
        } else{
            if(oe==OritentionEum.RIGHT || oe==OritentionEum.LEFT)
                {oritention.add(new Location(this.getSnakeHead(),this.getHeadOritention()));
                this.oe=oe;this.hasCrawl=false;}
           }
        }
    }
    //獲取當前蛇頭的方向
    public OritentionEum getHeadOritention() {
        return oe;
    }
    //獲取蛇尾方向,方向隊列不爲空,就按照隊列First方向,不然就按照蛇頭方向
    public OritentionEum getTailOritention() {
        if(!oritention.isEmpty()) {
            Location la  = oritention.getFirst();        //蛇頭轉向的標記點
            //Location tail = this.getSnakeTail();
            return la.getRecordOritention();
        }
        else return this.getHeadOritention();
    }
    //蛇吃食物,同時在蛇尾增長長度(須要根據方向來肯定增長在哪裏)
    public boolean eatFood(Food d) {
        Location fla=d.getFoodLocation();    //食物座標
        Location sla = this.getSnakeHead();    //蛇頭座標
        if(sla.getX()==fla.getX() && fla.getY() == sla.getY()) {
            isEat = true;
            d.setEat(true);         //更新食物裏面是否被吃的狀態
            return true;
        }
        return false;
    } 
    //在蛇尾增長一個單位的長度
    //若是方向隊列爲空,則按照當前蛇前進的方向來反向增長
    //不然就按照蛇尾方向隊列oritention的First元素來反向增長蛇尾長度
    public void growTail() {
        if(this.isEat) {
        OritentionEum oe = this.getTailOritention();
        //System.out.println(oe);
        Location la = this.getSnakeTail();
            switch(oe) {
            case UP: snake.addFirst(new Location(la.getX()+1,la.getY()));break;
            case DOWN: snake.addFirst(new Location(la.getX()-1,la.getY()));break;
            case LEFT: snake.addFirst(new Location(la.getX(),la.getY()+1));break;
            case RIGHT: snake.addFirst(new Location(la.getX(),la.getY()-1));break;
            }
            //增加完後復位
            this.isEat = false;
        }
    }
    // 碰撞遊戲結束
    public boolean crashed() {
        Location la = this.getSnakeHead();
        int x = la.getX(),y=la.getY();
            //只要蛇頭沒撞上就OK
            //分爲撞牆和撞蛇身
        if(x>=0 && x<=69 && y>=0 && y<=49) {        //在畫布範圍裏
            boolean flag = false;
            for(int i=0;i<snake.size()-1;i++) {
                Location sla = snake.get(i);
                if(sla.getX()==x && sla.getY()==y) //蛇與自身碰撞了
                    flag = true;    
            }
            return flag;
        } else return true;
    }
    //蛇運行方向枚舉類
    enum OritentionEum{
        //4個枚舉方向的枚舉實例
        UP,DOWN,LEFT,RIGHT;
    }
    public static void main(String [] args) {
        Snake sk = new Snake();
        for(Location i : sk.snake) {
            System.out.println(i.getX()+" "+i.getY());
        }
        System.out.println(sk.snake.getLast().getY());
    }
}

7.food類

package 貪吃蛇;

import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;

//定義食物的類,顯示和隨機生成
public class Food {
    private boolean iseat = false;    //默認沒有被吃
    Location foodla = new Location(20,20);        //食物的初始位置  
    public void drawFood(Graphics g,int rate) {
        g.setColor(Color.RED);
        g.fillRect(foodla.getY()*rate+10, foodla.getX()*rate+50, rate, rate);
    }
    //獲取食物的座標
    public Location getFoodLocation() {
        return foodla;
    }
    //設定食物座標
    public void setLocation(Location x) {
        this.foodla = x;
    }
    //食物是否被吃
    public boolean isEat() {
        return iseat;
    }
    //設定食物是否被吃狀態
    public void setEat(boolean b) {
        this.iseat = b;
    }
    //若是食物被吃了,就更新食物座標,食物座標在(90,60)的範圍且不與蛇身重合
    public void updateFood() {
        //這裏只是隨機產生的座標,有可能食物在蛇身子上,由於不想用遍歷的方式來獲取哪些座標是空白的,有時間記得回來改
        Random am = new Random();
        foodla = new Location(am.nextInt(70),am.nextInt(50));
    }    
}

 另:遊戲結束會面以下:

    工做一年,從x=x+1都理解不了的萌新,到能本身刷leetcode(只是刷了簡單級別的67題和幾道medium的題),真羨慕那些初中就接觸代碼的人。本身也在一直補習組成原理、操做系統和網絡的相關知識。數據結構也在看,看的是C語言版的,而後本身用java實現棧(鏈式和數組式),隊列,avl樹,B樹等。但願本身堅持下來,解算法題也是會上癮的,哈哈。

相關文章
相關標籤/搜索