【Java】Java实现贪吃蛇小游戏(带详细注释)

源码

源码来源:Java swing 写的贪吃蛇代码200行
前段时间在博客上看到了这段源码,由于自己没有用Java做过小游戏,于是就copy了一下,然后在自己电脑上运行,顺便加上了注释。

代码

greedySnackMain.java

import javax.swing.JFrame;

public class greedySnackMain extends JFrame {
    snackWin snackwin;
    static final int Width = 800 , Height = 600 , LocX = 200 , LocY = 80;

    public greedySnackMain() {
        super("GreedySncak_SL");
        snackwin = new snackWin();
        //在JFrame窗口容器里添加其他组件
        add(snackwin);
        //设置组件的大小
        this.setSize(Width, Height);
        //设置组件的可见性
        this.setVisible(true);
        //设置组件的位置
        this.setLocation(LocX, LocY);
        //snackwin.requestFocus();
    }

    public static void main(String[] args) {
        new greedySnackMain();
    }
}

snackWin.java

import java.awt.Color;

import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JButton;
import javax.swing.JPanel;

public class snackWin extends JPanel implements ActionListener, KeyListener {

//     用数字代表方向
    static final int Up = 0 , Down = 1 , Left = 2 , Right = 3;
//    GameWidth/GameHeight :游戏区域的宽、高
//    GameLocX / GameLocY:游戏区域的左上角位置的坐标
    static final int GameLocX = 50, GameLocY = 50 , GameWidth = 700 , GameHeight = 500 , Size = 10;//Size:每次移动的位置大小以及增加的长度
 //    rx,ry:食物的坐标
    static int rx , ry , score = 0 , speed = 5; 
    boolean startFlag = false;


    JButton startButton , stopButton , quitButton;
    Snack snack;

    public snackWin() {
        snack = new Snack();
//        随机设置食物的位置
        rx = (int)(Math.random() * (GameWidth - 10) + GameLocX);
        ry = (int)(Math.random() * (GameHeight - 10) + GameLocY);

        startButton = new JButton("开始");
        stopButton = new JButton("暂停");
        quitButton = new JButton("退出");

        setLayout(new FlowLayout(FlowLayout.LEFT));
        // 添加三个按键:开始、暂停、退出
        this.add(startButton);
        this.add(stopButton);
        this.add(quitButton);
     // 添加三个按键的监听事件
        startButton.addActionListener(this);
        stopButton.addActionListener(this);
        quitButton.addActionListener(this);
        this.addKeyListener(this);
        new Thread(new snackThread()).start();  
    }

    public void paint(Graphics g)
    {
        super.paint(g);   //没有会将button刷掉
//        设置画笔颜色
        g.setColor(Color.white);
//       用白色 填充一个矩形区域
        g.fillRect(GameLocX, GameLocY, GameWidth, GameHeight);  
        g.setColor(Color.black);
//        drawRect用于画矩形;X、Y为左上角的位置,width和height为矩形的宽和高
//         用黑色化一个矩形
        g.drawRect(GameLocX, GameLocY, GameWidth, GameHeight); 
//        绘制字符串
        g.drawString("Score: " + score + "        Speed: " + speed + "      速度最大为100" , 250, 25);
//        用绿色填充一个长宽为10的矩形 表示 食物
        g.setColor(Color.green);
        g.fillRect(rx, ry, 10, 10); 
//        画蛇
        snack.draw(g);
    }

    public void actionPerformed(ActionEvent e) {
//      当开始按钮被点击时,开始按钮将不能再被点击
        if(e.getSource() == startButton) {
            startFlag = true;
            startButton.setEnabled(false);
            stopButton.setEnabled(true);
        }
        if(e.getSource() == stopButton) {
            startFlag = false;
            startButton.setEnabled(true);
            stopButton.setEnabled(false);
        }
//        退出程序
        if(e.getSource() == quitButton) {
            System.exit(0);
        }
        this.requestFocus();
    }

    /**
     * 当键盘被按下时调用
     */
    public void keyPressed(KeyEvent e) {
//     当还没按下开始按钮时,按键不可用
        if(!startFlag) 
            return ;

        switch(e.getKeyCode()) {
//         如果蛇的长度为1,则表示刚开始,什么方向都可以;
//         若蛇的长度不为1,则表示它在运动,此时不能操作它向反方向运动
           case KeyEvent.VK_UP:
               if(snack.length() != 1 && snack.getDir() == Down) break;
               snack.changeDir(Up);
               break;
          case KeyEvent.VK_DOWN:
              if(snack.length() != 1 && snack.getDir() == Up) break;
              snack.changeDir(Down);
              break;
          case KeyEvent.VK_LEFT:
              if(snack.length() != 1 && snack.getDir() == Right) break;
              snack.changeDir(Left);
              break;
          case KeyEvent.VK_RIGHT:
              if(snack.length() != 1 && snack.getDir() == Left) break;
              snack.changeDir(Right);
             break;
         }
    }

    public void keyReleased(KeyEvent e) {}
    public void keyTyped(KeyEvent e) {}

    class snackThread implements Runnable
    {
        public void run() {
            while(true) {
                try {
//                  通过线程休眠时间控制蛇的速度
                    Thread.sleep(100 - speed >= 0 ? 100 - speed : 0);
                    repaint(); // 调用paint(),重绘组件(刷新界面)
                    if(startFlag) {
                        snack.move();
                    }
                }
                catch(InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Snack.java

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JOptionPane;

/**
 *  存放每个点的X、Y坐标
 */
class Node {
    private int x , y;
    public Node() {}
    public Node(int a , int b){
         x = a;
         y = b;
     }
    public Node(Node tmp) {
        x = tmp.getX(); 
        y = tmp.getY();
    }
    int getX() {
        return x;
    }
    int getY() {
        return y;
    }
    void setX(int a) {
        x = a;
    }
    void setY(int b) {
        y = b;
    }
}


public class Snack {

    static final int DIR [][] = {{0 , -1} , {0 , 1} , {-1 , 0} , {1 , 0}};
    private List<Node> lt = new ArrayList<Node>();
    private int curDir; //当前运动方向

    /**
     *  初始化
     *  蛇的初始方向是向右,Right=3
     */
    public Snack() {
        curDir = 3;
        lt.add(new Node(350 , 250));
    }

    /**
     *  List的长度表示蛇的长度
     * @return
     */
    int length() {
        return lt.size();
    }

    int getDir() {
        return curDir;
    }

/**  
 * Graphics,绘图类
 *  画蛇
 *  找到每个下标指向的X、Y值,即组成蛇的每一点所在位置
 */
    void draw(Graphics g) 
    {
        g.setColor(Color.black);
        for(int i = 0; i < lt.size(); i++) {
            g.fillRect(lt.get(i).getX(), lt.get(i).getY(), 10, 10);
        }
    }

  /**
   *  判断蛇是否咬到自己
   *  如果蛇的头部(0)与其他位置重合,则表示蛇咬到自己了
   * @return
   */
    boolean Dead() {
        for(int i = 1; i < lt.size(); i++) {
            if(lt.get(0).getX() == lt.get(i).getX() && lt.get(0).getY() == lt.get(i).getY()) 
                return true;
        }
        return false;
    }

    /**
     * 头部移动的位置
     * @return tmp:头部的位置坐标
     */
    Node headMove()
    {
//      当向上或向下时,curDir为0/1,此时X轴不变(即 snackWin.Size * DIR[curDir][0]的值为0),Y轴变化
//      当向左或向右时,curDir为2/3,此时Y轴不变(即 snackWin.Size * DIR[curDir][1]的值为0),X轴变化
        int tx = lt.get(0).getX() + snackWin.Size * DIR[curDir][0];
        int ty = lt.get(0).getY() + snackWin.Size * DIR[curDir][1];

//     用一个矩形代表蛇的一个节点,矩形的长宽为10,故设置Size也为10
//     当头部的位置大于游戏区域的宽度证明它到达了最右边,之后它需要从左边出现,即X坐标变为最左边的X坐标位置
        if(tx > snackWin.GameLocX + snackWin.GameWidth - snackWin.Size) 
            tx = snackWin.GameLocX;
//     当头部的位置小于左边的X坐标,证明它向左走并到达最左边,之后它需要从右边出现,即X坐标变为最右边的X坐标位置
        if(tx < snackWin.GameLocX) 
            tx = snackWin.GameWidth + snackWin.GameLocX - snackWin.Size;
//     与上同
        if(ty > snackWin.GameLocY + snackWin.GameHeight - snackWin.Size) 
            ty = snackWin.GameLocY;
        if(ty < snackWin.GameLocY) 
            ty = snackWin.GameHeight + snackWin.GameLocY - snackWin.Size;
        Node tmp = new Node(tx, ty);
        return tmp;
    }

    void move()
    {
//      头部的位置
        Node head = headMove();
//    新添加的节点的位置
        Node newNode = new Node();
        boolean eat = false;

//     abs()返回参数的绝对值
//     通过判断头部的X/Y坐标位置和食物的X/Y坐标位置,由于头部大小是10,故距离小于10就代表吃到了
        if(Math.abs(head.getX() - snackWin.rx) < 10 && Math.abs(head.getY() - snackWin.ry) < 10) {
            eat = true;
            newNode = new Node(lt.get(lt.size() - 1));
            snackWin.rx = (int)(Math.random() * (snackWin.GameWidth - 10) + snackWin.GameLocX);
            snackWin.ry = (int)(Math.random() * (snackWin.GameHeight - 10) + snackWin.GameLocY);
        }

//      把前一个点的位置给后一点(即移动)
        for(int i = lt.size() - 1; i > 0; i--) 
            lt.set(i, lt.get(i - 1));
        lt.set(0, head);

        if(Dead()) {
//          弹出对话框,告知游戏结束
            JOptionPane.showMessageDialog(null, "Snake eating itself", "Message", JOptionPane.ERROR_MESSAGE);
            System.exit(1);
        }
//        如果吃到了食物,则eat为true。此时添加节点、分数加一,速度加一
        if(eat) {
            lt.add(newNode);
            snackWin.score++;
            snackWin.speed++;
        }
    }

    /**
     *  改变蛇的方向
     * @param dir
     */
    void changeDir(int dir) {
        curDir = dir;
     }

}

参考资料:
Java swing 写的贪吃蛇代码200行

猜你喜欢

转载自blog.csdn.net/go_d_og/article/details/79621970