这段 Java 代码实现了一个经典的贪吃蛇游戏。玩家可以使用键盘的上下左右箭头键控制蛇的移动方向,蛇会在游戏面板中移动并尝试吃掉随机生成的食物。每吃掉一个食物,蛇的身体会变长,玩家的得分也会增加。如果蛇撞到自己的身体或者撞到游戏面板的边界,游戏就会结束。
java
public class SnakeGame extends JPanel implements ActionListener {
SnakeGame
类继承自 JPanel
,用于创建游戏面板。ActionListener
接口,用于处理定时器触发的事件。java
static final int SCREEN_WIDTH = 600;
static final int SCREEN_HEIGHT = 600;
static final int UNIT_SIZE = 25;
static final int GAME_UNITS = (SCREEN_WIDTH * SCREEN_HEIGHT) / UNIT_SIZE;
static final int DELAY = 75;
SCREEN_WIDTH
和 SCREEN_HEIGHT
:定义游戏面板的宽度和高度,单位为像素。UNIT_SIZE
:定义游戏中每个格子的大小,单位为像素。GAME_UNITS
:计算游戏面板中总的格子数量。DELAY
:定义游戏的延迟时间,单位为毫秒,用于控制蛇的移动速度。java
final int x[] = new int[GAME_UNITS];
final int y[] = new int[GAME_UNITS];
int bodyParts = 6;
int applesEaten;
int appleX;
int appleY;
char direction = 'R';
boolean running = false;
Timer timer;
Random random;
x[]
和 y[]
:存储蛇的身体每个部分的 x 和 y 坐标。bodyParts
:蛇的初始身体长度。applesEaten
:记录蛇吃掉的食物数量,即玩家的得分。appleX
和 appleY
:食物的 x 和 y 坐标。direction
:蛇的移动方向,初始为向右('R'
)。running
:表示游戏是否正在运行的标志。timer
:定时器,用于控制游戏的更新。random
:随机数生成器,用于随机生成食物的位置。java
SnakeGame() {
random = new Random();
this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
this.setBackground(Color.black);
this.setFocusable(true);
this.addKeyListener(new MyKeyAdapter());
startGame();
}
startGame()
方法开始游戏。java
public void startGame() {
newApple();
running = true;
timer = new Timer(DELAY, this);
timer.start();
}
newApple()
方法生成新的食物。running
标志设置为 true
,表示游戏开始运行。DELAY
毫秒触发一次 actionPerformed()
方法。java
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
paintComponent()
方法,调用父类的 paintComponent()
方法进行默认绘制。draw()
方法绘制游戏元素。java
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);
} 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);
}
}
gameOver()
方法显示游戏结束信息。java
public void newApple() {
appleX = random.nextInt((int) (SCREEN_WIDTH / UNIT_SIZE)) * UNIT_SIZE;
appleY = random.nextInt((int) (SCREEN_HEIGHT / UNIT_SIZE)) * UNIT_SIZE;
}
java
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;
}
}
java
public void checkApple() {
if ((x[0] == appleX) && (y[0] == appleY)) {
bodyParts++;
applesEaten++;
newApple();
}
}
newApple()
方法生成新的食物。java
public void checkCollisions() {
for (int i = bodyParts; i > 0; i--) {
if ((x[0] == x[i]) && (y[0] == y[i])) {
running = false;
}
}
if (x[0] < 0) {
running = false;
}
if (x[0] > SCREEN_WIDTH) {
running = false;
}
if (y[0] < 0) {
running = false;
}
if (y[0] > SCREEN_HEIGHT) {
running = false;
}
if (!running) {
timer.stop();
}
}
running
标志设置为 false
,表示游戏结束。running
标志设置为 false
。java
public void gameOver(Graphics g) {
g.setColor(Color.red);
g.setFont(new Font("Ink Free", Font.BOLD, 40));
FontMetrics metrics1 = getFontMetrics(g.getFont());
g.drawString("Score: " + applesEaten, (SCREEN_WIDTH - metrics1.stringWidth("Score: " + applesEaten)) / 2, g.getFont().getSize());
g.setColor(Color.red);
g.setFont(new Font("Ink Free", Font.BOLD, 75));
FontMetrics metrics2 = getFontMetrics(g.getFont());
g.drawString("Game Over", (SCREEN_WIDTH - metrics2.stringWidth("Game Over")) / 2, SCREEN_HEIGHT / 2);
}
java
@Override
public void actionPerformed(ActionEvent e) {
if (running) {
move();
checkApple();
checkCollisions();
}
repaint();
}
move()
方法移动蛇,调用 checkApple()
方法检查是否吃到食物,调用 checkCollisions()
方法检查是否发生碰撞。repaint()
方法重新绘制游戏面板。java
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;
}
}
}
MyKeyAdapter
类继承自 KeyAdapter
,用于处理键盘事件。java
public static void main(String[] args) {
JFrame frame = new JFrame("Snake Game");
SnakeGame gamePanel = new SnakeGame();
frame.add(gamePanel);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
JFrame
窗口,设置窗口标题为 “Snake Game”。SnakeGame
类的实例,将其添加到窗口中。完整代码
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.ArrayList;
import java.util.Random;
// 主游戏类
public class SnakeGame extends JPanel implements ActionListener {
// 定义游戏面板的大小
static final int SCREEN_WIDTH = 600;
static final int SCREEN_HEIGHT = 600;
// 定义每个格子的大小
static final int UNIT_SIZE = 25;
// 定义游戏面板的格子数量
static final int GAME_UNITS = (SCREEN_WIDTH * SCREEN_HEIGHT) / UNIT_SIZE;
// 定义游戏的延迟时间,控制蛇的移动速度
static final int DELAY = 75;
// 存储蛇的身体坐标
final int x[] = new int[GAME_UNITS];
final int y[] = new int[GAME_UNITS];
// 蛇的初始身体长度
int bodyParts = 6;
// 食物的数量
int applesEaten;
// 食物的 x 坐标
int appleX;
// 食物的 y 坐标
int appleY;
// 蛇的移动方向,'R' 表示向右
char direction = 'R';
// 游戏是否正在运行的标志
boolean running = false;
// 定时器,用于控制游戏的更新
Timer timer;
// 随机数生成器,用于随机生成食物的位置
Random random;
// 构造函数,初始化游戏
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;
// 创建定时器,每隔 DELAY 毫秒触发一次 actionPerformed 方法
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);
} 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;
}
}
// 检查是否吃到食物的方法
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[0] == x[i]) && (y[0] == y[i])) {
running = false;
}
}
// 检查蛇头是否撞到边界
if (x[0] < 0) {
running = false;
}
if (x[0] > SCREEN_WIDTH) {
running = false;
}
if (y[0] < 0) {
running = false;
}
if (y[0] > SCREEN_HEIGHT) {
running = false;
}
if (!running) {
timer.stop();
}
}
// 游戏结束的方法
public void gameOver(Graphics g) {
// 绘制得分
g.setColor(Color.red);
g.setFont(new Font("Ink Free", Font.BOLD, 40));
FontMetrics metrics1 = getFontMetrics(g.getFont());
g.drawString("Score: " + applesEaten, (SCREEN_WIDTH - metrics1.stringWidth("Score: " + applesEaten)) / 2, g.getFont().getSize());
// 绘制游戏结束信息
g.setColor(Color.red);
g.setFont(new Font("Ink Free", Font.BOLD, 75));
FontMetrics metrics2 = getFontMetrics(g.getFont());
g.drawString("Game Over", (SCREEN_WIDTH - metrics2.stringWidth("Game Over")) / 2, SCREEN_HEIGHT / 2);
}
// 定时器触发的方法,处理游戏逻辑
@Override
public void actionPerformed(ActionEvent e) {
if (running) {
move();
checkApple();
checkCollisions();
}
repaint();
}
// 键盘事件监听器类
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;
}
}
}
// 主方法,启动游戏
public static void main(String[] args) {
JFrame frame = new JFrame("Snake Game");
SnakeGame gamePanel = new SnakeGame();
frame.add(gamePanel);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}