首先定义一个JFrame窗口,之后会在窗口上加面板用来显示游戏。
StartGame.java
import javax.swing.*;
//游戏主启动
public class StartGame {
    public static void main(String[] args) {
        JFrame jFrame = new JFrame();
        jFrame.setBounds(300,100,900,800);//宽度和高度要按照一定比例
        jFrame.setResizable(false);//设置窗口大小不可变
        jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        jFrame.setVisible(true);
    }
}
然后创建一个GamePanel.java用来些游戏的面板,使用画笔来完善具体的游戏元素。
public class GamePanel extends JPanel{
    @Override
    protected void paintComponent(Graphics g){
        super.paintComponent(g);
    }
}
面板中首先要画出一个游戏的范围,这里设定蛇一块身体是2525大小,因此游戏区域的长和宽就需要与蛇的身体成比例。
这里设定了一个850650的游戏区域
g.fillRect(25,25,850,650);
设置一个键盘监听实现按空格键开始游戏,这里将键盘监听设置为GamePanel的内部类。
先定义一个游戏状态变量
boolean isStart = false;//游戏状态默认未开始
将开始提示用画笔画到界面上
    if(!isStart){
        g.setColor(Color.white);
        g.setFont(new Font("微软雅黑",Font.BOLD,40));
        g.drawString("按空格开始游戏",300,300);
    }
键盘监听类
class GameListener extends KeyAdapter{
        @Override
        public void keyPressed(KeyEvent e) {
            int keyCode = e.getKeyCode();//获得键盘按键是哪一个
            //空格开始游戏
            if (keyCode == KeyEvent.VK_SPACE) {
                isStart = !isStart;//取反
                repaint();
            }
        }
    }
接下来要用画笔将蛇画出来,这里设定蛇的每一个身体都是由25*25大小的正方形组成,为了区分头与身体,头部用红色,身体用绿色,设定头部坐标(100,50),身体部分在X轴依次减25(初始状态向右)。
//定义蛇的数据结构
int length;//蛇的长度
final int size = 24;//蛇的尺寸,这里设置为24*24可以更好区分每一块身体
//设定蛇每一个身体的坐标
int[] snakeX = new int[600];
int[] snakeY = new int[600];
String direction;//初始方向
//设定一个初始化方法用于初始化数据
public void init(){
        length = 3;//初始长度
        snakeX[0] = 100;//头部坐标
        snakeY[0] = 50;
        snakeX[1] = 75;//第一个身体坐标
        snakeY[1] = 50;
        snakeX[2] = 50;//第二个身体坐标
        snakeY[2] = 50;
        direction = "R";//初始方向向右
    }
实现按方向键使蛇移动
利用键盘监听获得按的是哪一个方向
        if (keyCode == KeyEvent.VK_UP) {
            direction = "U";
        } else if (keyCode == KeyEvent.VK_DOWN) {
            direction = "D";
        } else if (keyCode == KeyEvent.VK_RIGHT) {
            direction = "R";
        } else if (keyCode == KeyEvent.VK_LEFT) {
            direction = "L";
        }
设置定时器定义刷新时间
Timer timer = new Timer(50,new timeListener());//50毫秒执行一次
定义一个内部类timeListener用于刷新画面实现蛇的移动
class timeListener implements ActionListener{
        @Override
        public void actionPerformed(ActionEvent e) {
            if(isStart){
                for (int i = length-1;i>0;i--){
                    //将后一节移动到前一节的位置
                    snakeX[i] = snakeX[i-1];
                    snakeY[i] = snakeY[i-1];
                }
                switch (direction) {
                    case "R":
                        snakeX[0] = snakeX[0] + 25;
                        break;
                    case "L":
                        snakeX[0] = snakeX[0] - 25;
                        break;
                    case "U":
                        snakeY[0] = snakeY[0] - 25;
                        break;
                    case "D":
                        snakeY[0] = snakeY[0] + 25;
                        break;
                }
                repaint();//重画页面
            }
            timer.start();//开启定时器
        }
    }
再用类似的方法在随机坐标画出一个食物。先定义食物的相关变量
int foodX;
int foodY;
Random random = new Random();
在init中添加初始化食物坐标代码
foodX = 25+25*random.nextInt(33);
foodY = 25+25*random.nextInt(23);
使用画笔将食物画出来
g.setColor(Color.yellow);
g.fillOval(foodX,foodY,25,25);
实现蛇吃食物事件,当蛇头部坐标与食物坐标重合时重新生成食物并让蛇的长度+1。
actionPerformed方法中
            if (snakeX[0]==foodX && snakeY[0]==foodY){
                length++;//长度增加
                //重新生成食物
                foodX = 25+25*random.nextInt(33);
                foodY = 25+25*random.nextInt(23);
            }
游戏失败的情况:
蛇碰到自己的身体
蛇碰到边界
首先定义一个失败状态变量
boolean isFail = false;//游戏失败状态
将以上两种情况代码添加到timeListener类中
        if(snakeX[0] > 850 || snakeX[0] < 25 || snakeY[0] > 650 || snakeY[0] < 25){ //边界判定
            isFail = true;
        }
        for (int i = 1;i < length;i++) {//重合判定
            if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
                isFail = true;
                break;
            }
        }
画出一个失败的画面
    if(isFail){
        g.setColor(Color.red);
        g.setFont(new Font("微软雅黑",Font.BOLD,40));
        g.drawString("失败,请重新开始游戏",300,300);
    }
修改开始游戏部分代码
        if (keyCode == KeyEvent.VK_SPACE) {
            if(isFail){
                isFail = false;
                init();
            }else {
                isStart = !isStart;//取反
            }
            repaint();
        }
timeListener类中修改
if(isStart && !isFail)
最后在StartGame类中添加面板
jFrame.add(new GamePanel());
完成。

完整代码如下
StartGame.java
import javax.swing.*;
//游戏主启动
public class StartGame {
    public static void main(String[] args) {
        JFrame jFrame = new JFrame();
        jFrame.add(new GamePanel());
        jFrame.setBounds(300,100,900,800);//宽度和高度要按照一定比例
        jFrame.setResizable(false);//设置窗口大小不可变
        jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        jFrame.setVisible(true);
    }
}
GamePanel.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
//游戏面板
public class GamePanel extends JPanel {
    //定义蛇的数据结构
    int length;//蛇的长度
    final int size = 24;//蛇的尺寸
    int[] snakeX = new int[600];
    int[] snakeY = new int[600];
    String direction;//初始方向
    //食物坐标
    int foodX;
    int foodY;
    Random random = new Random();
    boolean isStart = false;//游戏状态默认未开始
    boolean isFail = false;//游戏失败状态
    //定时器,100毫秒刷新一次
    Timer timer = new Timer(50,new timeListener());
    //构造器
    public GamePanel(){
        init();
        //获得焦点和键盘事件
        setFocusable(true);
        addKeyListener(new GameListener());
    }
    //初始化方法
    public void init(){
        length = 3;//初始长度
        snakeX[0] = 100;//头部坐标
        snakeY[0] = 50;
        snakeX[1] = 75;//第一个身体坐标
        snakeY[1] = 50;
        snakeX[2] = 50;//第二个身体坐标
        snakeY[2] = 50;
        //随机生成事物
        foodX = 25+25*random.nextInt(33);
        foodY = 25+25*random.nextInt(23);
        direction = "R";//初始方向向右
        timer.start();//游戏一开始启动定时器
    }
    //绘制面板,游戏中所有东西都使用此画笔
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        //绘制静态面板
//        setBackground(Color.BLACK);
        g.fillRect(25,25,850,650);
        g.setColor(Color.red);
        g.drawRect(25,25,850,650);
        //画食物
        g.setColor(Color.yellow);
        g.fillOval(foodX,foodY,25,25);
        //画蛇
        g.setColor(Color.red);
        g.fillRect(snakeX[0],snakeY[0],size,size);
        g.setColor(Color.green);
        for (int i = 1;i < length; i++) {
            g.fillRect(snakeX[i],snakeY[i],size,size);
        }
        //画积分
        g.setColor(Color.white);
        g.setFont(new Font("微软雅黑",Font.BOLD,15));
        g.drawString("长度:"+length,750,50);
        //游戏状态
        if(!isStart){
            g.setColor(Color.white);
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("按空格开始游戏",300,300);
        }
        if(isFail){
            g.setColor(Color.red);
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("失败,请重新开始游戏",300,300);
        }
    }
    //内部类,键盘监听
    class GameListener extends KeyAdapter{
        @Override
        public void keyPressed(KeyEvent e) {
            int keyCode = e.getKeyCode();//获得键盘按键是哪一个
            //空格开始游戏
            if (keyCode == KeyEvent.VK_SPACE) {
                if(isFail){
                    isFail = false;
                    init();
                }else {
                    isStart = !isStart;//取反
                }
                repaint();
            }
            //移动
            if (keyCode == KeyEvent.VK_UP) {
                direction = "U";
            } else if (keyCode == KeyEvent.VK_DOWN) {
                direction = "D";
            } else if (keyCode == KeyEvent.VK_RIGHT) {
                direction = "R";
            } else if (keyCode == KeyEvent.VK_LEFT) {
                direction = "L";
            }
        }
    }
    //定时刷新
    class timeListener implements ActionListener{
        @Override
        public void actionPerformed(ActionEvent e) {
            if(isStart && !isFail){
                for (int i = length-1;i>0;i--){
                    //将后一节移动到前一节的位置
                    snakeX[i] = snakeX[i-1];
                    snakeY[i] = snakeY[i-1];
                }
                switch (direction) {
                    case "R":
                        snakeX[0] = snakeX[0] + 25;
                        break;
                    case "L":
                        snakeX[0] = snakeX[0] - 25;
                        break;
                    case "U":
                        snakeY[0] = snakeY[0] - 25;
                        break;
                    case "D":
                        snakeY[0] = snakeY[0] + 25;
                        break;
                }
                //吃事物事件
                if (snakeX[0]==foodX && snakeY[0]==foodY){
                    length++;//长度增加
                    //重新生成食物
                    foodX = 25+25*random.nextInt(33);
                    foodY = 25+25*random.nextInt(23);
                }
                //失败判定
                if(snakeX[0] > 850 || snakeX[0] < 25 || snakeY[0] > 650 || snakeY[0] < 25){ //边界判定
                    isFail = true;
                }
                for (int i = 1;i < length;i++) {//重合判定
                    if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
                        isFail = true;
                        break;
                    }
                }
                repaint();//重画页面
            }
            timer.start();//开启定时器
        }
    }
}原文:https://www.cnblogs.com/ellll/p/15195925.html