GUI入门项目:一小时完成贪吃蛇

7 篇文章 4 订阅
1 篇文章 0 订阅

4.贪吃蛇小游戏

输出效果:

在这里插入图片描述

预科:帧,如果时间片足够小,那就是动画。一秒30帧/60帧,连接起来就是动画,拆分开来就是静态图片。
实测,纯敲代码事件在一小时内,但是搞懂体系结构等问题,使用较长的时间。

体系结构:

在这里插入图片描述

整体编写流程:
1.定义数据
2.画上面板
3.监听事件

  • 键盘
  • 事件

4.有待优化的问题:

  • 不许回头吃自己
  • 分数等级,小蛇对应不同的速度
  • 界面优化……

代码:

StartGame:

package com.edwin.snake;
import javax.swing.*;
/**
 * @author EdwinD
 * @create 2020.08.19 下午 08:09
 * @desc 贪吃蛇主启动界面
 **/
public class StartGame {
    public static void main(String[] args) {
        JFrame frame = new JFrame("EdwinD's TanChi Snake.");

//      正常的游戏内容都在界面上。
        frame.add(new GamePanel());

        frame.setBounds(477, 177, 900, 720);
        frame.setResizable(false);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

GamePanel:

package com.edwin.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;
/**
 * @author EdwinD
 * @create 2020.08.19 下午 08:11
 * @desc 游戏的面板
 * 编写流程:
    1.定义数据
    2.画上面板
    3.监听事件
        - 键盘
        - 事件
    4.有待优化的问题:
        - 不许回头吃自己
        - 分数等级,对应不同的速度
        - 界面优化……
 **/
public class GamePanel extends JPanel implements KeyListener, ActionListener {

    //    定义蛇的数据结构
    int length; // 蛇的长度
    int[] snakeX = new int[600]; // 蛇的X坐标:25*25
    int[] snakeY = new int[600]; // 蛇的Y// 坐标:25*25
    //    初始方向
    String fx;
    //    游戏当前的状态:开始 / 暂停或停止
    boolean isStart = false; //默认关闭
    boolean isFail = false; //游戏失败的状态
    //   食物的坐标
    int foodX;
    int foodY;
    Random random = new Random();
    //   成绩,积分
    int score;

    //    定时器,以毫秒(ms)为单位,1000ms = 1s
    Timer timer = new Timer(100,this);// 100毫秒执行一次

    //    构造器,小蛇没有此构造器,将会是一坨堆在那里。
    public GamePanel() {
        init();
//        获得焦点和键盘事件
        this.setFocusable(true);//获得焦点事件,即鼠标是否聚焦了页面
        this.addKeyListener(this);//获得键盘监听事件,即是键盘否聚焦了页面
        timer.start();// 游戏一开始,定时器就启动
    }

    //    初始化数据
    public void init() {
//        身体
        length = 5;
        snakeX[0] = 150;snakeY[0] = 100; // 脑袋的坐标
        snakeX[1] = 125;snakeY[1] = 100; // 第一节身体的坐标
        snakeX[2] = 100;snakeY[2] = 100; // 第二节身体的坐标
        snakeX[3] = 75; snakeY[3] = 100; // 第三节身体的坐标
        snakeX[4] = 50; snakeY[4] = 100; // 第四节身体的坐标
//        方向
        fx = "R";
//        食物
        foodX = 25 + 25 * random.nextInt(34);// 设置边界,保证食物可以被吃到
        foodY = 75 + 25 * random.nextInt(24);// 设置边界,保证食物可以被吃到
//        积分
        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);

/*      画上小蛇,静态的,无法变动
        Data.right.paintIcon(this, g, snakeX[0], snakeY[0]); // 蛇头的初始化向右
        Data.body.paintIcon(this, g, snakeX[1], snakeY[1]); // 第一节身体的坐标
        Data.body.paintIcon(this, g, snakeX[2], snakeY[2]); // 第二节身体的坐标
        Data.body.paintIcon(this, g, snakeX[3], snakeY[3]); // 第三节身体的坐标
        Data.body.paintIcon(this, g, snakeX[4], snakeY[4]); // 第四节身体的坐标
*/
//        画积分
        g.setColor(Color.WHITE);
        g.setFont(new Font("楷体", Font.BOLD, 17));
        g.drawString("长度"+length,750,30);
        g.drawString("分数"+score,750,50);
//        画食物
        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,37));
            g.drawString("按下空格开始游戏!",300,300);
        }

//        判断是否失败
        if (isFail) {
            g.setColor(Color.RED);
            g.setFont(new Font("楷体",Font.BOLD,37));
            g.drawString("您失败了,请按空格重新开始!",200,300);
        }
    }

    //键盘监听器
    @Override
    public void keyPressed(KeyEvent e) {
        int KeyCode = e.getKeyCode(); //获得按下的是键盘的哪个键

        if (KeyCode == KeyEvent.VK_SPACE) { // 如果按下的是空格键
//            isStart = !isStart;//取反,开始变暂停
//            repaint(); 在添加了判断失败后,需要更新换代
            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";
        }
    }

//    事件监听——>需要通过固定事件来刷新,比如 1s 刷新10此
    @Override
    public void actionPerformed(ActionEvent e) {
//      如果游戏是开始状态,才允许小蛇进行运动
        if (isStart && isFail ==false) {
//            吃食物
            if (snakeX[0] == foodX && snakeY[0] == foodY) {
//                长度加一
                length++;
//                分数加十
                score += 10;
//                再次出现随机食物
                foodX = 25 + 25 * random.nextInt(34);// 设置边界,保证食物可以被吃到
                foodY = 75 + 25 * random.nextInt(24);// 设置边界,保证食物可以被吃到
            }
//            右移动,后一节身体移动到前一节的位置,即snakeX[1] = snakeX[0]
            for (int i = length - 1; i > 0; i--) {
//                向前移动一节
                snakeX[i] = snakeX[i - 1];
                snakeY[i] = snakeY[i - 1];
            }
//            需要先写for循环,否则小蛇前进的过程中,会出现头和第一节身体重合的情况
//            snakeX[0] = snakeX[0] + 25; //在写了走向的代码后,这一段可以删除了。
//            走向
            if (fx.equals("R")) {
                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 k = 1; k < length; k++) {
                if (snakeX[0] == snakeX[k] && snakeY[0] == snakeY[k]) {
                    isFail = true;
                }
            }

//            重画页面,刷新
            repaint();
        }
//        定时器开启
        timer.start();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
    @Override
    public void keyTyped(KeyEvent e) {

    }
}

Data:

package com.edwin.snake;
import javax.swing.*;
import java.net.URL;
/**
 * @author EdwinD
 * @create 2020.08.19 下午 08:27
 * @desc 数据存储中心
 **/
public class Data {
/*
    两种路径:
    相对路径:相对于当前文件的路径,比如直接加上Study.jpg。
    绝对路径:使用 "/" 来引导。 ”/“代表了当前的项目。
*/

    private static URL headerURL = Data.class.getResource("static/header.png");
    public static ImageIcon header = new ImageIcon(headerURL);

    private static URL upURL = Data.class.getResource("static/up.png");
    private static URL downURL = Data.class.getResource("static/down.png");
    private static URL leftURL = Data.class.getResource("static/left.png");
    private static URL rightURL = Data.class.getResource("static/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);

    private static URL bodyURL = Data.class.getResource("static/body.png");
    public static ImageIcon body = new ImageIcon(bodyURL);

    private static URL foodURL = Data.class.getResource("static/food.png");
    public static ImageIcon food = new ImageIcon(foodURL);
}

输出效果;

在这里插入图片描述

路漫漫其修远兮,吾将上下而求索。

参考文献

《【狂神说Java】GUI编程入门到游戏实战》
视频链接

2020.08.20

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值