JAVA原生SWIING编写贪吃蛇

该博客主要介绍贪吃蛇游戏的代码逻辑与实现。初始化界面由MainScene类完成,画面绘制放在Snack类。用ArrayList存放结点信息控制蛇移动,可用LinkedList优化。实现了移动、果实拾取、碰撞、暂停重开及分数显示等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、代码逻辑

1、初始化界面:MainScene类

2、画面绘制:因为主要是绘制蛇,所以放在Snack类中

3、蛇控制:使用ArrayList存放结点位置信息,通过增删控制蛇的移动,应该可以用LinkedList优化。

4、实现功能:

  • 移动:穿越边界时从对面边界出现
  • 果实拾取,蛇身加长
  • 碰撞
  • 游戏暂停、结束后重开
  • 分数显示

二、代码

package com.SnackGame;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class MainScene {

    private Container contentPane;

    public static void main(String[] args) {
        PublicData.newApple();
        new MainScene().init();
    }

    //初始化操作
    private void init(){
        //创建窗口
        JFrame frame = new JFrame();
        frame.setBounds(100,100,PublicData.width,PublicData.height);
        frame.setResizable(false);
        this.contentPane = frame.getContentPane();
        this.contentPane.setBackground(new Color(42, 42, 42));

        //创建蛇
        Snack snack = new Snack();
        snack.init();
        frame.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                snack.pressKey(e.getKeyCode());
            }
        });
        frame.add(snack);

        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //每50ms根据状态刷新一次游戏,游戏结束时终止线程
        new Thread(() -> {
            while(PublicData.GameState != GameConst.GAME_OVER){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(PublicData.GameState == GameConst.GAME_RUN){
                    snack.move();
                }
                else{
                    snack.repaint();
                }
            }
            snack.repaint();
        }).start();
    }

}
package com.SnackGame;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.ArrayList;

public class Snack extends JLabel {
    //初始化信息
    private int dirt = GameConst.DIRT_RIGHT;
    private int snackState = GameConst.SNACK_NORMAL;
    private int bodyLength = 3;
    private int nodeLength = 10;
    private ArrayList<Point> bodyPositions = new ArrayList<>();
    void init(){
        for (int i = 0; i < 3; i++) {
            bodyPositions.add(new Point(PublicData.width / 3 - i * nodeLength,90));
        }
        repaint();
    }

    //移动逻辑
    public void move(){
        addHead();
        if(snackState == GameConst.SNACK_EAT){
            bodyLength++;
            snackState = GameConst.SNACK_NORMAL;
            PublicData.newApple();
        }
        else{
            bodyPositions.remove(bodyLength);
        }
        repaint();
    }

    //由当前方向判定新头位置,并判断新头是否与果实或身体发生碰撞
    private void addHead(){
        int offsetX = 0;
        int offsetY = 0;
        switch (dirt){
            case GameConst.DIRT_UP:
                offsetY = -1;
                break;
            case GameConst.DIRT_DOWN:
                offsetY = 1;
                break;
            case GameConst.DIRT_LEFT:
                offsetX = -1;
                break;
            default:
                offsetX = 1;
        }
        Point oldHead = bodyPositions.get(0);
        int newX = oldHead.x + offsetX * nodeLength;
        int newY = oldHead.y + offsetY * nodeLength;

        if(newX < 0 || newX > PublicData.width){
            newX = Math.abs(Math.abs(newX) - PublicData.width);
        }
        if(newY < 0 || newY > PublicData.height){
            newY = Math.abs(Math.abs(newY) - PublicData.height);
        }
        if(checkHit(newX, newY, PublicData.appleX, PublicData.appleY)){
            snackState = GameConst.SNACK_EAT;
        }
        for(Point p: bodyPositions){
            if(checkHit(newX, newY, p.x, p.y)){
                snackState = GameConst.SNACK_DEAD;
                PublicData.GameState = GameConst.GAME_OVER;
                break;
            }
        }
        bodyPositions.add(0,new Point(newX,newY));
    }

    //判断两个结点是否重合
    private boolean checkHit(int x1, int y1, int x2, int y2){
        return (Math.abs(x1 - x2) < nodeLength && Math.abs(y1 - y2) < nodeLength);
    }

    //键盘监听到方向键或空格键时触发操作
    void pressKey(int keyCode){
        if(keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_W){
            if(this.dirt != GameConst.DIRT_DOWN){
                this.dirt = GameConst.DIRT_UP;
            }
        }
        else if(keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_S){
            if(this.dirt != GameConst.DIRT_UP){
                this.dirt = GameConst.DIRT_DOWN;
            }
        }
        else if(keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_A){
            if(this.dirt != GameConst.DIRT_RIGHT){
                this.dirt = GameConst.DIRT_LEFT;
            }
        }
        else if(keyCode == KeyEvent.VK_RIGHT || keyCode == KeyEvent.VK_D){
            if(this.dirt != GameConst.DIRT_LEFT){
                this.dirt = GameConst.DIRT_RIGHT;
            }
        }
        else if(keyCode == KeyEvent.VK_SPACE){
            if(PublicData.GameState == GameConst.GAME_RUN){
                PublicData.GameState = GameConst.GAME_PAUSE;
            }
            else if(PublicData.GameState == GameConst.GAME_PAUSE){
                PublicData.GameState = GameConst.GAME_RUN;
            }
            else if(PublicData.GameState == GameConst.GAME_OVER){
                PublicData.GameState = GameConst.GAME_RUN;
                init();
            }
        }
    }

    //画面绘制
    @Override
    public void paint(Graphics g) {
        //绘制果实
        g.setColor(new Color(250, 244, 190));
        g.fillOval(PublicData.appleX - PublicData.appleSize/2, PublicData.appleY - PublicData.appleSize/2, PublicData.appleSize, PublicData.appleSize);

        //绘制蛇
        int headX = 0;
        int headY = 0;
        boolean isHead = true;
        for(Point p: bodyPositions){
            if(isHead){
                headX = p.x;
                headY = p.y;
                paintHead(headX,headY,g);
                isHead = false;
                g.setColor(new Color(108, 243, 112));
            }
            else{
                g.fillRect(p.x - nodeLength / 2,p.y - nodeLength / 2, nodeLength, nodeLength);
            }
        }
        //结束时再次绘制蛇头避免与蛇身发生碰撞导致蛇头被蛇身覆盖
        paintHead(headX,headY,g);

        //绘制得分
        g.setColor(Color.white);
        g.setFont(new Font("微软雅黑",Font.BOLD,20));
        g.drawString("得分:" + (bodyLength - 3), 30,30);

        //绘制暂停或结束时屏幕提示信息
        if(PublicData.GameState != GameConst.GAME_RUN){
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            String s = PublicData.GameState == GameConst.GAME_OVER ? "按下【空格】开始游戏": "按下【空格】继续或暂停游戏";
            g.drawString(s, (PublicData.width-s.length()*40)/2,PublicData.height/2);
        }
    }

    //将蛇头绘制到(x,y)处
    private void paintHead(int x, int y, Graphics g){
        g.setColor(new Color(255, 91, 91));
        g.fillOval(x - nodeLength / 2,y - nodeLength / 2, nodeLength, nodeLength);
        g.setColor(new Color(108, 243, 112));
    }
}

 

package com.SnackGame;

public class PublicData {
    //全局变量:游戏状态、窗口宽高
    public static int GameState = GameConst.GAME_OVER;
    public static int width = 600;
    public static int height = 400;

    //果实尺寸、位置
    public static int appleSize = 10;
    public static int appleX;
    public static int appleY;

    //生成新的果实位置
    public static void newApple(){
        appleX = ((int)(Math.random() * PublicData.width) * 8 + PublicData.width)/10;
        appleY = ((int)(Math.random() * PublicData.height) * 8 + PublicData.height)/10;
    }
}
package com.SnackGame;

public class GameConst {
    public static final int GAME_OVER = 0;
    public static final int GAME_PAUSE = 1;
    public static final int GAME_RUN = 2;

    public static final int DIRT_UP = 0;
    public static final int DIRT_LEFT = 1;
    public static final int DIRT_RIGHT = 2;
    public static final int DIRT_DOWN = 3;

    public static final int SNACK_NORMAL = 0;
    public static final int SNACK_EAT = 1;
    public static final int SNACK_DEAD = 2;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值