Android基础Java 基础爱上Android

Java GUI 实例:贪吃蛇游戏

2022-01-15  本文已影响0人  yjtuuige

Java GUI 基础

一、实现效果

  1. 空格暂停;
  2. 上、下、左、右控制方向;
  3. 长度随着食物的吃下,而增加;
  4. 定时刷新;
  5. 累计长度和分数等。

二、实现思路:

  1. 定义所有功能的数据
  2. 绘制功能需要的图形
  3. 添加监听功能需要的事件(帧率事件)键盘、鼠标

三、实现代码

  1. 静态界面绘制
package com.xxx.gui.snake;

import javax.swing.*;

/**
 * 游戏主启动类
 */
public class StartGame {
    public StartGame() {
        JFrame frame = new JFrame("贪吃蛇");
        // 长宽尺寸,根据内容计算得出
        frame.setBounds(10, 10, 915, 730);
        // 窗口大小不可变,避免游戏变形
        frame.setResizable(false);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        // 将游戏面板添加到窗口中
        frame.add(new GamePanel());
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        new StartGame();
    }
}
package com.xxx.gui.snake;

import javax.swing.*;
import java.net.URL;

/**
 * 数据类
 */
public class Data {
    // 相对路径 header.png
    // 绝对路径 / 相当于当前的项目
    // 顶部图片
    public static URL headerURL = Data.class.getResource("statics/header.png");
    // 图像转为图标
    public static ImageIcon header = new ImageIcon(headerURL);
    // 上下左右
    public static URL upURL = Data.class.getResource("statics/up.png");
    public static URL downURL = Data.class.getResource("statics/down.png");
    public static URL leftURL = Data.class.getResource("statics/left.png");
    public static URL rightURL = Data.class.getResource("statics/right.png");
    public static ImageIcon up = new ImageIcon(upURL);
    public static ImageIcon down = new ImageIcon(downURL);
    public static ImageIcon left = new ImageIcon(leftURL);
    public static ImageIcon right = new ImageIcon(rightURL);
    // 身体
    public static URL bodyURL = Data.class.getResource("statics/body.png");
    public static ImageIcon body = new ImageIcon(bodyURL);
    // 食物
    public static URL foodURL = Data.class.getResource("statics/food.png");
    public static ImageIcon food = new ImageIcon(foodURL);
}
package com.xxx.gui.snake;

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

/**
 * 游戏的面板
 */
public class GamePanel extends JPanel {
    // 绘制面板,游戏中所有的东西,都是用这个笔来画
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);    // 清屏
        // 绘制静态面板
        this.setBackground(Color.WHITE);
        // 顶部图片绘制到当前组件中
        Data.header.paintIcon(this, g, 25, 11);
        // 默认的游戏界面,坐标及尺寸,经过计算得出
        g.fillRect(25, 75, 850, 600);
    }
}
  1. 绘制静态小蛇
package com.xxx.gui.snake;

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

/**
 * 游戏的面板
 */
public class GamePanel extends JPanel {
    // 定义蛇的数据结构
    int length; // 蛇的长度
    int[] snakeX = new int[600];  // 蛇的 X 坐标,25*25
    int[] snakeY = new int[500];  // 蛇的 Y 坐标,25*25
    String fx;  // 初始方向:向右
    // 游戏当前状态:开始,停止
    boolean isStart = false;    // 默认停止

    // 构造器
    public GamePanel() {
        // 调用初始化
        init();
    }

    // 初始化方法
    public void init() {
        // 初始蛇有三节,包括头
        length = 3;
        // 初始化开始的蛇,给蛇定位
        // 头部位置
        snakeX[0] = 100;
        snakeY[0] = 100;
        // 第一节身体
        snakeX[1] = 75;
        snakeY[1] = 100;
        // 第二节身体
        snakeX[2] = 50;
        snakeY[2] = 100;
        fx = "R";
    }

    // 绘制面板,游戏中所有的东西,都是用这个笔来画
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);    // 清屏
        // 绘制静态面板
        this.setBackground(Color.WHITE);
        // 顶部图片绘制到当前组件中
        Data.header.paintIcon(this, g, 25, 11);
        // 默认的游戏界面,坐标及尺寸,经过计算得出
        g.fillRect(25, 75, 850, 600);

        // 把小蛇画上去
        // 蛇头初始化,向右,需要通过方向来判断
        if (fx.equals("R")) {
            Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("L")) {
            Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("U")) {
            Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("D")) {
            Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
        }

        // 根据身体的长度,绘制蛇身
        for (int i = 1; i < length; i++) {
            Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
        }

        //游戏状态
        if (isStart == false) {
            g.setColor(Color.white);
            g.setFont(new Font("黑体", Font.BOLD, 40));   // 设置字体
            g.drawString("按下空格开始游戏", 300, 300);    // 初始文字
        }
    }
}
  1. 小蛇开始移动
package com.xxx.gui.snake;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

/**
 * 游戏的面板
 * ActionListener(接口):实现定时器
 */
public class GamePanel extends JPanel implements KeyListener, ActionListener {
    // 定义蛇的数据结构
    int length; // 蛇的长度
    int[] snakeX = new int[600];  // 蛇的 X 坐标,25*25
    int[] snakeY = new int[500];  // 蛇的 Y 坐标,25*25
    String fx;  // 初始方向:向右
    // 游戏当前状态:开始,停止
    boolean isStart = false;    // 默认停止

    // 定时器:100(ms) 毫秒刷新一次(1000ms=1秒)
    Timer timer = new Timer(100, this);

    // 构造器
    public GamePanel() {
        // 调用初始化
        init();
        // 获取事件
        this.setFocusable(true);    // 焦点事件
        this.addKeyListener(this); // 键盘监听事件
        timer.start();  // 游戏一开始,定时器启动
    }

    // 初始化方法
    public void init() {
        // 初始蛇有三节,包括头
        length = 3;
        // 初始化开始的蛇,给蛇定位
        // 头部位置
        snakeX[0] = 100;
        snakeY[0] = 100;
        // 第一节身体
        snakeX[1] = 75;
        snakeY[1] = 100;
        // 第二节身体
        snakeX[2] = 50;
        snakeY[2] = 100;
        fx = "R";
    }

    // 绘制面板,游戏中所有的东西,都是用这个笔来画
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);    // 清屏
        // 绘制静态面板
        this.setBackground(Color.WHITE);
        // 顶部图片绘制到当前组件中
        Data.header.paintIcon(this, g, 25, 11);
        // 默认的游戏界面,坐标及尺寸,经过计算得出
        g.fillRect(25, 75, 850, 600);

        // 把小蛇画上去
        // 蛇头初始化,向右,需要通过方向来判断
        if (fx.equals("R")) {
            Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("L")) {
            Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("U")) {
            Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("D")) {
            Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
        }

        // 根据身体的长度,绘制蛇身
        for (int i = 1; i < length; i++) {
            Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
        }

        //游戏状态
        if (isStart == false) {
            g.setColor(Color.white);
            g.setFont(new Font("黑体", Font.BOLD, 40));   // 设置字体
            g.drawString("按下空格开始游戏", 300, 300);    // 初始文字
        }
    }

    // 键盘监听事件
    @Override
    public void keyPressed(KeyEvent e) {
        // 获得键盘按键
        int keyCode = e.getKeyCode();
        // 如果按下空格键
        if (keyCode == KeyEvent.VK_SPACE) {
            isStart = !isStart;   // 取反
            repaint();  // 重新绘制
        }
        // 小蛇移动(通过方向键判断)
        if (keyCode == KeyEvent.VK_UP) {
            fx = "U";
        } else if (keyCode == KeyEvent.VK_DOWN) {
            fx = "D";
        } else if (keyCode == KeyEvent.VK_LEFT) {
            fx = "L";
        } else if (keyCode == KeyEvent.VK_RIGHT) {
            fx = "R";
        }
    }

    // 事件监听:定时执行时的操作(刷新)
    @Override
    public void actionPerformed(ActionEvent e) {
        // 如果是开始状态,让小蛇移动
        if (isStart) {
            // 移动:
            for (int i = length - 1; i > 0; i--) {
                // 身体后一节移到前一节的位置(snakeX[1]=snakeX[0])
                snakeX[i] = snakeX[i - 1];
                snakeY[i] = snakeY[i - 1];
            }
            // 通过方向键控制,头部移动
            if (fx.equals("R")) {
                // 头部 X 坐标,右移一格(25)
                snakeX[0] = snakeX[0] + 25;
                // 判断边界:超出后,回到最左边
                if (snakeX[0] > 850) snakeX[0] = 25;
            } else if (fx.equals("L")) {
                snakeX[0] = snakeX[0] - 25;
                // 判断边界:超出后,回到最右边
                if (snakeX[0] < 25) snakeX[0] = 850;
            } else if (fx.equals("U")) {
                snakeY[0] = snakeY[0] - 25;
                // 判断边界:超出后,回到最左边
                if (snakeY[0] < 75) snakeY[0] = 650;
            } else if (fx.equals("D")) {
                snakeY[0] = snakeY[0] + 25;
                if (snakeY[0] > 650) snakeY[0] = 75;
            }
            repaint();  // 重画页面
        }
        timer.start();  // 定时器开启
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }
}
  1. 小蛇开始吃食物
package com.xxx.gui.snake;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

/**
 * 游戏的面板
 * ActionListener(接口):实现定时器
 */
public class GamePanel extends JPanel implements KeyListener, ActionListener {
    // 定义蛇的数据结构
    int length; // 蛇的长度
    int[] snakeX = new int[600];  // 蛇的 X 坐标,25*25
    int[] snakeY = new int[500];  // 蛇的 Y 坐标,25*25
    String fx;  // 初始方向:向右

    // 食物坐标
    int foodX;
    int foodY;
    Random random = new Random();   // 获取随机数

    // 游戏当前状态:开始,停止
    boolean isStart = false;    // 默认停止

    // 定时器:100(ms) 毫秒刷新一次(1000ms=1秒)
    Timer timer = new Timer(100, this);

    // 构造器
    public GamePanel() {
        // 调用初始化
        init();
        // 获取事件
        this.setFocusable(true);    // 焦点事件
        this.addKeyListener(this); // 键盘监听事件
        timer.start();  // 游戏一开始,定时器启动
    }

    // 初始化方法
    public void init() {
        // 初始蛇有三节,包括头
        length = 3;
        // 初始化开始的蛇,给蛇定位
        // 头部位置
        snakeX[0] = 100;
        snakeY[0] = 100;
        // 第一节身体
        snakeX[1] = 75;
        snakeY[1] = 100;
        // 第二节身体
        snakeX[2] = 50;
        snakeY[2] = 100;
        fx = "R";
        // 把食物随机分布在界面上
        foodX = 25 + 25 * random.nextInt(34); // 34:850/25
        foodY = 75 + 25 * random.nextInt(34); // 24:600/25

    }

    // 绘制面板,游戏中所有的东西,都是用这个笔来画
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);    // 清屏
        // 绘制静态面板
        this.setBackground(Color.WHITE);
        // 顶部图片绘制到当前组件中
        Data.header.paintIcon(this, g, 25, 11);
        // 默认的游戏界面,坐标及尺寸,经过计算得出
        g.fillRect(25, 75, 850, 600);

        // 把小蛇画上去
        // 蛇头初始化,向右,需要通过方向来判断
        if (fx.equals("R")) {
            Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("L")) {
            Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("U")) {
            Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("D")) {
            Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
        }

        // 根据身体的长度,绘制蛇身
        for (int i = 1; i < length; i++) {
            Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
        }

        // 画食物
        Data.food.paintIcon(this, g, foodX, foodY);

        //游戏状态
        if (isStart == false) {
            g.setColor(Color.white);
            g.setFont(new Font("黑体", Font.BOLD, 40));   // 设置字体
            g.drawString("按下空格开始游戏", 300, 300);    // 初始文字
        }
    }

    // 键盘监听事件
    @Override
    public void keyPressed(KeyEvent e) {
        // 获得键盘按键
        int keyCode = e.getKeyCode();
        // 如果按下空格键
        if (keyCode == KeyEvent.VK_SPACE) {
            isStart = !isStart;   // 取反
            repaint();  // 重新绘制
        }
        // 小蛇移动(通过方向键判断)
        if (keyCode == KeyEvent.VK_UP) {
            fx = "U";
        } else if (keyCode == KeyEvent.VK_DOWN) {
            fx = "D";
        } else if (keyCode == KeyEvent.VK_LEFT) {
            fx = "L";
        } else if (keyCode == KeyEvent.VK_RIGHT) {
            fx = "R";
        }
    }

    // 事件监听:定时执行时的操作(刷新)
    @Override
    public void actionPerformed(ActionEvent e) {
        // 如果是开始状态,让小蛇移动
        if (isStart) {
            // 吃食物
            if (snakeX[0] == foodX && snakeY[0] == foodY) {
                // 身体长度+1
                length++;
                // 再次随机食物
                // 把食物随机分布在界面上
                foodX = 25 + 25 * random.nextInt(34);
                foodY = 75 + 25 * random.nextInt(34);
            }
            // 移动:
            for (int i = length - 1; i > 0; i--) {
                // 身体后一节移到前一节的位置(snakeX[1]=snakeX[0])
                snakeX[i] = snakeX[i - 1];
                snakeY[i] = snakeY[i - 1];
            }
            // 通过方向键控制,头部移动
            if (fx.equals("R")) {
                // 头部 X 坐标,右移一格(25)
                snakeX[0] = snakeX[0] + 25;
                // 判断边界:超出后,回到最左边
                if (snakeX[0] > 850) snakeX[0] = 25;
            } else if (fx.equals("L")) {
                snakeX[0] = snakeX[0] - 25;
                // 判断边界:超出后,回到最右边
                if (snakeX[0] < 25) snakeX[0] = 850;
            } else if (fx.equals("U")) {
                snakeY[0] = snakeY[0] - 25;
                // 判断边界:超出后,回到最左边
                if (snakeY[0] < 75) snakeY[0] = 650;
            } else if (fx.equals("D")) {
                snakeY[0] = snakeY[0] + 25;
                if (snakeY[0] > 650) snakeY[0] = 75;
            }
            repaint();  // 重画页面
        }
        timer.start();  // 定时器开启
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }
}
  1. 失败判定,积分系统
package com.xxx.gui.snake;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

/**
 * 游戏的面板
 * ActionListener(接口):实现定时器
 */
public class GamePanel extends JPanel implements KeyListener, ActionListener {
    // 定义蛇的数据结构
    int length; // 蛇的长度
    int[] snakeX = new int[600];  // 蛇的 X 坐标,25*25
    int[] snakeY = new int[500];  // 蛇的 Y 坐标,25*25
    String fx;  // 初始方向:向右
    // 食物坐标
    int foodX;
    int foodY;
    Random random = new Random();   // 获取随机数
    // 成绩
    int score;
    // 游戏当前状态:开始,停止
    boolean isStart = false;    // 默认停止
    // 游戏失败状态
    boolean isFail = false;
    // 定时器:100(ms) 毫秒刷新一次(1000ms=1秒)
    Timer timer = new Timer(100, this);

    // 构造器
    public GamePanel() {
        // 调用初始化
        init();
        // 获取事件
        this.setFocusable(true);    // 焦点事件
        this.addKeyListener(this); // 键盘监听事件
        timer.start();  // 游戏一开始,定时器启动
    }

    // 初始化方法
    public void init() {
        // 初始蛇有三节,包括头
        length = 3;
        // 初始化开始的蛇,给蛇定位
        // 头部位置
        snakeX[0] = 100;
        snakeY[0] = 100;
        // 第一节身体
        snakeX[1] = 75;
        snakeY[1] = 100;
        // 第二节身体
        snakeX[2] = 50;
        snakeY[2] = 100;
        fx = "R";
        // 把食物随机分布在界面上
        foodX = 25 + 25 * random.nextInt(34); // 34:850/25
        foodY = 75 + 25 * random.nextInt(24); // 24:600/25

        score = 0;
    }

    // 绘制面板,游戏中所有的东西,都是用这个笔来画
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);    // 清屏
        // 绘制静态面板
        this.setBackground(Color.WHITE);
        // 顶部图片绘制到当前组件中
        Data.header.paintIcon(this, g, 25, 11);
        // 默认的游戏界面,坐标及尺寸,经过计算得出
        g.fillRect(25, 75, 850, 600);
        // 画积分
        g.setColor(Color.WHITE);
        g.setFont(new Font("黑体", Font.BOLD, 16));
        g.drawString("长度 " + length, 750, 32);
        g.drawString("分数 " + score, 750, 53);
        // 画食物
        Data.food.paintIcon(this, g, foodX, foodY);
        // 把小蛇画上去
        // 蛇头初始化,向右,需要通过方向来判断
        if (fx.equals("R")) {
            Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("L")) {
            Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("U")) {
            Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
        } else if (fx.equals("D")) {
            Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
        }
        // 根据身体的长度,绘制蛇身
        for (int i = 1; i < length; i++) {
            Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
        }
        // 游戏状态
        if (isStart == false) {
            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);    // 初始文字
        }
    }

    // 键盘监听事件
    @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) {
            fx = "U";
        } else if (keyCode == KeyEvent.VK_DOWN) {
            fx = "D";
        } else if (keyCode == KeyEvent.VK_LEFT) {
            fx = "L";
        } else if (keyCode == KeyEvent.VK_RIGHT) {
            fx = "R";
        }
    }

    // 事件监听:定时执行时的操作(刷新)
    @Override
    public void actionPerformed(ActionEvent e) {
        // 如果是开始状态,且没有结束,让小蛇移动
        if (isStart && isFail == false) {
            // 吃食物
            if (snakeX[0] == foodX && snakeY[0] == foodY) {
                // 身体长度+1
                length++;
                // 分数+10
                score += 10;
                // 再次随机食物
                // 把食物随机分布在界面上
                foodX = 25 + 25 * random.nextInt(34);
                foodY = 75 + 25 * random.nextInt(24);
            }
            // 移动:
            for (int i = length - 1; i > 0; i--) {
                // 身体后一节移到前一节的位置(snakeX[1]=snakeX[0])
                snakeX[i] = snakeX[i - 1];
                snakeY[i] = snakeY[i - 1];
            }
            // 通过方向键控制,头部移动
            if (fx.equals("R")) {
                // 头部 X 坐标,右移一格(25)
                snakeX[0] = snakeX[0] + 25;
                // 判断边界:超出后,回到最左边
                if (snakeX[0] > 850) snakeX[0] = 25;
            } else if (fx.equals("L")) {
                snakeX[0] = snakeX[0] - 25;
                // 判断边界:超出后,回到最右边
                if (snakeX[0] < 25) snakeX[0] = 850;
            } else if (fx.equals("U")) {
                snakeY[0] = snakeY[0] - 25;
                // 判断边界:超出后,回到最左边
                if (snakeY[0] < 75) snakeY[0] = 650;
            } else if (fx.equals("D")) {
                snakeY[0] = snakeY[0] + 25;
                if (snakeY[0] > 650) snakeY[0] = 75;
            }
            // 失败判断:撞到自己
            // 遍历身体的每一个坐标
            for (int i = 1; i < length; i++) {
                // 头部坐标与身体坐标重合
                if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
                    // 游戏失败
                    isFail = true;
                }
            }
            repaint();  // 重画页面
        }
        timer.start();  // 定时器开启
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }
}

Java 版贪吃蛇,图片素材:

上一篇下一篇

猜你喜欢

热点阅读