贪吃蛇:从零开始搭建一个完整的小游戏

news2024/12/23 18:24:41

目录

导语:

一、游戏框架

二、蛇的实现

三、绘制游戏界面

四、食物

五、移动蛇

六.得分系统,是否吃到食物

七、检查碰撞

八、处理按键事件

九、得分系统

十、游戏状态管理


导语:

    贪吃蛇这个经典的小游戏,我上学的时候就曾经写过一个,参加校内竞赛还拿了个参与奖。想找以前代码,结果发现没有保存。今天有空就再次整理编写一个。理一理思路。

一、游戏框架

首先,我们需要一个游戏框架,用来处理游戏的逻辑、绘制游戏界面等。

游戏框架是贪吃蛇游戏的基础结构,负责处理游戏的逻辑、绘制游戏界面以及处理用户输入等任务。在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,用于绘制游戏界面和处理游戏逻辑。在这个类中,我们定义了蛇的移动、食物生成、碰撞检测等方法,并实现了ActionListenerKeyListener接口来处理游戏逻辑和键盘输入。

    可以根据自己的一些设计需要填充drawnewFoodmovecheckFoodcheckCollision等方法的具体实现逻辑。

三、绘制游戏界面

用于绘制蛇、食物和游戏边界等元素。

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

欢迎讨论!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1559856.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用一个程序解决SQLite常见的各项操作(实用篇)

文章说明&#xff1a; 本篇文章是在之前的一篇文章SQLite3进行数据库各项常用操作基础上写的&#xff0c;将SQLite涉及到的常用的几种操作&#xff0c;以函数的形式处理成相互调用的形式。 因为之前的文章对基础操作已经解释过了&#xff0c;所以这里直接放置可执行代码和结果…

常见6种开源协议比较

前言 常见的开源许可协议有6种是比较常见和广泛使用的&#xff0c;每种协议都有其特定的使用场景和约束条件。这6种分别是GPL, LGPL&#xff0c;MIT许可证&#xff0c; Apache许可证&#xff0c;BSD许可证和Mozilla Public License&#xff08;MPL&#xff09;. 6种开源许可协议…

深度学习评价指标(1):目标检测的评价指标

1. 简述 在计算机视觉/深度学习领域&#xff0c;每一个方向都有属于自己的评价指标。通常在评估一个模型时&#xff0c;只需要计算出相应的评价指标&#xff0c;便可以评估算法的性能。同时&#xff0c;所谓SOTA&#xff0c;皆是基于某一评价指标进行的评估。 接下来&#xff0…

GitHub - 使用SSH进行连接

文章目录 前言开发环境单个SSH密钥1.1. 生成SSH密钥1.2. 添加SSH密钥1.3. 测试SSH连接2.1. 简化密钥密码输入 多个SSH密钥1.1. 生成/添加/测试SSH密钥2.1. 简化密钥密码输入 无密码密钥补充内容最后 前言 有一个SSH密钥跟了我很多年&#xff0c;更换电脑也不曾更换它。它不需要…

腾讯云轻量2核2G3M云服务器优惠价格61元一年,限制200GB月流量

腾讯云轻量2核2G3M云服务器优惠价格61元一年&#xff0c;配置为轻量2核2G、3M带宽、200GB月流量、40GB SSD盘&#xff0c;腾讯云优惠活动 yunfuwuqiba.com/go/txy 活动链接打开如下图&#xff1a; 腾讯云轻量2核2G云服务器优惠价格 腾讯云&#xff1a;轻量应用服务器100%CPU性能…

QT初识(1)

QT初识 桌面开发什么是QT下载QT安装好之后的工具AssisantDesignerQT Creator 创建一个简单的项目 我们今天来认识一下QT。 桌面开发 在了解QT&#xff0c;我们得了解一下桌面开发&#xff1a; 桌面开发指的是编写和构建在个人计算机或其他桌面操作系统&#xff08;如Windows、…

论文笔记 - :MonoLSS: Learnable Sample Selection For Monocular 3D Detection

论文笔记✍MonoLSS: Learnable Sample Selection For Monocular 3D Detection &#x1f4dc; Abstract &#x1f528; 主流做法限制 &#xff1a; 以前的工作以启发式的方式使用特征来学习 3D 属性&#xff0c;没有考虑到不适当的特征可能会产生不利影响。 &#x1f528; 本…

全面概述Gitee和GitHub生成/添加SSH公钥

前言 现如今将代码开源已经成为软件开发行业的一种趋势&#xff0c;而现在比较有名的代码托管平台有GItHub、Gitee、Gitlab等相关平台。而我们在使用代码托管平台最为常见的就是如何将自己本地的代码推送到远程托管平台中的仓库中&#xff0c;现如今各大托管平台基本上都提供了…

鸿蒙手机cordova-plugin-camera不能拍照和图片不显示问题

鸿蒙手机cordova-plugin-camera不能拍照和图片不显示问题 一、运行环境 1、硬件 手机型号&#xff1a;NOVA 7 系统&#xff1a;HarmonyOS版本 4.0.0 2、软件 android SDK platforms&#xff1a;14.0(API Level 34)、13.0&#xff08;API Level 33&#xff09; SDK Build-T…

jvm类加载机制概述

、什么是jvm的类加载机制 类加载机制是指我们将类的字节码文件所包含的数据读入内存&#xff0c;同时我们会生成数据的访问入口的一种 特殊机制。那么我们可以得知&#xff0c;类加载的最终产品是数据访问入口。 加载类文件&#xff08;即.class文件&#xff09;的方式有以下几…

一些基本类上实用的注解及例子

文章目录 一些基本类上实用的注解及例子一、Data二、Accessors三、AllArgsConstructor四、NoArgsConstructor五、EqualsAndHashCode六、后面在补充&#xff01;先这样吧&#xff0c;哈哈&#xff01;总结 一些基本类上实用的注解及例子 一、Data Data 注解是一个组合注解&…

Django源码之路由的本质(上)——逐步剖析底层执行流程

目录 1. 前言 2. 路由定义 3. 路由定义整体源码分析 3.1 partial实现path函数调用 3.2 图解_path函数 3.3 最终 4.URLPattern和Pattern的简单解析 5. 小结 1. 前言 在学习Django框架的时候&#xff0c;我们大多时候都只会使用如何去开发项目&#xff0c;对其实现流程并…

linux 软中断入门

在 linux 中&#xff0c;任务执行的载体有很多&#xff0c;包括线程&#xff0c;中断&#xff0c;软中断&#xff0c;tasklet&#xff0c;定时器等。但是从本质上来划分的话&#xff0c;任务执行的载体只有两个&#xff1a;线程和中断。软中断和 tasklet 的执行可能在中断中&am…

云服务器8核32G配置报价大全,腾讯云、阿里云和京东云

8核32G云服务器租用优惠价格表&#xff0c;云服务器吧yunfuwuqiba.com整理阿里云8核32G服务器、腾讯云8核32G和京东云8C32G云主机配置报价&#xff0c;腾讯云和京东云是轻量应用服务器&#xff0c;阿里云是云服务器ECS&#xff1a; 阿里云8核32G服务器 阿里云8核32G服务器价格…

9.动态规划——2.最大序列和

例题——最大序列和 找状态 思路一&#xff08;&#xff09; 定义一个状态 d p [ i ] dp[i] dp[i]&#xff0c;计为前i个数构成子序列和的最大值 此法状态转移比较困难&#xff0c;即若 d p [ i ] dp[i] dp[i]与 d p [ i − 1 ] dp[i-1] dp[i−1]没有明确的关系&#xff0c;有…

获取电商数据的几种方法分享

在数字化时代&#xff0c;电商数据已经成为企业决策的重要依据。无论是市场趋势的洞察、用户行为的分析&#xff0c;还是产品优化和营销策略的制定&#xff0c;都离不开电商数据的支持。本文将分享几种获取电商数据的有效方法&#xff0c;力求在干货满满的同时&#xff0c;也不…

PyCharm中出现Microsoft Defender配置建议

原因 Windows安全中心的病毒和威胁防护会自动扫描电脑中的文件夹&#xff0c;我们的项目文件夹和IDE文件夹也会被扫描&#xff0c;而PyCharm认为这会降低IDE性能。 解决方法 直接点击提示框里的自动。 或是手动给扫描添加排除项&#xff0c;步骤如下&#xff1a; 1、先打开…

Sui原生功能如何改变链上游戏体验

从zkLogin到可编程交易区块&#xff08;PTB&#xff09;&#xff0c;Sui的原生功能为游戏开发人员提供了工具&#xff0c;最终利用了Web3的力量&#xff0c;给玩家带来了新的体验和参与度。之前的区块链在支持链上游戏方面存在技术上的局限&#xff0c;但是Sui提供了开发人员所…

受益于边缘计算的三个关键应用

边缘计算和 5G 网络正在改变物联网&#xff0c;增强跨多个领域的广泛应用的功能&#xff0c;并催生大量新兴应用。我们通过研究三个突出的用例来说明边缘计算的强大功能。 工业4.0智能工厂 工业 4.0 为制造商提供了基于灵活的工业环境提高生产力和盈利能力的愿景&#xff0c;…

AR-Net网络(图像篡改检测)

AR-Net网络 摘要AbstractAR-Net1. 文献摘要2. 研究背景3. 创新点4. AR-Net 网络架构5. 实验6. 结论总结 摘要 AR-Net使用自适应注意力机制来融合位置和通道维度的特征&#xff0c;使网络能够充分利用不同维度的被篡改特征&#xff0c;此外&#xff0c;AR-Net 改进了预测掩模&a…