项目二:贪吃蛇的游戏开发

本文描述了一个使用JavaSwing实现的贪吃蛇游戏项目。项目包括设计图形界面,创建基于对话框的应用,添加背景、标题和各种控件。游戏中的蛇能通过键盘控制移动,有边界或无边界设定,食物随机生成,墙体会阻挡蛇的移动。游戏还包括计分系统,允许玩家通过空格键开始或暂停游戏,以及游戏介绍和操作帮助。代码中实现了蛇的移动、食物生成、碰撞检测等功能,并包含了一个菜单栏提供游戏选项和帮助信息。

1、项目概述

1.1项目目标和主要内容

  1. 学习图形界面的设计,利用 MFC 应用程序(Java swing 或 QT 框架,或 C#)创建基于对话框的应用程序,添加背景,标题等控件;
  2. 蛇能进行移动,并且通过键盘按钮进行方向(上下左右)的调控。
  3. 蛇有界面边界或者无边界的设定,有随机食物可以随机生成,或者墙体阻挡。
  4. 有分数的判断和记录,可以通过空格开始游戏或者暂停,死亡后也可重新开始。
  5. 有游戏的介绍和操作帮助,还有音乐。

1.2项目的主要功能

  1. 贪吃蛇通过键盘移动吃到随机食物导致身体变长,直到撞到边界或者有自己的身体重合导致游戏结束;

2、项目设计

一个类用来放加载的图片:

包括蛇的头部(4),身体,食物,标题

package Msnake;

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

public class Data {

    //头部的图片
    public static URL headerURL = Data.class.getResource("xiao.png");
    public static ImageIcon header = new ImageIcon(headerURL);

    //蛇的头部:
    public static URL upURL = Data.class.getResource("xiaou.png");
    public static URL downURL = Data.class.getResource("xiaod.png");
    public static URL leftURL = Data.class.getResource("xiaol.png");
    public static URL rightURL = Data.class.getResource("xiao.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("fashi.png");
    public static ImageIcon body = new ImageIcon(bodyURL);

    //食物:
    public static URL foodURL = Data.class.getResource("fashi.png");
    public static ImageIcon food = new ImageIcon(foodURL);

    //墙的图片
    public static URL wallURL = Data.class.getResource("qiang.png");
    public static ImageIcon wall = new ImageIcon(wallURL);

}

一个类实现贪吃蛇具体功能

1·初始化蛇的各部位

2·分配背景,积分器,标题的位置

3·使用时钟(timer)让食物随机出现

4·使用监听机制通过键盘控制蛇的行动

5·让蛇通过吃到食物让身体变长,积分增加,速度加快等

6·让蛇可以穿墙或者接触墙壁死亡

7·让游戏启动音乐

8·制作菜单,帮助游戏理解游戏操作

package Msnake;
import javax.sound.sampled.*;
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.io.File;
import java.io.IOException;
import java.util.Random;

public class GamePanel extends JPanel implements KeyListener, ActionListener {

    int length; //蛇的长度
    int[] snakeX = new int[600];    //蛇的坐标X
    int[] snakeY = new int[500];    //蛇的坐标Y
    String fx;  //R:右     	L:左 	U:上 	D:下

    static boolean isStart = false;   //游戏是否开始
    boolean isDie = false;     //判断蛇是否死亡
    Timer timer = new Timer(125, this);//定时器

    //定义食物:
    int foodx;
    int foody;
    Random random = new Random();

    //积分系统
    int score;

    //初始化数据
    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 = 50 + 25 * random.nextInt(32);
        foody = 125 + 75 * random.nextInt(6);

        score = 0;
    }

    //构造器调用初始化数据
    public GamePanel() {
        init();
        //获取键盘的监听事件
        this.setFocusable(true);
        this.addKeyListener(this);
        timer.start();  //让时间动起来
    }

    //画板:画界面,画蛇
    //Graphics:画笔
    public void paint(Graphics g) {
        super.paintComponent(g);    //清屏
        this.setBackground(Color.BLACK);//设置背景的颜色

        //绘制积分框
        Data.header.paintIcon(this, g, 0, 0);

        //绘制游戏区域
        g.fillRect(0, 75, 1000, 625);

        //画一条静态的蛇
        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]);
        }

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

        //画食物
        Data.food.paintIcon(this, g, foodx, foody);

        //画积分
        g.setColor(Color.white); //设置画笔颜色
        g.setFont(new Font("微软雅黑", Font.BOLD, 18));
        g.drawString("长度:" + length, 750, 35);
        g.drawString("分数:" + score, 750, 55);

        //游戏提示:是否开始
        if (isStart == false) {
            //画一段文字
            g.setColor(Color.white); //设置画笔颜色
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("按下空格开始游戏", 300, 300);
        }

        //失败提醒
        if (isDie) {
            //画一段文字
            g.setColor(Color.red); //设置画笔颜色
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("游戏失败,按下空格重新开始", 200, 300);
        }
    }

    //接受键盘的输入:监听
    @Override
    public void keyPressed(KeyEvent e) {
        // 键盘按下,未释放
        //获取按下的键盘是哪个键
        int keyCode = e.getKeyCode();

        if (keyCode == KeyEvent.VK_SPACE) { //如果按下的是空格键
            if (isDie) {    //失败,游戏再来一遍
                isDie = false;
                init(); //重新初始化游戏
            } else {    //暂停游戏
                isStart = !isStart;
            }
            repaint();  //刷新界面
        }

        //键盘控制走向
        if (keyCode == KeyEvent.VK_LEFT) {
            fx = "L";
        } else if (keyCode == KeyEvent.VK_RIGHT) {
            fx = "R";
        } else if (keyCode == KeyEvent.VK_UP) {
            fx = "U";
        } else if (keyCode == KeyEvent.VK_DOWN) {
            fx = "D";
        }
    }

    //定时器:监听时间流动;执行定时操作
    @Override
    public void actionPerformed(ActionEvent e) {
        //如果游戏处于开始状态,并且游戏没有结束
        if (isStart && isDie == false) {
            //右移
            for (int i = length - 1; i > 0; i--) {    //身体移动
                snakeX[i] = snakeX[i - 1];
                snakeY[i] = snakeY[i - 1];
            }

            //通过控制方向让头部移动
            //这里可以控制蛇撞到四周墙壁不死
        /*    if (fx.equals("R")) {
                snakeX[0] = snakeX[0] + 25; //头部移动
                //边界判断
                for (int i = 0; i < snakeX.length; i++) {
                    if (snakeX[i] > 1000) {
                        snakeX[i] = snakeX[0] - 1000;                   //{isDie=true;}
                    }
                }
            } else if (fx.equals("L")) {
                snakeX[0] = snakeX[0] - 25; //头部移动
                //边界判断
                for (int i = 0; i < snakeX.length; i++) {
                    if (snakeX[i] < 0) {
                        snakeX[i] = snakeX[0] + 1000;                   //{isDie=true;}
                    }
                }
                //if(snakeX[0]<25){isDie=true;}
            } else if (fx.equals("U")) {
                snakeY[0] = snakeY[0] - 25; //头部移动
                //边界判断
                for (int i = 0; i < snakeY.length; i++) {
                    if (snakeY[i] < 90) {
                        snakeY[i] = snakeY[0] + 600;                   //{isDie=true;}
                    }
                }
                //if(snakeY[0]<75){isDie=true;}
            } else if (fx.equals("D")) {
                snakeY[0] = snakeY[0] + 25; //头部移动
                //边界判断
                for (int i = 0; i < snakeY.length; i++) {
                    if (snakeY[i] > 600) {
                        snakeY[i] = snakeY[0] - 525;                   //{isDie=true;}
                    }
                }
                //if(snakeY[0]>625){isDie=true;}
            }*/

            if(fx.equals("R")){
                snakeX[0] = snakeX[0] + 25; //头部移动
                // 边界判断
                if(snakeX[0]>925 ) {isDie=true;}
            }else if(fx.equals("L")){
                snakeX[0] = snakeX[0] - 25; //头部移动
                // 边界判断
                if(snakeX[0]<25){isDie=true;}
            }else if(fx.equals("U")){
                snakeY[0] = snakeY[0] - 25; //头部移动
                // 边界判断
                if(snakeY[0]<100){isDie=true;}
            }else if(fx.equals("D")){
                snakeY[0] = snakeY[0] + 25; //头部移动
                // 边界判断
                if(snakeY[0]>625){isDie=true;}
            }

            //如果小蛇的头和食物坐标重合
            if (snakeX[0] == foodx && snakeY[0] == foody) {
                //长度+1
                length++;
                //重新生成食物
                foodx = 25 + 25 * random.nextInt(32);
                foody = 100 + 75 * random.nextInt(6);

                //积分+10
                score += 10;
            }

            //如果小蛇的头和小蛇的身体坐标重合,失败,(把小蛇设置为撞到身体不死)
            for(int i=1;i<length;i++){
                if(snakeX[0]==snakeX[i] && snakeY[0]==snakeY[i]){
                    isDie=true;
                }
            }

            //刷新界面
            repaint();
        }
        timer.start();  //让时间动起来
    }

    @Override
    public void keyReleased(KeyEvent arg0) {
        // 释放某个键		不实现
    }

    @Override
    public void keyTyped(KeyEvent arg0) {
        // 键盘按下,弹起		不实现
    }


}


class GameJMenuBar extends JMenuBar implements ActionListener {


    public void init() {
        //创建菜单对象
        JMenu menu1 = new JMenu("游戏选项");
        JMenu menu2 = new JMenu("帮助");
        //添加菜单到工具栏对象
        this.add(menu1);
        this.add(menu2);
        //创建菜单项对象
        JMenuItem item1 = new JMenuItem("退出");
        //添加菜单项到菜单对象menu1中
        menu1.add(item1);
        //创建菜单项对象
        JMenuItem item3 = new JMenuItem("操作方法");
        //添加菜单项到菜单对象menu1中
        menu2.add(item3);
        //创建菜单项对象
        JMenuItem item2 = new JMenuItem("注意事项");
        //添加菜单项到菜单对象menu1中
        menu2.add(item2);

        item1.addActionListener(this);// 为菜单项添加事件监听器
        item2.addActionListener(this);// 为菜单项添加事件监听器
        item3.addActionListener(this);// 为菜单项添加事件监听器

    }

    public GameJMenuBar() {
        init();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        String cmd = e.getActionCommand();
        if (cmd.equals("退出")) {
            System.exit(0);
        }
        if (cmd.equals("注意事项")) {

            JPanel panel = new JPanel(new GridLayout(0, 1, 5, 5));
            JLabel Label1 = new JLabel("请注意正确使用");
            JLabel Label2 = new JLabel("游戏结束请按空格重新开始");
            JLabel Label3 = new JLabel("暂停游戏也可以按空格");
            panel.add(Label1);
            panel.add(Label2);
            panel.add(Label3);

            GamePanel.isStart = false;
            JOptionPane.showMessageDialog(this, panel, "注意事项", JOptionPane.PLAIN_MESSAGE);
            repaint();  //刷新界面
        }
        if (cmd.equals("操作方法")) {
            JPanel panel = new JPanel(new GridLayout(0, 1, 5, 5));
            JLabel Label1 = new JLabel("按下空格可以暂停/开始游戏");
            //JLabel Label2 = new JLabel("目前死亡的唯一方式:暂停,并退出");
            JLabel Label2 = new JLabel("目前死亡的唯一方式:碰到自己的身体");
            JLabel Label3 = new JLabel("按下方向键可以控制蛇的移动");

            panel.add(Label1);
            panel.add(Label2);
            panel.add(Label3);

            GamePanel.isStart = false;
            JOptionPane.showMessageDialog(this, panel, "操作方法", JOptionPane.PLAIN_MESSAGE);
            repaint();  //刷新界面,repaint()方法用来重新绘制图像,图像发生改变时立即显示,paint()方法用来绘制图像,
        }
    }
}
class Music extends Thread {
    private String fileName;
    private final int EXTERNAL_BUFFER_SIZE = 524288;

    public Music(String wavFile) {
        this.fileName = wavFile;
    }

    @SuppressWarnings("unused")
    public void run() {
        File soundFile = new File(fileName); // 播放音乐的文件名
        if (!soundFile.exists()) {
            System.err.println("Wave file not found:" + fileName);
            return;
        }
        while (true) { // 设置循环播放
            AudioInputStream audioInputStream = null; // 创建音频输入流对象
            try {
                audioInputStream = AudioSystem.getAudioInputStream(soundFile); // 创建音频对象
            } catch (UnsupportedAudioFileException e1) {
                e1.printStackTrace();
                return;
            } catch (IOException e1) {
                e1.printStackTrace();
                return;
            }
            AudioFormat format = audioInputStream.getFormat(); // 音频格式
            SourceDataLine auline = null; // 源数据线
            DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
            try {
                auline = (SourceDataLine) AudioSystem.getLine(info);
                auline.open(format);
            } catch (LineUnavailableException e) {
                e.printStackTrace();
                return;
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
            if (auline.isControlSupported(FloatControl.Type.PAN)) {
                FloatControl pan = (FloatControl) auline.getControl(FloatControl.Type.PAN);
            }
            auline.start();
            int nBytesRead = 0;
            byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];
            try {
                while (nBytesRead != -1) {
                    nBytesRead = audioInputStream.read(abData, 0, abData.length);
                    if (nBytesRead >= 0)
                        auline.write(abData, 0, nBytesRead);
                }
            } catch (IOException e) {
                e.printStackTrace();
                return;
            } finally {
                auline.drain();
                // auline.close();
            }
        }
    }
}

一个类作为主类实现界面构造

1·标题,游戏界面位置和大小

2·启动游戏

package Msnake;

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

public class StartGame {
    static final int WIDTH = 1000;
    static final int HEIGHT = 750;

    public static void main(String args[]) {
        //1、绘制一个静态窗口 JFrame
        JFrame jf = new JFrame();
        jf.setTitle("魈宝逮深渊法师");
        //设置界面大小
        jf.setSize(WIDTH, HEIGHT);
        //窗口大小不可以改变
        jf.setResizable(false);
        Toolkit kit = Toolkit.getDefaultToolkit();
        Dimension screenSize = kit.getScreenSize();
        //获取屏幕的宽度和高度
        int width = screenSize.width;
        int height = screenSize.height;
        int x = (width - WIDTH) / 2;
        int y = (height - HEIGHT) / 2;

        //背景音乐启动
//        Music audioPlayWave = new Music("");// 开音乐 音樂名
        Music audioPlayWave = new Music("");// 开音乐 音樂名
        audioPlayWave.start();
        @SuppressWarnings("unused")
        int musicOpenLab = 1;


        jf.setLocation(x, y);
        //设置关闭事件
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //2、面板 JPanel
        jf.add(new GamePanel());

        //3、菜单栏 JMenuBar
        jf.setJMenuBar(new GameJMenuBar());

        jf.setVisible(true);

    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落不心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值