用AI写游戏4——java实现贪吃蛇1

在这里插入图片描述

package com.ai.snake;

/**
 * @author cruder
 * @version 1.0
 * @date 2025/2/28 9:00
 */
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Random;

public class SnakeGame extends JPanel implements ActionListener {
    private static final int SCREEN_WIDTH = 600;
    private static final int SCREEN_HEIGHT = 600;
    private static final int UNIT_SIZE = 25;
    private static final int GAME_UNITS = (SCREEN_WIDTH * SCREEN_HEIGHT) / UNIT_SIZE;
    private static final int DELAY = 75;
    private final int[] x = new int[GAME_UNITS];
    private final int[] y = new int[GAME_UNITS];
    private int bodyParts = 6;
    private int applesEaten;
    private int appleX;
    private int appleY;
    private char direction = 'R';
    private boolean running = false;
    private Timer timer;
    private Random random;

    public SnakeGame() {
        random = new Random();
        this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
        this.setBackground(Color.black);
        this.setFocusable(true);
        this.addKeyListener(new MyKeyAdapter());
        startGame();
    }

    public void startGame() {
        newApple();
        running = true;
        timer = new Timer(DELAY, this);
        timer.start();
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        draw(g);
    }

    public void draw(Graphics g) {
        if (running) {
            g.setColor(Color.red);
            g.fillOval(appleX, appleY, UNIT_SIZE, UNIT_SIZE);

            for (int i = 0; i < bodyParts; i++) {
                if (i == 0) {
                    g.setColor(Color.green);
                    g.fillRect(x[i], y[i], UNIT_SIZE, UNIT_SIZE);
                } else {
                    g.setColor(new Color(45, 180, 0));
                    g.fillRect(x[i], y[i], UNIT_SIZE, UNIT_SIZE);
                }
            }
            g.setColor(Color.red);
            g.setFont(new Font("Ink Free", Font.BOLD, 40));
            FontMetrics metrics = getFontMetrics(g.getFont());
            g.drawString("Score: " + applesEaten, (SCREEN_WIDTH - metrics.stringWidth("Score: " + applesEaten)) / 2, g.getFont().getSize());
        } else {
            gameOver(g);
        }
    }

    public void newApple() {
        appleX = random.nextInt((int) (SCREEN_WIDTH / UNIT_SIZE)) * UNIT_SIZE;
        appleY = random.nextInt((int) (SCREEN_HEIGHT / UNIT_SIZE)) * UNIT_SIZE;
    }

    public void move() {
        for (int i = bodyParts; i > 0; i--) {
            x[i] = x[i - 1];
            y[i] = y[i - 1];
        }

        switch (direction) {
            case 'U':
                y[0] = y[0] - UNIT_SIZE;
                break;
            case 'D':
                y[0] = y[0] + UNIT_SIZE;
                break;
            case 'L':
                x[0] = x[0] - UNIT_SIZE;
                break;
            case 'R':
                x[0] = x[0] + UNIT_SIZE;
                break;
        }

        if (x[0] == appleX && y[0] == appleY) {
            bodyParts++;
            applesEaten++;
            newApple();
        }

        if (x[0] < 0 || x[0] >= SCREEN_WIDTH || y[0] < 0 || y[0] >= SCREEN_HEIGHT) {
            running = false;
        }

        for (int i = bodyParts; i > 0; i--) {
            if (x[0] == x[i] && y[0] == y[i]) {
                running = false;
            }
        }
    }

    public void gameOver(Graphics g) {
        g.setColor(Color.red);
        g.setFont(new Font("Ink Free", Font.BOLD, 75));
        FontMetrics metrics1 = getFontMetrics(g.getFont());
        g.drawString("Game Over", (SCREEN_WIDTH - metrics1.stringWidth("Game Over")) / 2, SCREEN_HEIGHT / 2);

        g.setColor(Color.red);
        g.setFont(new Font("Ink Free", Font.BOLD, 40));
        FontMetrics metrics2 = getFontMetrics(g.getFont());
        g.drawString("Score: " + applesEaten, (SCREEN_WIDTH - metrics2.stringWidth("Score: " + applesEaten)) / 2, g.getFont().getSize());
    }

    public class MyKeyAdapter extends KeyAdapter {
        @Override
        public void keyPressed(KeyEvent e) {
            switch (e.getKeyCode()) {
                case KeyEvent.VK_LEFT:
                    if (direction != 'R') {
                        direction = 'L';
                    }
                    break;
                case KeyEvent.VK_RIGHT:
                    if (direction != 'L') {
                        direction = 'R';
                    }
                    break;
                case KeyEvent.VK_UP:
                    if (direction != 'D') {
                        direction = 'U';
                    }
                    break;
                case KeyEvent.VK_DOWN:
                    if (direction != 'U') {
                        direction = 'D';
                    }
                    break;
            }
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (running) {
            move();
        }
        repaint();
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        SnakeGame snakeGame = new SnakeGame();
        frame.add(snakeGame);
        frame.setTitle("Snake Game");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.pack();
        frame.setVisible(true);
        frame.setLocationRelativeTo(null);
    }
}

1. 基础结构

package com.ai.snake;
import javax.swing.*;
import java.awt.*;
... // 其他导入
  • package声明代码所属的包(Java的代码组织单元)
  • import语句引入Swing GUI组件(JFrame, JPanel等)和AWT图形库

2. 类定义与常量

public class SnakeGame extends JPanel implements ActionListener {
    private static final int SCREEN_WIDTH = 600;  // 游戏窗口宽度
    private static final int UNIT_SIZE = 25;      // 每个游戏单位(蛇身/苹果)的像素大小
    private static final int DELAY = 75;          // 定时器间隔(控制游戏速度)
  • 继承JPanel实现绘图功能
  • 实现ActionListener接口处理定时器事件

3. 核心变量

private final int[] x = new int[GAME_UNITS];  // 蛇身X坐标数组
private final int[] y = new int[GAME_UNITS];  // 蛇身Y坐标数组
private int bodyParts = 6;                    // 初始蛇身长度
private char direction = 'R';                 // 初始移动方向(右)
  • 使用数组存储蛇身每个节点的坐标
  • bodyParts记录当前蛇身长度
  • direction用U/D/L/R表示移动方向

4. 构造函数

public SnakeGame() {
    random = new Random();
    this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
    this.addKeyListener(new MyKeyAdapter());  // 注册键盘监听
    startGame();
}
  • 设置面板尺寸为600x600
  • 添加自定义键盘事件监听器
  • 调用startGame()初始化游戏

5. 游戏逻辑方法

移动逻辑:
void move() {
    for (int i = bodyParts; i > 0; i--) {  // 从尾部开始更新坐标
        x[i] = x[i-1]; 
        y[i] = y[i-1];
    }
    switch(direction) {  // 根据方向更新头部坐标
        case 'U': y -= UNIT_SIZE; break;
        case 'D': y += UNIT_SIZE; break;
        // ...其他方向类似
    }
}
  • 通过循环将每个身体节点的坐标更新为前一个节点的位置
  • 根据方向键改变头部坐标

碰撞检测:

if (x < 0 || x >= SCREEN_WIDTH) running = false;  // 撞墙检测
for (int i = bodyParts; i>0; i--) {  // 自碰检测
    if (x==x[i] && y==y[i]) running = false;
}

6. 图形绘制

public void draw(Graphics g) {
    if (running) {
        g.fillOval(appleX, appleY, UNIT_SIZE, UNIT_SIZE);  // 绘制苹果
        for (int i=0; i<bodyParts; i++) {  // 绘制蛇身
            if(i==0) g.setColor(Color.GREEN);  // 头部颜色
            else g.setColor(new Color(45,180,0));  // 身体颜色
            g.fillRect(x[i], y[i], UNIT_SIZE, UNIT_SIZE);
        }
    } else {
        gameOver(g);  // 显示结束画面
    }
}

7. 事件处理

class MyKeyAdapter extends KeyAdapter {
    @Override
    public void keyPressed(KeyEvent e) {
        switch(e.getKeyCode()) {
            case KeyEvent.VK_LEFT: 
                if(direction != 'R') direction = 'L';  // 禁止反向移动
                break;
            // 其他方向处理类似
        }
    }
}

8. 游戏启动

public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.add(new SnakeGame());
    frame.setTitle("Snake Game");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();  // 自动调整窗口大小
    frame.setVisible(true);
}

关键机制说明:

  1. 定时器驱动:通过Timer(DELAY, this)每75ms触发一次actionPerformed,实现游戏循环
  2. 双缓冲绘图:继承JPanel后重写paintComponent方法实现无闪烁绘图
  3. 坐标系统:所有坐标以UNIT_SIZE为基本单位,保证蛇和苹果对齐网格
  4. 方向控制:通过限制180度转向(如不能从右直接转左)保证游戏合理性

建议运行代码时注意:

  1. 游戏结束条件包括撞墙和自碰
  2. 每吃一个苹果(红色圆点)得分+1,蛇身长度+1
  3. 使用方向键控制移动方向

这个实现包含了Java GUI编程的核心要素:事件处理、图形绘制、动画定时器等,是学习游戏开发的经典案例。可以尝试修改UNIT_SIZEDELAY值观察游戏变化,加深理解。

由小艺AI生成<xiaoyi.huawei.com>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值