实验二 贪吃蛇的游戏开发

实验目的:

实现贪吃蛇游戏基本功能,屏幕上随机出现一个“食物”,称为豆子,上下左右控制“蛇”的移动,吃到“豆子”以后“蛇”的身体加长一点,得分增加,“蛇”碰到边界或,蛇头与蛇身相撞,蛇死亡,游戏结束。为游戏设计初始欢迎界面,游戏界面,游戏结束界面。

功能和设计要求:

(1)游戏界面设计:界面构造,蛇的构造,食物构造。其中蛇与食物的构造不一定就是“蛇”和“食物,可以有自己的创意。我更愿意把它们设置成我喜欢的样子,而不失其合理性。

(2)游戏过程:蛇的移动,蛇吃食物,碰撞检测。这就是大多数贪吃蛇游戏的普遍的玩法了。

(3)游戏结束:结束界面。结束界面最好是设计成与游戏本身有关的内容。

(4)扩展功能:功能食物,移动速度,得分记录。

可能需要的方法和GUI功能:

public void addKeyListener(KeyListener 1):添加指定的按键侦听器,以接收发自此组件的按键事件。如果 l 为 null,则不会抛出异常并且不执行动作。因为贪吃蛇游戏肯定是要用键盘的上下左右键去操作的。

Random 类中的 nextlnt()方法:生成一个随机的 int 值,该值介于 int 的区间,也就是 -2^31~2^31-1。如果需要生成指定区间的 int 值,则需要进行一定的数学变换。可用于随机生成食物的位置。

Timer(定时调用、实现固定时间执行):可用于对速度的调整。

各种绘图的方法也是必须的,不管是界面设计还是蛇头、蛇身、食物都需要图像,所以绘制方法在这里是很重要的。在下面的代码中会提及。

游戏的大体设计:

首先,我们要对游戏有一个整体的概念设计。因为贪吃蛇的玩法是固定的,但是游戏性不是固定的,如果想要提高游戏的趣味性,就要有比其他同类游戏创新的地方。比如,“蛇身”、“蛇头”的设计,游戏背景的选择,以及得分规则等等。在这些方面是可以有所创新的,大家可以选择自己喜欢的、好玩的角色或者事物、梗图等进行设计。在这里我选择了我非常喜欢虚拟歌姬星尘作为游戏的主角。

其次,我们要确定游戏界面设计。贪吃蛇游戏可以在一个窗口上就完成的,但是我想要先介绍游戏的人物,或者我的设计灵感来源,然后在说明得分标准,最才开始真正的游戏,所以我想使用三个窗口,每个窗口之间用一个按钮进行跳转。

根据上述的要求,我形成了大体的思路,大致需要四个类,一个用于实现主方法的运行,并且完成三个窗口的定义,和第一个窗口的设计。还有两个类用于实现第二个和第三个窗口的设计。最后一个类用于获取和调用图片,其实可以不用这个类也能获取图片,但是在反复绘制图片的时候会非常麻烦,所以最好通过调用去实现。

编程:

主方法类:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.net.URL;

public class 贪吃蛇 extends JFrame {
    public static JFrame frame1;
    public static JFrame frame2;
    public static JFrame frame3;
    public static void main(String[] args) {
        frame1 = new JFrame("星尘记");
        frame1.setResizable(true);
        frame1.setBounds(450, 10, 900, 900);
        frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame2=new JFrame("星尘记");
        frame2.setResizable(true);
        frame2.setBounds(450,10,940,900);
        frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame3 = new JFrame("星尘记");
        frame3.setResizable(true);
        frame3.setBounds(450, 10, 900, 900);
        frame3.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel jPanel1 =new JPanel();
        ImageIcon bgim1 = new ImageIcon(Data.class.getResource("/static/『平行四界Quadimension X-1』Cover(1).jpg")) ;//背景图案
        bgim1.setImage(bgim1.getImage().getScaledInstance(bgim1.getIconWidth(), bgim1.getIconHeight(), Image.SCALE_DEFAULT));
        JButton jButton1= new JButton("开始游戏");
        JLabel label1 = new JLabel();
        JLabel label2 = new JLabel("该游戏是以VOCALOID中文声库虚拟人物星尘为原型的贪吃蛇类小游戏");
        label1.setIcon(bgim1);//文件路径
        label1.add(label2);
        label2.setFont(new Font("微软雅黑", Font.BOLD + Font.ITALIC, 20));
        label2.setBounds(100,600,700,100);
        label1.add(jButton1);
        jButton1.setBounds(400,800,100,40);
        jPanel1.add(label1);
        frame1.add(jPanel1);
        jButton1.addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent e) {
                frame1.dispose();
                frame3.add(new Panel2());
                frame3.setVisible(true);
            }

            @Override
            public void mousePressed(MouseEvent e) {

            }

            @Override
            public void mouseReleased(MouseEvent e) {

            }

            @Override
            public void mouseEntered(MouseEvent e) {

            }

            @Override
            public void mouseExited(MouseEvent e) {

            }
        });
        frame1.setVisible(true);//数据模型已经构造好了,允许JVM可以根据数据模型执行paint方法开始画图并显示到屏幕上了,并不是显示图形,而是可以运行开始画图了

    }

}

Data类:(用于获取图片的类方法,在后面会有大用场)

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

public class Data {
    //获取图片资源
    public static URL bodyURL = Data.class.getResource("/static/梅塔特隆.png");
    public static ImageIcon body = new ImageIcon(bodyURL);
    public static ImageIcon MetatroCube = new ImageIcon(Data.class.getResource("/static/梅塔特隆.png"));
    public static ImageIcon background1 = new ImageIcon(Data.class.getResource("/static/『平行四界Quadimension X-1』Cover(1).jpg"));
    public static ImageIcon background2 = new ImageIcon(Data.class.getResource("/static/『平行四界Quadimension X-1』Cover(2).jpg"));
    public static ImageIcon up = new ImageIcon(Data.class.getResource("/static/星尘右.png"));
    public static ImageIcon down = new ImageIcon(Data.class.getResource("/static/星尘右.png"));
    public static ImageIcon left = new ImageIcon(Data.class.getResource("/static/星尘左.png"));
    public static ImageIcon right = new ImageIcon(Data.class.getResource("/static/星尘右.png"));
    public static ImageIcon haiyi = new ImageIcon(Data.class.getResource("/static/海伊(1).png"));

    public static ImageIcon getbackground1() {
        return background1;
    }

    public static void setbackground1(ImageIcon background) {
        Data.background1 = background;
    }

    public static ImageIcon getbackground2() {
        return background2;
    }

    public static void setbackground2(ImageIcon background) {
        Data.background2 = background;
    }

    public static ImageIcon getBody() {
        return body;
    }

    public static void setBody(ImageIcon body) {
        Data.body = body;
    }

    public static ImageIcon getUp() {
        return up;
    }

    public static void setUp(ImageIcon up) {
        Data.up = up;
    }

    public static ImageIcon getDown() {
        return down;
    }

    public static void setDown(ImageIcon down) {
        Data.down = down;
    }

    public static ImageIcon getLeft() {
        return left;
    }

    public static void setLeft(ImageIcon left) {
        Data.left = left;
    }

    public static ImageIcon getRight() {
        return right;
    }

    public static void setRight(ImageIcon right) {
        Data.right = right;
    }

    public static ImageIcon getMetatroCube() {
        return MetatroCube;
    }

    public static void setMetatroCube(ImageIcon food) {
        Data.MetatroCube = food;
    }
    
    public static ImageIcon getHaiyi() {
        return haiyi;
    }

    public static void setHaiyi(ImageIcon food) {
        Data.haiyi = food;
    }

}

因为图片不好找,上面的“up”,“down”,“right”用得都是同一张图,只要定义一个就可以了,但为了更好理解,我把每个都定义了一遍。

Panel2类:为窗口3设计面板(窗口3是frame3,是第二个窗口,不要弄混了,至于为什么命名为frame3,是因为之前还没想好要做第二个界面)

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

public class Panel2 extends JPanel implements KeyListener {
    public static int Keycode;
    public static JButton jButton1 = new JButton("继续");
    public Panel2(){
        jButton1.setBounds(400,800,100,40);
        jButton1.addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked (MouseEvent e){
                贪吃蛇.frame3.dispose();
                贪吃蛇.frame2.add(new GamePanel());
                贪吃蛇.frame2.setVisible(true);
            }

            @Override
            public void mousePressed (MouseEvent e){

            }

            @Override
            public void mouseReleased (MouseEvent e){

            }

            @Override
            public void mouseEntered (MouseEvent e){

            }

            @Override
            public void mouseExited (MouseEvent e){

            }
        });
        贪吃蛇.frame3.add(jButton1);
    }

    @Override
    protected void paintComponent(Graphics Picture) {
        Data.getbackground1().paintIcon(this, Picture, 0, 0);
        Data.getRight().paintIcon(this, Picture, 100, 200);
        Data.getHaiyi().paintIcon(this, Picture, 100, 350);
        Data.getMetatroCube().paintIcon(this, Picture, 100, 475);
        Picture.setColor(Color.CYAN);
        Picture.setFont(new Font("微软雅黑", Font.BOLD + Font.ITALIC, 30));
        Picture.drawString("星尘:本作主角,五维介质成员之一", 150, 225);
        Picture.drawString("海伊:星尘的妹妹,五维介质成员之一,获取可得5分", 150, 380);
        Picture.drawString("梅塔特隆立方体:星尘代表物,收集可得1分", 150, 500);
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {
        Keycode = e.getKeyCode();
        if (e.getKeyCode() == KeyEvent.VK_SPACE || e.getKeyCode() == KeyEvent.VK_R) {
            贪吃蛇.frame3.dispose();
            贪吃蛇.frame2.add(new GamePanel());
            贪吃蛇.frame2.setVisible(true);
        } else Keycode = 0;
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
}

GamePanel类:游戏面板

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

public class GamePanel extends JPanel implements KeyListener, ActionListener {

    int length;  // 蛇的长度
    int[] stardustX = new int[600];//角色的x轴坐标位置
    int[] stardustY = new int[500];//角色的y轴坐标位置
    String forward;// 设置角色的方向
    boolean isStart = false;
    int MetatroCubeX;  //梅塔特隆(就是食物)的x轴坐标位置
    int MetatroCubeY;  //梅塔特隆的y轴坐标位置
    int haiyiX=-100;   //海伊的x轴坐标位置,设置成-100,是因为这是特殊的奖励方式,一开始是没有的,每次只有一定概率才能出现,所以我干脆一开始把她放到屏幕外边了
    int haiyiY=-100;   //海伊的y轴坐标位置
    boolean isFalse = false;
    int score;
    Random random = new Random();   //随机函数
    Timer timer = new Timer(200, this);  
    public static JButton jButton1;    
    //方格的宽度和长度,因为我把图像都设置成这样的大小了,所以画的方格也要有这样的大小,是看个人喜好的,不一定都这样设置
    public static final int BLOCK_WIDTH = 40; 
    public static final int BLOCK_HEIGHT = 41;
    //界面的方格的行数和列数,只要比边界大就行
    public static final int ROW = 50;
    public static final int COL = 50;

    public GamePanel() {
        inti(); //初始化
        this.setFocusable(true);
        this.addKeyListener(this);
        timer.start();
        this.setSize(ROW * BLOCK_HEIGHT, COL * BLOCK_WIDTH);
        this.setLocation(840, 650);
    }

    public void inti() {
        length = 1;
        stardustX[0] = 80 + 40 * random.nextInt(16);  //一开始角色的位置在少靠近中心的位置随机出现,所以用到了随机函数,而且不能太靠近边界,不然玩家很容易来及反应
        stardustY[0] = 130 + 40 * random.nextInt(14);
        int x = random.nextInt(4); //随机数x表示了角色一开始朝的方向
        if (x == 0) {
            forward = "right";
        } else if (x == 1) {
            forward = "up";
        } else if (x == 2) {
            forward = "down";
        } else if (x == 3) {
            forward = "left";
        }
        MetatroCubeX = 0 + 40 * random.nextInt(21);  //梅塔特隆的位置可以在边界
        MetatroCubeY = 50 + 40 * random.nextInt(17);
        // 让生成的梅塔特隆不会和“蛇身”重合
        for (int i = 0; i < length; i++) {
            if (MetatroCubeX == stardustX[i] && MetatroCubeY == stardustY[i]) {
                MetatroCubeX = 0 + 40 * random.nextInt(20);
                MetatroCubeY = 50 + 40 * random.nextInt(16);
                i = 0;
            }
        }
        score = 0;
    }

    @Override
    protected void paintComponent(Graphics Picture) {
        super.paintComponent(Picture);  //清屏,这句话挺重要的,没这句话分数和长度的字会重合变得很模糊,游戏失败后角色会有卡在边界的情况
        this.setBackground(Color.WHITE);
        Picture.setColor(Color.black);
        Data.getbackground2().paintIcon(this, Picture, 0, 50); //放背景图片
        Picture.setColor(Color.white);
        
        for (int i = 0; i < ROW; i++) {
            Picture.drawLine(0, i * BLOCK_HEIGHT, COL * BLOCK_WIDTH, i * BLOCK_HEIGHT);//将界面画成由ROW*COL个方格
        }
        for (int i = 0; i < COL; i++) {
            Picture.drawLine(i * BLOCK_WIDTH, 50, i * BLOCK_WIDTH, ROW * BLOCK_HEIGHT);
        }

        Picture.setColor(Color.CYAN);
        Picture.setFont(new Font("微软雅黑", Font.BOLD + Font.ITALIC, 15));
        Picture.drawString("长度" + length, 750, 20);
        Picture.drawString("得分" + score, 750, 45);
        Data.getMetatroCube().paintIcon(this, Picture, MetatroCubeX, MetatroCubeY); //画梅塔特隆
        Data.getHaiyi().paintIcon(this,Picture,haiyiX,haiyiY); //画海伊
        // 放头部
        if (forward.equals("up")) {
            Data.up.paintIcon(this, Picture, stardustX[0], stardustY[0]);
        } else if (forward.equals("down")) {
            Data.down.paintIcon(this, Picture, stardustX[0], stardustY[0]);
        } else if (forward.equals("left")) {
            Data.left.paintIcon(this, Picture, stardustX[0], stardustY[0]);
        } else if (forward.equals("right")) {
            Data.right.paintIcon(this, Picture, stardustX[0], stardustY[0]);
        }
        for (int i = 1; i < length; i++) {
            Data.getBody().paintIcon(this, Picture, stardustX[i], stardustY[i]);
        }
        if (isStart == false) {
            Picture.setColor(Color.white);
            Picture.setFont(new Font("微软雅黑", Font.BOLD + Font.ITALIC, 30));
            Picture.drawString("按下空格开始游戏", 300, 300);
        }
        if (isFalse == true) {
            Picture.setColor(Color.RED);
            Picture.setFont(new Font("微软雅黑", Font.BOLD + Font.ITALIC, 30));
            Picture.drawString("游戏失败,按空格键重新开始", 300, 300);
        }
    }

    @Override
    public void keyPressed(KeyEvent X) {//键盘监听
        int keyCode = X.getKeyCode();
        if (keyCode == KeyEvent.VK_SPACE || keyCode == KeyEvent.VK_R) {
            if (isFalse) {
                isFalse = false;//重新开始
                inti();
            } else {
                isStart = !isStart;
            }
            repaint();//重新绘制
        }
        // 让用户用键盘的上下左右键来操作蛇头,
        // 并且让蛇不能直接回头,当蛇在往上走,那么就不能立刻往下走
        if (keyCode == KeyEvent.VK_UP) {
            forward = forward.equals("down") ? "down" : "up";
        } else if (keyCode == KeyEvent.VK_DOWN) {
            forward = forward.equals("up") ? "up" : "down";
        } else if (keyCode == KeyEvent.VK_LEFT) {
            forward = forward.equals("right") ? "right" : "left";
        } else if (keyCode == KeyEvent.VK_RIGHT) {
            forward = forward.equals("left") ? "left" : "right";
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyReleased(KeyEvent e) {

    }


    @Override
    public void actionPerformed(ActionEvent e) {//事件监听
        int x = random.nextInt(10);  //因为海伊的出现是有一定概率的,所以设了一个0到9的随机数,每次获得一个梅塔特隆,就i进行判断
        if (isStart && !isFalse) {
            if (stardustX[0] == MetatroCubeX && stardustY[0] == MetatroCubeY) {
                length++;
                score = score + 1;
                if (length == 15) {
                    Timer timer1 = new Timer(150, this);
                } else if (length == 30) {
                    Timer timer2 = new Timer(120, this);
                } else if (length == 45) {
                    Timer timer3 = new Timer(90, this);
                } else if (length == 60) {
                    Timer timer4 = new Timer(70, this);
                }
                MetatroCubeX = 40 + 40 * random.nextInt(20); //重新生成梅塔特隆立方体
                MetatroCubeY = 90 + 40 * random.nextInt(16);
                for (int i = 0; i < length; i++) {
                    if (MetatroCubeX == stardustX[i] && MetatroCubeY == stardustY[i]) {
                        MetatroCubeX = 40 + 40 * random.nextInt(20);
                        MetatroCubeY = 90 + 40 * random.nextInt(16);
                        i = 0;
                    }
                }if (x>7&&haiyiX==-100&&haiyiY==-100){  //随机数大于7,才生成海伊
                    haiyiX = 40 + 40 * random.nextInt(20);
                    haiyiY = 90 + 40 * random.nextInt(16);
                    for (int i = 0; i < length; i++) {
                        if (haiyiX == stardustX[i] && haiyiY == stardustY[i]) {
                            haiyiX = 40 + 40 * random.nextInt(20);
                            haiyiY = 90 + 40 * random.nextInt(16);
                            i = 0;
                        }
                    }
                }
            }if (stardustX[0] == haiyiX && stardustY[0] == haiyiY){
                length++;
                score = score + 5;
                if (length == 15) {
                    Timer timer1 = new Timer(150, this);
                } else if (length == 30) {
                    Timer timer2 = new Timer(120, this);
                } else if (length == 45) {
                    Timer timer3 = new Timer(90, this);
                } else if (length == 60) {
                    Timer timer4 = new Timer(70, this);
                }
                haiyiX=-100;   //获取一次海伊后,要把她放到屏幕外
                haiyiY=-100;
            }
            
            if (forward.equals("up")) {
                for (int i = length - 1; i > 0; i--) {
                    stardustX[i] = stardustX[i - 1];
                    stardustY[i] = stardustY[i - 1];
                }
                stardustY[0] = stardustY[0] - 40;
                //碰到边界,游戏结束
                if (stardustY[0] < 50) {
                    isFalse = true;
                }
            } else if (forward == "down") {
                //身体下移
                for (int i = length - 1; i > 0; i--) {
                    stardustX[i] = stardustX[i - 1];
                    stardustY[i] = stardustY[i - 1];
                }
                //头部下移
                stardustY[0] = stardustY[0] + 40;

                //碰到边界,游戏结束
                if (stardustY[0] > 800) {
                    isFalse = true;
                }
            } else if (forward == "left") {
                //身体左移
                for (int i = length - 1; i > 0; i--) {
                    stardustX[i] = stardustX[i - 1];
                    stardustY[i] = stardustY[i - 1];

                }
                //头部左移
                stardustX[0] = stardustX[0] - 40;

                //碰到边界,游戏结束
                if (stardustX[0] < 0) {
                    isFalse = true;
                }
            } else if (forward == "right") {
                //身体右移
                for (int i = length - 1; i > 0; i--) {
                    stardustX[i] = stardustX[i - 1];
                    stardustY[i] = stardustY[i - 1];

                }
                //头部右移
                stardustX[0] = stardustX[0] + 40;

                //碰到边界,游戏结束
                if (stardustX[0] > 800) {
                    isFalse = true;
                }
            }


            //判断是否撞到身体
            for (int i = 2; i < length; i++) {
                if (stardustX[0] == stardustX[i] && stardustY[0] == stardustY[i]) {
                    isFalse = true;
                }
            }
            //重画
            repaint();
        }
        timer.start();
    }

}

以上就是我的贪吃蛇游戏的全部代码了,重点是掌握GUI的各种方法,能够理解各种方法的用法,掌握这些方法可以美化窗口,增加游戏的趣味性。

接下来,给大家展示一下成品。

主面板:

 第二面板:介绍基本规则

 游戏面板:

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值