实验2:Java贪吃蛇

源码:klskk23/Snake: 贪吃蛇小游戏基于Java swing. (github.com)

实验内容

1)实现贪吃蛇游戏基本功能,屏幕上随机出现一个“食物”,称为豆子。玩家能利用上下左右键控制的移动,“蛇”吃到“豆子”后“蛇”身体加长一节,得分增加,“蛇”碰到边界或蛇头与蛇身相撞,“蛇”死亡,游戏结束。

2)进行交互界面的设计,要有开始键、暂停键和停止退出的选项,能够控制游戏进程。对蛇吃到豆子进行分值计算,可以设置游戏速度,游戏音乐等拓展元素。

实现效果

点击暂停即可暂停游戏,点击开始即可继续游戏,点击背景音乐按钮即可播放背景音乐,点击得分弹出得分面板。

实现思路

使用swing和绘图类实现

实现过程

1.首先需要创建一个名为GameFrame,用于放置GamePanel和按键。

为按键设置侦听器,再使用KeyListener设置按键侦听。

public class GameFrame extends JFrame {
    GamePanel gamePanel;
    Score score;
    //public Timer timer0;
    boolean stop0=true;
    private boolean bgm_control_jud = true;//防止打开多个bgm;
    public static Font font1 = new Font("宋体", Font.BOLD, 20);//定义字体1

    public GameFrame() throws HeadlessException {
        Container container = getContentPane();

        //设置窗体属性
        this.setTitle("贪吃蛇 BY 废旧螺栓机甲");
        this.setSize(640, 690);
        this.setLocation(100, 100);
        this.setResizable(false);
        this.setVisible(true);
        this.setFocusable(true);
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //设置键盘监听
        this.keyEvent();
        //开始按钮,重置游戏
        JButton game_start = new JButton();
        game_start.setText("开始");
        game_start.setForeground(Color.red);
        game_start.setFont(font1);
        game_start.setBounds(0, 0, 150, 30);
        game_start.setBackground(new Color(135, 206, 250));
        game_start.setFocusable(false);
        container.add(game_start);

        //BGM操控
        JButton bgm_control = new JButton();
        bgm_control.setText("背景音乐");
        bgm_control.setForeground(Color.yellow);
        bgm_control.setFont(font1);
        bgm_control.setBounds(300, 0, 150, 30);
        bgm_control.setBackground(new Color(100, 149, 237));
        bgm_control.setFocusable(false);
        container.add(bgm_control);
        bgm_control.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Sound.Btn_press();
                if (bgm_control_jud) {
                    bgm_control_jud = false;
                    Sound.BGM();
                }
            }
        });
        //暂停按钮
        JButton stop_game = new JButton();
        stop_game.setText("暂停/继续");
        stop_game.setBounds(150, 0, 150, 30);
        stop_game.setForeground(Color.orange);
        stop_game.setFont(font1);
        stop_game.setBackground(new Color(147, 112, 219));
        stop_game.setFocusable(false);
        container.add(stop_game);

        //计分板按钮
        JButton score_get = new JButton();
        score_get.setText("得分");//等待补充分数
        score_get.setBounds(450, 0, 180, 30);
        score_get.setForeground(Color.MAGENTA);
        score_get.setFont(font1);
        score_get.setBackground(new Color(238, 232, 170));
        container.add(score_get);
        score_get.setFocusable(false);

        //JPanel对象
        gamePanel = new GamePanel(null);
        container.add(gamePanel);
        game_start.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Sound.Btn_press();
                if(!stop0){
                    stop0=true;gamePanel.stop=false;
                }
            }
        });
        stop_game.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Sound.Btn_press();
                if(stop0){stop0=false;gamePanel.stop=true;}
            }
        });
        score_get.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Sound.Btn_press();
                GameFrame.this.end();
            }
        });


    }
//键盘监听
    private void keyEvent() {
        addKeyListener(new KeyListener() {
            @Override
            public void keyTyped(KeyEvent e) {

            }

            @Override
            public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
    case KeyEvent.VK_UP:
        if(gamePanel.snake.getDirection()!=Direction.DOWN)gamePanel.snake.setDirection(Direction.UP);
        break;
    case KeyEvent.VK_DOWN:
        if(gamePanel.snake.getDirection()!=Direction.UP)gamePanel.snake.setDirection(Direction.DOWN);
        break;
    case KeyEvent.VK_LEFT:
        if(gamePanel.snake.getDirection()!=Direction.RIGHT)gamePanel.snake.setDirection(Direction.LEFT);
        break;
    case KeyEvent.VK_RIGHT:
        if(gamePanel.snake.getDirection()!=Direction.LEFT)gamePanel.snake.setDirection(Direction.RIGHT);
        break;
}
            }

            @Override
            public void keyReleased(KeyEvent e) {

            }
        });

    }
    //结算
    public  int end(){
        score=new Score(GameFrame.this);
        score.setVisible(true);
        return score.getScore();
    }

}

2.蛇由很多节点和蛇头的方向组成,所以创建三个类Node,Direction,Snake来表示蛇。

定义变量isLive来判断蛇头是否和自身重合,判断输入方向(比如蛇头向上不得朝下移动)

public enum Direction {
    UP,DOWN,LEFT,RIGHT
}
public class Node {
private int x;
private int y;

    public Node() {
    }

    public Node(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}
public class Snake {
    //body
    public LinkedList<Node>body;
    private Direction direction= Direction.LEFT;
    //die or live
    public  boolean isLive=true;

    public Snake() {
        initSnake();
    }
    public void initSnake(){
        body=new LinkedList<>();
        body.add(new Node(10,10));
        body.add(new Node(11,10));
        body.add(new Node(12,10));
        body.add(new Node(13,10));
    }
    public void move(){

        //蛇头
        Node head=body.getFirst();
        if(isLive){

            switch (direction){
            case UP:{
                body.addFirst(new Node(head.getX(),head.getY()-1));
                break;}
            case DOWN:{
                body.addFirst(new Node(head.getX(),head.getY()+1));
                break;}
            case RIGHT:{
                body.addFirst(new Node(head.getX()+1,head.getY()));
                break;}
            case LEFT:{
                body.addFirst(new Node(head.getX()-1,head.getY()));
                break;}
        }
            body.removeLast();}
        //live or die
        head=body.getFirst();
        if(head.getX()<0||head.getY()<0||head.getX()>30||head.getY()>30)isLive=false;
        for(int i=1;i<body.size();i++){
            Node node=body.get(i);
            if(head.getX()== node.getX()&&head.getY()==node.getY())isLive=false;
        }

    }

    public LinkedList<Node> getBody() {
        return body;
    }

    public void setBody(LinkedList<Node> body) {
        this.body = body;
    }

    public Direction getDirection() {
        return direction;
    }

    public void setDirection(Direction direction) {
        this.direction = direction;
    }
//吃食物
    public void eat(Food food) {
        Node head=body.getFirst();
        switch (direction){
            case UP:{
                body.addFirst(new Node(head.getX(),head.getY()-1));
                break;}
            case DOWN:{
                body.addFirst(new Node(head.getX(),head.getY()+1));
                break;}
            case RIGHT:{
                body.addFirst(new Node(head.getX()+1,head.getY()));
                break;}
            case LEFT:{
                body.addFirst(new Node(head.getX()-1,head.getY()));
                break;}
        }
    }
}

3.创建Food类,用于随机生成蛇的食物

public class Food {
    private int x;
    private int y;
    public Food(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public Food(){
        Random r=new Random();
        //生成横坐标
        this.x=r.nextInt(30);
        this.y=r.nextInt(30);
    }
    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

}

创建游戏窗体GamePanel,设置计时器,在屏幕上不断重绘,显示分数等。


public class GamePanel extends JPanel{
    public static int u_score;
    public boolean game_over=false;
    public boolean stop=true;
    public Timer timer;
    public Snake snake;
    private boolean repeat=false;//判断食物是否和蛇身体重合,默认不重合
    Image img;
    Food food;
    public Score score;
    public GamePanel(LayoutManager layout) {
        super(layout);
            score = new Score();
            food = new Food(18, 18);
            snake = new Snake();
            this.setBounds(0, 30, 640, 640);
            //添加背景图像
            img = new ImageIcon("src/data/background.png").getImage();
                stop=false;
            timer = new Timer();
            TimerTask timerTask = new TimerTask() {

                @Override
                public void run() {
                    if (!stop) {
                        snake.move();
                        if (!snake.isLive && !game_over) {
                            Sound.Game_over();
                            game_over = true;
                        }//结束音效
                        if (!repeat) {
                            //吃食物判断和食物是否和蛇身体重合判断
                            Node food0 = new Node(food.getX(), food.getY());
                            Node head = snake.getBody().getFirst();
                            if (head.getX() == food0.getX() && head.getY() == food0.getY()) {
                                snake.eat(food);
                                food = new Food();
                                Sound.Eat_food();
                                score.getScore();
                                u_score = score.getN();
                            }
                        } else {
                            food = new Food();

                        }
                        for (int i = 1; i < snake.body.size(); i++) {
                            Node node = snake.body.get(i);
                            if (food.getX() == node.getX() && food.getY() == node.getY()) repeat = true;
                            else repeat = false;
                        }
                        repaint();
                    }
                }
            };
            timer.scheduleAtFixedRate(timerTask, 0, 200);
        }


    @Override
    public void paint(Graphics g) {
        //背景图绘制
        Graphics2D g2=(Graphics2D) g;
        g2.drawImage(img,0,0,this.getWidth(),this.getHeight(),this);
        //网格线
        g.setColor(Color.BLACK);
        for (int i = 0; i <31; i++) {
            g.drawLine(0, 20 * i, 600, 20 * i);
            g.drawLine(20 * i, 0, 20 * i, 600);
        }
        //绘制snake
        LinkedList<Node>body=snake.getBody();
        g.setColor(Color.orange);
        for(Node node:body){
            g.fillRoundRect(node.getX()*20, node.getY()*20,20,20,50,50);
        }
        //绘制food
        g.setColor(Color.GREEN);
        g.fillRoundRect(food.getX()*20,food.getY()*20,20,20,50,50);
        //绘制分数
        g2.setColor(Color.pink);

        Font font0=new Font("宋体",Font.BOLD,20);
        g2.setFont(font0);
        g2.drawString(String.valueOf(u_score),580,20);
        Font font1=new Font("宋体",Font.BOLD,40);
        g2.setFont(font1);
        if(game_over){
            g2.drawString("游戏结束,请退出",160,300);
            g2.setFont(font0);
            g2.drawString("最终得分"+String.valueOf(u_score),160,360);
        }
    }
}

4.创建音频播放器MusicPlayer类用于播放音效,创建Sound类用于各种音效的控制。

public class MusicPlayer implements Runnable{
    File soundFile;//音乐文件
    Thread thread;
    boolean cir;//循环
    public MusicPlayer(String filepath,boolean cir){
        this.cir=cir;
        soundFile=new File(filepath);//文件地址
    }
    public void run(){
        byte[] auBuffer=new byte[1024*4096];//创建缓冲区
        do{
            AudioInputStream audioInputStream=null;
            SourceDataLine auline=null;
            try{
                //从文件中获取音频输入流
                audioInputStream= AudioSystem.getAudioInputStream(soundFile);
                AudioFormat format=audioInputStream.getFormat();//获得音频格式
                DataLine.Info info=new DataLine.Info(SourceDataLine.class,format);
                auline=(SourceDataLine) AudioSystem.getLine(info);
                auline.open(format);
                auline.start();
                int byteCount=0;
                while(byteCount!=-1){
                    byteCount=audioInputStream.read(auBuffer,0,auBuffer.length);
                    if(byteCount>=0){
                        auline.write(auBuffer,0,byteCount);
                    }
                }
            }
            catch (IOException e){e.printStackTrace();}
            catch (UnsupportedAudioFileException e){e.printStackTrace();}
            catch (LineUnavailableException e){e.printStackTrace();}
            finally {
                auline.drain();
                auline.close();
            }
        }while (cir);
    }
    public void play(){
        thread=new Thread(this);
        thread.start();//开始线程
    }
    public  void stop(){
        thread.stop();//强制关闭
    }
}
public class Sound {
    //各种音效
    static final String bgm = "音乐图片资源/bgm.wav";
    static final String eat_beans = "音乐图片资源/eat_beans.wav";
    static final String game_over = "音乐图片资源/game_over.wav";
    static final String press_button="音乐图片资源/btn_press.wav";

    private static void play(String filepath, boolean cir) {//描述如何播放音乐,包括位置和是否循环
        MusicPlayer player = new MusicPlayer(filepath, cir);//创建播放器
        player.play();//start to play music
    }
    static public void Btn_press(){play(press_button,false);}
    static public void Game_over(){play(game_over,false);}
    static public void Eat_food(){play(eat_beans,false);}
    static public void BGM(){//播放bgm
        play( bgm, false);
    }
}

5.创建继承于JDialog的类Score用于返回分数信息

public class Score extends JDialog {
    public int n=0;
    public Score(GameFrame owner) {
        super(owner,"分数",false);
        Container container=getContentPane();
        this.setLayout(null);
        this.setFocusable(false);
        this.setBounds(300,100,200,200);
        JLabel score=new JLabel();
        score.setText("你的得分是"+GamePanel.u_score);
        score.setFont(GameFrame.font1);
        score.setBounds(0,0,200,100);
        container.add(score);
    }

    public Score() {
    }
    public int getScore(){
        n=n+10;
        return n;
}
    public int getN() {
        return n;
    }
}

6.最后创建一个Start类,运行程序

public class Start {
    public static void main(String[]args){
        new GameFrame();
    }
}

项目结构预览:

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值