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 { // 定义SnakeGame类,继承自JPanel并实现ActionListener接口
static final int SCREEN_WIDTH =1000; // 定义屏幕宽度常量
static final int SCREEN_HEIGHT = 1000; // 定义屏幕高度常量
static final int UNIT_SIZE = 25; // 定义每个游戏单位的大小
static final int GAME_UNITS = (SCREEN_WIDTH * SCREEN_HEIGHT) / UNIT_SIZE; // 计算游戏单位总数
static final int DELAY = 200; // 定义游戏更新的延迟时间,单位为毫秒
final int[] x = new int[GAME_UNITS]; // 创建一个数组,用于存储蛇的x坐标
final int[] y = new int[GAME_UNITS]; // 创建一个数组,用于存储蛇的y坐标
int bodyParts = 6; // 初始化蛇的身体部分数量
int applesEaten; // 初始化吃掉的苹果数量
int appleX; // 定义苹果的x坐标
int appleY; // 定义苹果的y坐标
char direction = 'R'; // 初始化蛇的移动方向为向右('R'代表Right)
boolean running = false; // 初始化游戏状态为非运行状态
Timer timer; // 定义一个Timer对象,用于控制游戏更新
Random random; // 定义一个Random对象,用于生成随机数
SnakeGame() { // 构造函数
random = new Random(); // 初始化随机数生成器
this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT)); // 设置面板的首选大小为屏幕大小
this.setBackground(Color.white); // 设置面板的背景颜色为白色
this.setFocusable(true); // 设置面板可以获得焦点
this.addKeyListener(new MyKeyAdapter()); // 为面板添加键盘监听器
startGame(); // 调用startGame方法开始游戏
}
public void actionPerformed1(ActionEvent e) { // 这个方法名是错误的,应该使用actionPerformed
if (running) {
move(); // 移动蛇
checkApple(); // 检查蛇是否吃到苹果
checkCollisions(); // 检查蛇是否发生碰撞
}
repaint(); // 重绘面板
}
public void startGame() { // 初始化游戏
if (timer != null) {
timer.stop(); // 停止之前的计时器
}
newApple(); // 生成新的苹果
running = true; // 设置游戏状态为运行
bodyParts = 6; // 重置蛇的身体部分数量
direction = 'R'; // 重置蛇的初始方向为向右
x[0] = SCREEN_WIDTH / 2; // 设置蛇头的初始x坐标
y[0] = SCREEN_HEIGHT / 2; // 设置蛇头的初始y坐标
for (int i = 1; i < bodyParts; i++) { // 初始化蛇的身体部分
x[i] = x[0] - i * UNIT_SIZE; // 设置每个身体部分的x坐标
y[i] = y[0]; // 设置每个身体部分的y坐标
}
applesEaten = 0; // 重置吃掉的苹果数量
timer = new Timer(DELAY, this); // 创建新的计时器,并设置延迟时间和监听器
timer.start(); // 启动计时器
}
public void paintComponent(Graphics g) { // 重写paintComponent方法,用于绘制游戏界面
super.paintComponent(g); // 调用父类的paintComponent方法
if (running) {
draw(g); // 如果游戏正在运行,调用draw方法绘制游戏元素
} else {
gameOver(g); // 如果游戏结束,调用gameOver方法显示游戏结束信息
}
}
public void draw(Graphics g) { // 绘制游戏元素
for (int i = 0; i < SCREEN_HEIGHT / UNIT_SIZE; i++) { // 绘制网格线
g.drawLine(i * UNIT_SIZE, 0, i * UNIT_SIZE, SCREEN_HEIGHT); // 绘制垂直线
g.drawLine(0, i * UNIT_SIZE, SCREEN_WIDTH, i * UNIT_SIZE); // 绘制水平线
}
g.setColor(Color.red);
g.fillOval(appleX, appleY, UNIT_SIZE, UNIT_SIZE); // 绘制苹果
for (int i = 0; i < bodyParts; i++) { // 绘制蛇的身体
g.setColor((i == 0) ? Color.green : new Color(45, 180, 0)); // 设置蛇头和身体的颜色
g.fillRect(x[i], y[i], UNIT_SIZE, UNIT_SIZE); // 绘制蛇的身体部分
}
g.setColor(Color.red); // 设置画笔颜色为红色
g.setFont(new Font("华文楷体", Font.BOLD, 40)); // 设置字体
FontMetrics metrics = getFontMetrics(g.getFont()); // 获取字体度量
g.drawString("得分: " + applesEaten, (SCREEN_WIDTH - metrics.stringWidth("得分: " + applesEaten)) / 2, g.getFont().getSize());
}
public void newApple() { // 生成新的苹果
appleX = random.nextInt((int) (SCREEN_WIDTH / UNIT_SIZE)) * UNIT_SIZE; // 随机生成苹果的x坐标
appleY = random.nextInt((int) (SCREEN_HEIGHT / UNIT_SIZE)) * UNIT_SIZE; // 随机生成苹果的y坐标
}
public void move() { // 移动蛇
for (int i = bodyParts; i > 0; i--) { // 更新蛇的身体部分位置
x[i] = x[i - 1]; // 将前一个身体部分的x坐标赋值给当前身体部分
y[i] = y[i - 1]; // 将前一个身体部分的y坐标赋值给当前身体部分
}
switch (direction) { // 根据方向更新蛇头的位置
case 'U':
y[0] -= UNIT_SIZE; // 向上移动
break;
case 'D':
y[0] += UNIT_SIZE; // 向下移动
break;
case 'L':
x[0] -= UNIT_SIZE; // 向左移动
break;
case 'R':
x[0] += UNIT_SIZE; // 向右移动
break;
}
}
public void checkApple() { // 检查蛇是否吃到苹果
if (x[0] == appleX && y[0] == appleY) { // 如果蛇头的位置与苹果的位置相同
bodyParts++; // 增加蛇的身体部分
applesEaten++; // 增加吃掉的苹果数量
newApple(); // 生成新的苹果
}
}
public void checkCollisions() { // 检查蛇是否发生碰撞
for (int i = bodyParts; i > 0; i--) { // 检查蛇是否撞到自己
if (x[i] == x[0] && y[i] == y[0]) {
running = false; // 如果撞到自己,游戏结束
break;
}
}
if (x[0] < 0 || x[0] >= SCREEN_WIDTH || y[0] < 0 || y[0] >= SCREEN_HEIGHT) { // 检查蛇是否撞到边界
running = false; // 如果撞到边界,游戏结束
}
}
public void gameOver(Graphics g) { // 显示游戏结束信息
g.setColor(Color.red); // 设置画笔颜色为红色
g.setFont(new Font("华文楷体", Font.BOLD, 75)); // 设置字体为华文楷体,加粗,字号为75
FontMetrics metrics = getFontMetrics(g.getFont()); // 获取字体度量
String gameOverText = "游戏结束"; // 中文游戏结束文本
g.drawString(gameOverText, (SCREEN_WIDTH - metrics.stringWidth(gameOverText)) / 2, SCREEN_HEIGHT / 2); // 绘制“游戏结束”文本
g.setFont(new Font("华文楷体", Font.BOLD, 60)); // 设置字体为华文楷体,加粗,字号为60
String scoreText = "得分: " + applesEaten; // 中文得分文本
g.drawString(scoreText, (SCREEN_WIDTH - metrics.stringWidth(scoreText)) / 2, SCREEN_HEIGHT / 2 + g.getFont().getSize()); // 绘制得分
String restartText = "按空格键重新开始 ";
g.drawString(restartText, SCREEN_WIDTH / 2 - 100, SCREEN_HEIGHT / 2 + g.getFont().getSize() * 2); // 绘制重启提示
}
@Override
public void actionPerformed(ActionEvent e) { // 处理计时器触发的动作事件
if (running) {
move(); // 移动蛇
checkApple(); // 检查蛇是否吃到苹果
checkCollisions(); // 检查蛇是否发生碰撞
}
repaint(); // 重绘面板
}
public class MyKeyAdapter extends KeyAdapter { // 内部类,用于处理键盘事件
@Override
public void keyPressed(KeyEvent e) { // 重写keyPressed方法,处理按键事件
if (!running) { // 如果游戏不在运行状态
if (e.getKeyCode() == KeyEvent.VK_SPACE) { // 如果按下空格键
startGame(); // 重新开始游戏
}
} else { // 如果游戏正在运行
int key = e.getKeyCode(); // 获取按下的键的代码
if ((key == KeyEvent.VK_LEFT || key == KeyEvent.VK_RIGHT) && direction != ((key == KeyEvent.VK_LEFT) ? 'R' : 'L')) {
direction = (key == KeyEvent.VK_LEFT) ? 'L' : 'R'; // 改变方向为左或右
} else if ((key == KeyEvent.VK_UP || key == KeyEvent.VK_DOWN) && direction != ((key == KeyEvent.VK_UP) ? 'D' : 'U')) {
direction = (key == KeyEvent.VK_UP) ? 'U' : 'D'; // 改变方向为上或下
}
}
}
}
public static void main(String[] args) { // 程序的入口点
JFrame frame = new JFrame(); // 创建一个JFrame窗口
SnakeGame game = new SnakeGame(); // 创建SnakeGame实例
frame.add(game); // 将SnakeGame实例添加到窗口中
frame.setTitle("贪吃蛇"); // 设置窗口的标题
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置窗口关闭时的操作
frame.setResizable(false); // 设置窗口不可调整大小
frame.pack(); // 调整窗口大小以适应其内容
frame.setVisible(true); // 设置窗口可见
frame.setLocationRelativeTo(null); // 将窗口居中显示
}
}