1. 程式人生 > 實用技巧 >用java實現貪吃蛇小遊戲

用java實現貪吃蛇小遊戲

在本類中目的是做一個背景和畫板,實現貪吃蛇遊戲的一個動態效果

package Snake;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Background {
    //容器物件
    private JFrame sanke_jf = new JFrame("貪吃蛇小遊戲");
    private JPanel jPanel = new JPanel();
    private SnakeClass snake;//
    private GetFood.Food food;//
食物 public Background( SnakeClass snake, GetFood.Food food) { //構造器 this.food = food; this.snake = snake; } //背景的大小 public static final int BACK_WIDTH = 1210; public static final int BACK_HEIGHT = 858; //遊戲相關資訊的畫布和畫筆 private class DrawArea extends Canvas { @Override
public void paint(Graphics g) { if (Is.isOver) { g.setColor(Color.black); g.setFont(new Font("Times", Font.BOLD, 45)); g.drawString("遊戲結束!", 600, 350); g.drawString("你的分數:"+food.count, 600, 400); g.drawString("歷史最高分:"+SaveMaxCount.read(), 600, 450); }
else { if (food.color==5||food.color==10||food.color==7){ g.setColor(Color.orange); g.fillOval(food.foodx, food.foody, Node.NODE_DIA, Node.NODE_DIA); }else if(food.color==4||food.color==8){ g.setColor(Color.black); g.fillOval(food.foodx, food.foody, Node.NODE_DIA, Node.NODE_DIA); }else { g.setColor(Color.GREEN); g.fillOval(food.foodx, food.foody, Node.NODE_DIA, Node.NODE_DIA); } //蛇頭(黃色) g.setColor(Color.orange); g.fillOval(snake.head.nodex, snake.head.nodey, Node.NODE_DIA, Node.NODE_DIA); //蛇的身體(藍色) g.setColor(Color.BLUE); Node temp = snake.head; while (true) { if (temp.next == null) { g.fillOval(temp.nodex, temp.nodey, Node.NODE_DIA, Node.NODE_DIA); break; } temp = temp.next; g.fillOval(temp.nodex, temp.nodey, Node.NODE_DIA, Node.NODE_DIA); } } } } private Timer timer;//多久自動執行一次相關程式碼的物件 //建立繪畫區域物件 DrawArea drawArea = new DrawArea(); //組裝顯示介面的方法 public void draw_back() { drawArea.setPreferredSize(new Dimension(BACK_WIDTH, BACK_HEIGHT));//為繪畫區域設定大小 //鍵盤監聽器,用鍵盤控制蛇的移動 KeyListener listener = new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); snake.move(keyCode); } }; //為相關區域新增監聽器 jPanel.addKeyListener(listener); sanke_jf.addKeyListener(listener); drawArea.addKeyListener(listener); //設定JFrame容器的大小 sanke_jf.setSize(BACK_WIDTH, BACK_HEIGHT); //每過指定時間自動執行的任務程式碼 ActionListener tack = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { drawArea.repaint();//重畫,實現視覺效果 snake.autmove(); if (Is.isOver) timer.stop(); } }; timer = new Timer(230, tack); timer.start(); //將繪畫區域新增到容器中 jPanel.add(drawArea); sanke_jf.add(jPanel); sanke_jf.setResizable(false);//設定JFrame的大小不可變 sanke_jf.setVisible(true);//設定視窗可見 //點選右上角“x”號結束程式 sanke_jf.addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { System.exit(0); } }); }
//該類是食物相關類,實現同一時間有且僅有一個食物存在,主要利用多執行緒的通訊機制

package
Snake; import java.util.Random; public class GetFood { //食物消失與生成的類 //每一個食物也是一個節點 Random rx = new Random();//隨機生成食物的x座標 Random ry = new Random();//隨機生成食物的y座標 Random rc = new Random(); //將背景按節點大小劃分為一定的格子,食物只能產生在格子中以保證蛇一定可以吃到 int rowy = Background.BACK_WIDTH/Node.NODE_DIA; int rowx = Background.BACK_HEIGHT/Node.NODE_DIA; //食物類 class Food{ //座標 public int foodx =-1; public int foody =-1; public int color = 1; //是否需要產生食物 public boolean isGetFood = true; //計算分數 int count=0; } //用多執行緒實現食物的消亡與產生,利用等待喚醒機制保證食物的同步 class CreateFood extends Thread { //提供鎖物件 private Food food; public CreateFood(Food food){ this.food=food; } @Override public void run() { while(true){ synchronized (food){ if(!food.isGetFood){ try { food.wait();//如果有食物進入等待 } catch (InterruptedException e) { e.printStackTrace(); } }else{ //如果沒有食物隨機生成一個食物(座標) if(Is.is_Strength){ food.color=(int)(Math.random()*10); } food.foodx=Node.NODE_DIA*(rx.nextInt(rowy-2)+1); food.foody=Node.NODE_DIA*(ry.nextInt(rowx-3));//設定一定範圍,增強遊戲體驗 food.isGetFood=false;//產生食物以後修改食物的狀態 food.notify();//喚醒吃食物的執行緒 } } } } } class EatFood extends Thread{ private Food food;//提供鎖物件 private SnakeClass snake;//蛇(與食物發生碰撞) public EatFood(Food food,SnakeClass snake){ //構造器 this.food=food; this.snake=snake; } @Override public void run() { while(true){ synchronized (food){ if(food.isGetFood){ try { food.wait();//如果沒有食物,進入等待 } catch (InterruptedException e) { e.printStackTrace(); } }else{ //如果有食物進 if (snake.head.nodex==food.foodx&&snake.head.nodey==food.foody){ //如果蛇頭食物發生碰撞 if(food.color==5||food.color==10||food.color==7){ snake.add_Tail(new Node(snake.get_Tail().nodex,snake.get_Tail().nodey)); snake.add_Tail(new Node(snake.get_Tail().nodex,snake.get_Tail().nodey)); food.foodx=-1; food.foody=-1; food.count+=2; }else if(food.color==4||food.color==8){ snake.deleteTail(); food.foodx=-1; food.foody=-1; food.count--; try { Thread.sleep(3500); } catch (InterruptedException e) { e.printStackTrace(); }if(food.count>=SaveMaxCount.read()){ SaveMaxCount.save(food.count); } food.isGetFood=true;//修改食物狀態 food.notify();//喚醒產生食物的執行緒 } else{ snake.add_Tail(new Node(snake.get_Tail().nodex,snake.get_Tail().nodey)); food.foodx=-1; food.foody=-1; food.count++; } // System.out.println(food.color);(在測試時檢視隨機數) //如果當前分數大於歷史最大分數,則將當前分數存入檔案 if(!food.isGetFood){ food.isGetFood=true;//修改食物狀態 food.notify();//喚醒產生食物的執行緒 } } } } } } } }
//遊戲入口
public
class Inner { public static void main(String[] args) { new StartFrame().start_window(); } }
//遊戲模式和遊戲是否結束的開關
package
Snake; public class Is { public static boolean isOver = false;//表示遊戲是否結束 public static boolean is_nodie=false; public static boolean is_Strength=false; }
package Snake;

//蛇的沒一個節點的基本單位,因為在本遊戲中不斷地進行刪除與增加,所以選擇使用連結串列
public class Node {

    //表示蛇的每一個節點(連結串列結構)

    public  Node next;//指向下一個
    public  int nodex;//節點的x座標
    public  int  nodey;//節點的y座標
    public  static final int NODE_DIA = 22;//節點的大小

    public Node(int nodex,int nodey){
        //構造器
        this.nodex=nodex;
        this.nodey=nodey;
    }
}
package Snake;
//遊戲的初始化資訊和模式設定
public class RunGameMethod {

    public static void method1(){//標準模式
        Is.is_nodie=false;
        Is.is_Strength=false;
        original();
    }
    public static void method2(){//無盡模式
        Is.is_Strength=false;
        Is.is_nodie=true;
        original();
    }
    public static void method3(){//無盡模式
        Is.is_nodie=false;
        Is.is_Strength=true;
        original();
    }

    private static  void original(){
        SaveMaxCount filecount  = new SaveMaxCount();//建立儲存最高分數的物件

        //初始化一條小蛇
        Node head = new Node(2 * Node.NODE_DIA, 0);
        Node body1 = new Node(Node.NODE_DIA, 0);
        Node body2 = new Node(0, 0);
        SnakeClass snake = new SnakeClass(head);
        snake.add_Tail(body1);
        snake.add_Tail(body2);

        //建立食物的物件
        GetFood.Food food = new GetFood().new Food();
        //顯示遊戲執行介面
        Background back = new Background(snake, food);
        back.draw_back();
        //開啟產生食物和吃食物的兩個執行緒,並將食物物件作為鎖物件
        new GetFood().new EatFood(food, snake).start();
        new GetFood().new CreateFood(food).start();
    }
}
package Snake;
//利用檔案儲存遊戲的最高分
import java.io.*; public class SaveMaxCount { //儲存最高分數的檔案位置 private static File file_normal = new File("C:\\Users\\lenovo\\IdeaProjects\\MyJava\\src\\Snake\\savecount.txt"); private static File file_Strength= new File("C:\\Users\\lenovo\\IdeaProjects\\MyJava\\src\\Snake\\save_strength.txt"); private static File file = file_normal; private static File getFile() { if(Is.is_Strength){ return file_Strength; } return file; } //儲存分數的方法 public static void save(int count){ FileOutputStream fos = null; try{ fos = new FileOutputStream(SaveMaxCount.getFile()); fos.write(count); }catch(IOException e){ e.getMessage(); }finally { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } //得到歷史最高分數的方法 public static int read(){ FileInputStream fis = null; int count = 0; try { fis=new FileInputStream(SaveMaxCount.getFile()); count=fis.read(); } catch (IOException e) { e.printStackTrace(); }finally{ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } return count; } }
package Snake;
//實現蛇的構造、移動、遊戲結束邏輯的控制。
public class SnakeClass {

    public SnakeClass(){

    }
    //蛇的構造器,重點是頭節點  IS是為了在整個程式中保證判斷結束語句的物件的一致性
    public SnakeClass( Node head) {
        this.head = head;
    }
    public Node head;//頭結點
    public Node tail;//尾節點
    //增加蛇的長度(向尾部新增一個節點)
    public void add_Tail(Node node) {
        Node temp = head;
        while (true) {
            if (temp.next == null) {
                //此時temp為當前的尾節點
                break;
            }
            temp = temp.next;
        }
        temp.next = node;
    }

    //在頭結點的後面和原頭結點的下一個節點中間插入一個節點(移動的第一步)
    public void add_head(Node node) {
        Node temp = head.next;
        head.next = node;
        node.next = temp;
    }

    //得到當前的尾節點(在得分和移動時都需要用到)
    public Node get_Tail() {
        Node temp = head;
        while (true) {
            if (temp.next == null) {
                tail = temp;
                return tail;
            }
            temp = temp.next;
        }
    }

    //刪除尾節點(移動的第二步)
    public void deleteTail() {
        Node temp = head;
        Node temp2 = temp;
        while (true) {
            if (temp.next == null) {
                temp2.next = null;
                return;
            }
            temp2 = temp;
            temp = temp.next;
        }
    }

    //蛇頭撞到了自身,遊戲結束邏輯的一方面
    private boolean isOver_Self(){
        boolean b = false;
        Node temp = head.next;
        while(true){
            if(head.nodey==temp.nodey&&head.nodex==temp.nodex){
                b=true;
                break;
            }else if(temp.next==null){
                break;
            }
            temp=temp.next;
        }
        return b;
    }

    //遊戲結束的邏輯
    public   boolean isOver(){
        return  head.nodex < Node.NODE_DIA || head.nodex > (Background.BACK_WIDTH - 2 * Node.NODE_DIA) ||
                head.nodey < -0 || head.nodey > (Background.BACK_HEIGHT - 4 * Node.NODE_DIA)||isOver_Self();
    }

    //移動的方法(改變方向)(將頭結點按指定方法移動一個單位,在其後新增加一個單位,刪除尾節點
    //判斷蛇是否可以向右移動,正在向左方向移動
    //條件成立不能該方向移動
    private boolean can_moveRight(){
        return head.nodey==head.next.nodey&&head.nodex<head.next.nodex;
    }
    //判斷蛇是否可以向左移動,正在向右方向移動
    //條件成立不能該方向移動
    private boolean can_moveLeft(){
        return head.nodey==head.next.nodey&&head.nodex>head.next.nodex;
    }
    //判斷蛇是否可以向上移動,正在向下方向移動
    //條件成立不能該方向移動
    private boolean can_moveUp(){
        return head.nodex==head.next.nodex&&head.nodey<head.next.nodey;
    }
    //判斷蛇是否可以向下移動,正在向上方向移動
    //條件成立不能該方向移動
    private boolean can_moveDown(){
        return head.nodex==head.next.nodex&&head.nodey>head.next.nodey;
    }

    public void move(int ch) {
        boolean b=isOver();
        //撞到邊界的判斷邏輯,滿足條件遊戲結束
        int tempx = head.nodex;
        int tempy = head.nodey;
        //新增節點的x和y座標
        switch (ch) {
            case 37:
                if(can_moveLeft()) break;
                if(!Is.is_nodie){
                    if (b) Is.isOver = true;
                }
                head.nodex -= Node.NODE_DIA;
                add_head(new Node(tempx, tempy));
                deleteTail();
                break;
            case 40:
                if(can_moveUp()) break;
                if(!Is.is_nodie){
                    if (b) Is.isOver = true;
                }
                head.nodey += Node.NODE_DIA;
                add_head(new Node(tempx, tempy));
                deleteTail();
                break;
            case 39:
                if(can_moveRight()) break;
                if(!Is.is_nodie){
                    if (b) Is.isOver = true;
                }
                head.nodex += Node.NODE_DIA;
                add_head(new Node(tempx, tempy));
                deleteTail();
                break;
            case 38:
                if(can_moveDown()) break;
                if(!Is.is_nodie){
                    if (b) Is.isOver = true;
                }
                head.nodey -= Node.NODE_DIA;
                add_head(new Node(tempx, tempy));
                deleteTail();
                break;
            default:
                break;
        }
    }

    //實現蛇的自動移動(與第一種移動方式有相似的邏輯,但不會改變方向)
    public void autmove(){
        int tempx = head.nodex;
        int tempy = head.nodey;
        boolean b=isOver();
        if(can_moveRight()){
            if(!Is.is_nodie){
                if (b) Is.isOver = true;
            }
            head.nodex -= Node.NODE_DIA;
            add_head(new Node(tempx, tempy));
            deleteTail();
        }else if(can_moveLeft()){
            if(!Is.is_nodie){
                if (b) Is.isOver = true;
            }
            head.nodex += Node.NODE_DIA;
            add_head(new Node(tempx, tempy));
            deleteTail();
        }else if (can_moveUp()){
            if(!Is.is_nodie){
                if (b) Is.isOver = true;
            }
            head.nodey -= Node.NODE_DIA;
            add_head(new Node(tempx, tempy));
            deleteTail();
        }else{
            if(!Is.is_nodie){
                if (b) Is.isOver = true;
            }
            head.nodey += Node.NODE_DIA;
            add_head(new Node(tempx, tempy));
            deleteTail();
        }
    }
}
package Snake;
//遊戲開始介面
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class StartFrame implements ActionListener{

    private  JFrame start_trame = new JFrame("遊戲模式選擇");
    private  JPanel start_panle = new JPanel();
    private   JButton but1 = new JButton("標準模式");
    private  JButton but2 = new JButton("無盡模式");
    private   JButton but3 = new JButton("強化模式");
    private   JButton but4 = new JButton("遊戲資訊");
    private   String str;
    public  void start_window(){
        but1.addActionListener(this);
        but2.addActionListener(this);
        but3.addActionListener(this);
        but4.addActionListener(this);
        start_panle.add(but1);
        start_panle.add(but2);
        start_panle.add(but3);
        start_panle.add(but4);
        start_panle.setLayout(new GridLayout(4,1));
        start_trame.add(start_panle);
        start_trame.setSize(400,500);
        start_trame.setVisible(true);
        start_trame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        Is.isOver=false;
        if(e.getSource()==but1){
            RunGameMethod.method1();
        }else if(e.getSource()==but2){
            RunGameMethod.method2();
        }else if(e.getSource()==but3){
            RunGameMethod.method3();
        }else{
            str="你可以通過“↑”“↓”“←”“→”控制一條小蛇去吃食物,注意不要撞到邊界和自己的身體!\n" +
                    "在無盡模式中你可以任意移動而不擔心死亡,在強化模式中有三種不同的食物各有不同的效果!\n" +
                    "快點開始你的遊戲之旅吧!\n" +
                    "\n\n" +
            JOptionPane.showMessageDialog(null,str);
        }
    }
}