目录
导语:
一、游戏框架
二、蛇的实现
三、绘制游戏界面
四、食物
五、移动蛇
六.得分系统,是否吃到食物
七、检查碰撞
八、处理按键事件
九、得分系统
十、游戏状态管理
导语:
贪吃蛇这个经典的小游戏,我上学的时候就曾经写过一个,参加校内竞赛还拿了个参与奖。想找以前代码,结果发现没有保存。今天有空就再次整理编写一个。理一理思路。
一、游戏框架
首先,我们需要一个游戏框架,用来处理游戏的逻辑、绘制游戏界面等。
游戏框架是贪吃蛇游戏的基础结构,负责处理游戏的逻辑、绘制游戏界面以及处理用户输入等任务。在Java中,我们可以使用Swing库或JavaFX库来创建游戏窗口和图形界面。
Swing库:Swing是Java提供的一套用于创建图形用户界面(GUI)的工具包。通过Swing,你可以创建窗口、按钮、标签等GUI组件,并通过监听器来响应用户的输入事件。Swing适合于简单的2D游戏,对于贪吃蛇游戏来说,它是一个不错的选择。
下面是创建游戏框架的基本步骤:
使用Swing库创建游戏框架
-
导入Swing库:在Java项目中,确保你已经导入了Swing库。
-
创建游戏窗口:使用
JFrame
类创建游戏窗口,并设置窗口标题、大小等属性。
import javax.swing.*;
/**
* 微澜贪吃蛇游戏窗口
*/
public class SnakeGame extends JFrame {
public SnakeGame() {
add(new GamePanel(this));
setTitle("微澜贪吃蛇");
setSize(800, 600); // 设置窗口大小
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭行为
setLocationRelativeTo(null); // 将窗口置于屏幕中央
setVisible(true); // 显示窗口
}
public static void main(String[] args) {
// 创建游戏实例
SwingUtilities.invokeLater(() -> new SnakeGame());
}
}
代码运行如下图所示:
二、蛇的实现
蛇是游戏中的核心角色。我们需要实现以下功能:
蛇的移动:蛇可以朝四个方向之一移动,每次移动一个单位长度。
蛇身的增长:当蛇吃到食物时,蛇身长度增加一个单位。
碰撞检测:检测蛇头是否与墙壁、自己的身体或食物发生碰撞,如果碰撞则游戏结束。
创建一个游戏面板类:GamePanel.java
编写完之后需要将类添加到窗口中。
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class GamePanel extends JPanel implements ActionListener, KeyListener {
private static final int WIDTH = 600;
private static final int HEIGHT = 600;
private static final int UNIT_SIZE = 20;
private static final int GAME_UNITS = (WIDTH * HEIGHT) / UNIT_SIZE;
private static final int DELAY = 100;
private final int[] x = new int[GAME_UNITS];
private final int[] y = new int[GAME_UNITS];
private int bodyParts = 6;
private int foodX;
private int foodY;
private char direction = 'R';
private boolean running = false;
private Timer timer;
private SnakeGame game; // 添加 SnakeGame 实例变量
public GamePanel() {
this.game = game; // 初始化 game 对象
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setBackground(Color.BLACK);
setFocusable(true);
addKeyListener(this);
startGame();
}
public void startGame() {
newFood();
running = true;
timer = new Timer(DELAY, this);
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g) {
// 绘制游戏界面
}
public void newFood() {
// 生成新的食物的位置
}
public void move() {
// 移动蛇
}
public void checkFood() {
// 检查是否吃到食物
}
public void checkCollision() {
// 检查碰撞
}
@Override
public void actionPerformed(ActionEvent e) {
if (running) {
move();
checkFood();
checkCollision();
repaint();
}
}
@Override
public void keyPressed(KeyEvent e) {
// 处理按键事件
}
@Override
public void keyTyped(KeyEvent e) {}
@Override
public void keyReleased(KeyEvent e) {}
}
(具体代码过程,不完全展示)
这个类继承自JPanel
,用于绘制游戏界面和处理游戏逻辑。在这个类中,我们定义了蛇的移动、食物生成、碰撞检测等方法,并实现了ActionListener
和KeyListener
接口来处理游戏逻辑和键盘输入。
可以根据自己的一些设计需要填充draw
、newFood
、move
、checkFood
、checkCollision
等方法的具体实现逻辑。
三、绘制游戏界面
用于绘制蛇、食物和游戏边界等元素。
public void draw(Graphics g) {
if (running) {
// 绘制食物
g.setColor(Color.RED);
g.fillRect(foodX, foodY, 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.YELLOW);
g.drawLine(0, 0, WIDTH, 0);
g.drawLine(0, 0, 0, HEIGHT);
g.drawLine(WIDTH - 1, 0, WIDTH - 1, HEIGHT);
g.drawLine(0, HEIGHT - 1, WIDTH, HEIGHT - 1);
}
}
这段代码将会在游戏面板上绘制食物、蛇头、蛇身和游戏边界。食物使用红色填充,蛇头使用绿色填充,蛇身使用深绿色填充,游戏边界使用黄色线条绘制。
四、食物
生成新食物的位置
public void newFood() {
foodX = (int) (Math.random() * (WIDTH / UNIT_SIZE)) * UNIT_SIZE;
foodY = (int) (Math.random() * (HEIGHT / UNIT_SIZE)) * UNIT_SIZE;
}
这段代码将随机生成食物的位置。食物的横坐标foodX
和纵坐标foodY
被限制在游戏窗口的网格上,每个网格的大小为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] -= 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 checkFood() {
if (x[0] == foodX && y[0] == foodY) {
// 如果蛇头的位置与食物的位置重合,则吃到了食物
bodyParts++; // 增加蛇身长度
newFood(); // 生成新的食物
}
}
这段代码会检查蛇头的位置是否与食物的位置重合。如果重合,则表示蛇吃到了食物,蛇的身体长度会增加,并且生成新的食物位置。
七、检查碰撞
用于检查蛇是否与边界或自身发生碰撞:碰撞之后也就是结束
public void checkCollision() {
// 检查是否与边界发生碰撞
if (x[0] < 0 || x[0] >= WIDTH || y[0] < 0 || y[0] >= HEIGHT) {
running = false; // 游戏结束
return;
}
// 检查是否与自身发生碰撞
for (int i = bodyParts; i > 0; i--) {
if (x[0] == x[i] && y[0] == y[i]) {
running = false; // 游戏结束
return;
}
}
}
首先检查蛇头是否撞到了游戏边界,如果是,则游戏结束。然后,它检查蛇头是否与自身的任何部分重合,如果是,则游戏也会结束。
八、处理按键事件
补充keyPressed(KeyEvent e)
方法的代码,用于处理按键事件,改变蛇的移动方向。
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
if (direction != 'D') // 禁止向下移动
direction = 'U';
break;
case KeyEvent.VK_DOWN:
if (direction != 'U') // 禁止向上移动
direction = 'D';
break;
case KeyEvent.VK_LEFT:
if (direction != 'R') // 禁止向右移动
direction = 'L';
break;
case KeyEvent.VK_RIGHT:
if (direction != 'L') // 禁止向左移动
direction = 'R';
break;
}
}
根据用户按下的方向键来改变蛇的移动方向。如果用户按下上箭头键,则蛇向上移动;如果按下下箭头键,则蛇向下移动;如果按下左箭头键,则蛇向左移动;如果按下右箭头键,则蛇向右移动。同时,还加入了一些限制条件,例如禁止蛇直接反向移动(比如,当蛇向上移动时,不允许按下下箭头键使其立即向下移动)。
九、得分系统
玩游戏还有很重要就是自己有多厉害。那么得分展示那就是必不可少的了。
首先,在SnakeGame
类中添加得分变量和相应的方法:
public class SnakeGame extends JFrame {
// 其他代码...
private int score = 0;
public int getScore() {
return score;
}
public void increaseScore() {
score += 10; // 每次吃到食物增加10分
}
}
在GamePanel
类的draw(Graphics g)
方法中添加绘制得分的逻辑:
public void draw(Graphics g) {
// 绘制游戏界面的其他内容...
// 绘制得分
g.setColor(Color.WHITE);
g.setFont(new Font("Arial", Font.BOLD, 20));
g.drawString("Score: " + game.getScore(), 20, 30);
}
这样,每次游戏界面重新绘制时,都会显示当前的得分。接下来,在checkFood()
方法中调用increaseScore()
方法,以便在蛇吃到食物时增加得分:
public void checkFood() {
if (x[0] == foodX && y[0] == foodY) {
game.increaseScore(); // 增加得分
bodyParts++; // 增加蛇身长度
newFood(); // 生成新的食物
}
}
现在,游戏界面会显示当前的得分,并且每次蛇吃到食物时,得分都会增加。
十、游戏状态管理
要添加游戏状态管理,我们需要在 SnakeGame
类中添加游戏状态的枚举,并在 GamePanel
类中根据不同的游戏状态来控制游戏逻辑的执行。让我们一步步完成这个任务。
首先,在 SnakeGame
类中添加游戏状态的枚举:
public enum GameState {
RUNNING,
PAUSED,
GAME_OVER
}
public class SnakeGame extends JFrame {
// 其他代码...
private GameState gameState = GameState.RUNNING;
public GameState getGameState() {
return gameState;
}
public void setGameState(GameState gameState) {
this.gameState = gameState;
}
// 其他代码...
}
在这里,我们定义了一个 GameState
枚举,表示游戏的不同状态。我们还添加了一个 gameState
实例变量来保存当前的游戏状态,并提供了相应的访问方法。
接下来,在 GamePanel
类中根据不同的游戏状态来控制游戏逻辑的执行。在 actionPerformed
方法中,我们只在游戏运行中(RUNNING
状态)执行游戏逻辑:
public void actionPerformed(ActionEvent e) {
if (game.getGameState() == GameState.RUNNING) {
move();
checkFood();
checkCollision();
repaint();
}
}
然后,在 paintComponent
方法中,我们根据游戏状态来绘制相应的内容,比如游戏进行中绘制游戏界面,游戏暂停时绘制暂停提示,游戏结束时绘制游戏结束提示:
public void paintComponent(Graphics g) {
super.paintComponent(g);
switch (game.getGameState()) {
case RUNNING:
draw(g);
break;
case PAUSED:
// 绘制暂停提示
g.setColor(Color.WHITE);
g.setFont(new Font("Arial", Font.BOLD, 30));
g.drawString("PAUSE!!!", WIDTH / 2 - 100, HEIGHT / 2);
break;
case GAME_OVER:
// 绘制游戏结束提示
g.setColor(Color.WHITE);
g.setFont(new Font("Arial", Font.BOLD, 30));
g.drawString("Game Over!!!", WIDTH / 2 - 100, HEIGHT / 2);
break;
}
}
按键操作部分需要补充逻辑:
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
//其他代码
case KeyEvent.VK_SPACE:
if (game.getGameState() == GameState.RUNNING) {
game.setGameState(GameState.PAUSED); // 暂停游戏
} else if (game.getGameState() == GameState.PAUSED) {
game.setGameState(GameState.RUNNING); // 恢复游戏
}
break;
}
}
在这个更新后的 keyPressed
方法中,我们添加了对空格键的监听。如果游戏正在运行,按下空格键会将游戏状态设为暂停;如果游戏已经暂停,再次按下空格键会将游戏状态设为运行。这样就实现了按空格键暂停和恢复游戏的功能。
到这一步一个简单的贪吃蛇小游戏就搭建成功了。
代码地址:https://gitee.com/zhangyang214/snake.git
欢迎讨论!!!