GUI编程小实践——一小时完成贪吃蛇小游戏

GUI编程自我总结

GUI编程,是一种java早期的图形化界面编程,因为编写效率、界面效果和交互逻辑不能紧跟当下时代要求,已经有些渐渐淡出大众视野

但是作为学习Java和理解一些热门框架的入门练手还是很合适的,而且因为包含的内容不多,只谈谈重点的话就只需要解释两个关键的包java.awt和java.swing

在AWT中主要包含的是一些抽象的窗口工具,重在理解容器(Container)、面板(Panel)和窗口(Frame)这些元素的关系和布局问题

而swing则是Frame的一个子类,重在掌握一些元素的使用,有弹窗(Dialog)、标签(Label)、按钮(Button)、列表(JList)和文本框(Text)等

这次总结实践做的项目就是做一个贪吃蛇小游戏,当然只是实现了简单的开始和结束功能,更多的完善和优化还有很大的空间

全文阅读和自己动手完成保守需要两小时左右,希望不会浪费读者宝贵的时间

一小时贪吃蛇小游戏

在准备动手之前你需要先准备好小蛇的身体、“食物”的图片和游戏的头部图片,考虑到小蛇的移动,你需要将头部上下左右旋转着保存一遍

相对简单,所以你可以自己制作或者寻找差不多的图片替代一下都可以,下面是我准备的图片

预备图片

我用的IDE是IntelliJ IDEA 2019.1 x64版本,jdk版本用的13.0.2,当然任何集成开发软件都是可以的,我们就可以新建一个java项目

首先在新建的项目中导入你刚才准备的图片,尽量将图片资源与程序放在一个文件夹下,之后说我这样做的原因

下面就可以开始准备写程序了,写程序之前,你需要先把考虑到的写死的私有变量写在单独的类中,这是优秀的编程习惯

以下是我的资源文件的代码
package com.sbc.GUI.Snack;

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

//数据中心
public class Data {
    //相对路径
    private static URL headURL = Data.class.getResource("Snack/head.jpg");
    public static ImageIcon head = new ImageIcon(headURL);

    private static URL upURL = Data.class.getResource("Snack/up.jpg");
    public static ImageIcon up = new ImageIcon(upURL);

    private static URL downURL = Data.class.getResource("Snack/down.jpg");
    public static ImageIcon down = new ImageIcon(downURL);

    private static URL rightURL = Data.class.getResource("Snack/right.jpg");
    public static ImageIcon right = new ImageIcon(rightURL);

    private static URL leftURL = Data.class.getResource("Snack/left.jpg");
    public static ImageIcon left = new ImageIcon(leftURL);

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

    private static URL flagURL = Data.class.getResource("Snack/flag.jpg");
    public static ImageIcon flag = new ImageIcon(flagURL);
}

相信你已经看到了,这里我引用的图片用的是相对路径,自己项目经验过少,查阅资料说绝对路径会影响Linux部署问题,当然这个小游戏没有影响

所以你可以用你喜欢的方式来引用图片资源,这里我引用'/'来充当根目录时,是得不到图片资源的,这也是我选择放在一个文件夹下用相对路径的原因,也许是自己记得绝对路径方式有误吧

之后再写一个类,来作为启动类,这里面就是标准的窗口写法,以下是代码
package com.sbc.GUI.Snack;

import javax.swing.*;
//启动程序
public class StartSnack {
    public static void main(String[] args) {
        JFrame jFrame = new JFrame();

        jFrame.add(new GamePanel());

        //窗口可见性
        jFrame.setVisible(true);
        //窗口形状不可改变
        jFrame.setResizable(false);
        //窗口大小
        jFrame.setSize(900,720);
    }
}

接下来就是主要内容了,我们要做的游戏界面都要再一个Panel中完成,首先我们要先知道游戏的思路

当然大家对于贪吃蛇的游戏规则肯定是耳熟能详了,所以我们主要解释我们的实现思路,其实游戏大概就由初始化方法、监听器、定时器和绘图方法组成

初始化方法就是将变量初始化,保证游戏一开始的界面和开始顺利,游戏的开始图画是这样的

开始界面

其次是监听器,监听器主要负责监听用户操作键盘的操作,本游戏主要是监听空格和上下左右键的动作

定时器的存在是定时重新绘图,速度不能过快或者过慢,定时器的数值都可以参照我的代码中的来

绘图方法主要考虑的就是界面的布局,很多数字需要自己计算,而且绘图的顺序在吃掉“食物”时要保证蛇头在“食物”上方,当然其他优化的地方还需要你自己来完成

所以其实编程思路就是定义变量,然后绘制好,最后在监听器和定时器中做好逻辑交互即可,只要思路明确,自己很快就可以独立完成

还有积分的计算就是一个Label,自己找好位置得到自己定义的长度值就可以了,下面是代码展示
package com.sbc.GUI.Snack;

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;

//游戏面板
public class GamePanel extends JPanel implements KeyListener, ActionListener {
    public GamePanel() {
        init();
    }

    //定义蛇的属性
    int length;
    int[] snack_x = new int[600];
    int[] snack_y = new int[500];
    //蛇头方向
    String turn = "R";
    //游戏当前状态
    boolean isStart = false;
    boolean isFail = false;
    //定时器  1000ms = 1s
    Timer timer = new Timer(100, this);
    //食物的坐标
    int flagX;
    int flagY;
    Random ran = new Random();
    //积分成绩
    int score;

    //初始化方法
    public void init() {
        //初始化成绩
        score = 0;
        //初始长度
        length = 3;
        //初始头部位置
        snack_x[0] = 100;
        snack_y[0] = 100;

        //第一个身体坐标
        snack_x[1] = 75;
        snack_y[1] = 100;

        //第二个身体坐标
        snack_x[2] = 50;
        snack_y[2] = 100;

        turn = "R";

        this.setFocusable(true);
        this.addKeyListener(this);

        flagX = 25 + 25 * ran.nextInt(34);
        flagY = 75 + 25 * ran.nextInt(24);

        timer.start();
    }

    //绘制面板
    @Override
    protected void paintComponent(Graphics g) {
        //清屏
        super.paintComponent(g);

        //绘制静态面板
        //设置背景颜色
        this.setBackground(Color.white);
        Data.head.paintIcon(this, g, 18, 11);
        g.fillRect(18, 70, 850, 600);

        //画积分
        g.setColor(Color.red);
        g.setFont(new Font("微软雅黑", Font.BOLD, 18));
        g.drawString("长度" + length, 750, 35);
        g.drawString("分数" + 10 * length, 750, 50);

        //画食物目标
        Data.flag.paintIcon(this, g, flagX, flagY);

        //画小蛇
        if (turn.equals("R")) {
            Data.right.paintIcon(this, g, snack_x[0], snack_y[0]);
        }
        if (turn.equals("L")) {
            Data.left.paintIcon(this, g, snack_x[0], snack_y[0]);
        }
        if (turn.equals("U")) {
            Data.up.paintIcon(this, g, snack_x[0], snack_y[0]);
        }
        if (turn.equals("D")) {
            Data.down.paintIcon(this, g, snack_x[0], snack_y[0]);
        }

        for (int i = 1; i <= length - 1; i++) {
            Data.body.paintIcon(this, g, snack_x[i], snack_y[i]);
        }


        //游戏状态
        if (isStart == false) {
            g.setColor(Color.white);
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("按下空格开始游戏!", 300, 300);
        }

        if (isFail == true) {
            g.setColor(Color.red);
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("游戏结束!", 300, 300);
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

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

    @Override
    public void keyReleased(KeyEvent e) {

    }

    //定时器定时刷新
    @Override
    public void actionPerformed(ActionEvent e) {

        if (isFail == false && isStart == true) {

            //吃食物
            if (snack_x[0] == flagX && snack_y[0] == flagY) {
                length++;
                score += 10;
                flagX = 25 + 25 * ran.nextInt(34);
                flagY = 75 + 25 * ran.nextInt(24);
            }

            if (isStart) {
                //移动操作
                for (int i = length - 1; i > 0; i--) {
                    snack_x[i] = snack_x[i - 1];
                    snack_y[i] = snack_y[i - 1];
                }

                if (turn.equals("R")) {
                    snack_x[0] += 25;
                    //边界判断
                    if (snack_x[0] > 850) {
                        snack_x[0] = 25;
                    }
                } else if (turn.equals("L")) {
                    snack_x[0] -= 25;
                    //边界判断
                    if (snack_x[0] < 25) {
                        snack_x[0] = 850;
                    }
                } else if (turn.equals("U")) {
                    snack_y[0] -= 25;
                    //边界判断
                    if (snack_y[0] < 75) {
                        snack_y[0] = 650;
                    }
                } else if (turn.equals("D")) {
                    snack_y[0] += 25;
                    //边界判断
                    if (snack_y[0] > 650) {
                        snack_y[0] = 75;
                    }
                }
            }
            //失败判定
            for (int i = 1; i < length; i++) {
                if (snack_x[0] == snack_x[i] && snack_y[0] == snack_y[i]) {
                    isFail = true;
                }
            }

            repaint();
        }
        timer.start();
    }

}

以上就是全部代码了,相信你已经自己调试和完成了,自己肯定也完了好几局了,所以你会发现你有很多可以改善的地方

比如说存档下次继续、选择闯关难度和增加障碍物,只要你想到的都可以加到上面,相信编程的我们有自己独特的想法和思路

最后附上自己较长的游戏图片,相信大家肯定有比我还优秀的,都可以在评论区展示自己的作品和成绩

游戏结束


总结

通过这个小游戏,我也熟练了GUI编程的基本过程,当然在过程中也遇到了一些问题

在我继承了键盘监听器接口的时候,我只需要重写键盘按压方法的接口就可以了,但是我发现我必须要把其他两个接口方法显示出来

我度娘后才知道吗,原来一个类实现接口方法时,如果是非抽象类需要重写所有方法,否之不然

以上就是贪吃蛇小程序所有的内容了,如果存在问题或者有可以完善和改正的地方,希望大佬斧正,希望大家和我一起讨论一起进步
posted @ 2021-02-21 20:55  21岁还不是架构师  阅读(235)  评论(0编辑  收藏  举报