用Java实现贪吃蛇小游戏

news2024/12/30 3:14:31

一、创建新项目
首先创建一个新的项目,并命名为贪吃蛇。

其次在贪吃蛇项目下创建一个名为images的文件夹用来存放游戏相关图片。

然后再在项目的src文件下创建一个com.xxx.view的包用来存放所有的图形界面类,创建一个com.xxx.controller的包用来存放启动的入口类(控制类)。

二、游戏界面

package com.snake.view;
 
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
 
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
 
 
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class SnakeJPanel extends JPanel implements ActionListener{
    
    private boolean start;//当前游戏状态
    
    private int speed;//速度
    
    private boolean exist;//当前是否存在食物
    
    private int foodType;//食物种类
    
    private int x;//豆子的横坐标
    private int y;//豆子的纵坐标

private ArrayList<int[]> localList;//蛇
    
    
    public String direction;//方向
    
    private String direction2;//引导方向
    
    public boolean flag;
    
    Random rand = new Random();
 
    private ImageIcon up;
    private ImageIcon down;
private ImageIcon right;
    private ImageIcon left;
    private ImageIcon body; 
    private ImageIcon food;
    private ImageIcon title;
    Timer time;

private int score;//当前得分情况
    
    private int num;//吃到的食物个数
    
//    private Image offScreenImage;  //图形缓存
    
    
    //图片绘制
    @Override
    public void paint(Graphics g) {
        
        direction = direction2;
        
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, 900, 700);
  //绘制游戏框
        //标题框
//        g.drawRect(25, 30, 800, 75);
        title.paintIcon(this, g, 25, 10);
        
        //内容框
        g.setColor(Color.black);
        g.fillRect(25, 75, 850, 600);
        
        //绘制食物的坐标位置
        if(!exist) {//如果当前不存在豆子,随机绘制一个豆子    
            if(num % 5 == 0) {
                foodType = 1;
            }else {
                foodType = 0;
  }
            boolean isProduce = true;
            while(isProduce) {
                isProduce = false;
                x = rand.nextInt(33) * 25 + 25;        
                y = rand.nextInt(23) * 25 + 75;            
                for (int[] arr:localList) {
                    if(x == arr[0] && y == arr[1]) {    
                        isProduce = true;
                        break;    
                    }
                }
}            
            System.out.println(x + "---" + y);
        }
        
         if(eat()) {
            exist = false;
         }else {
            exist = true;
         }
         
         
        if(foodType == 0) {
            //绘制食物
 g.setColor(Color.blue);
//            g.fillRect(x, y, 25, 25);
            g.drawImage(food.getImage(),x, y, 25, 25,null);
        }else {
            //绘制食物
            g.setColor(Color.WHITE);
            g.fillRect(x, y, 25, 25);
//            g.drawImage(food.getImage(),x, y, 25, 25,null);
        }
    
            
        //绘制头
 g.setColor(Color.red);
//        g.fillRect(localList.get(0)[0], localList.get(0)[1], 25, 25);    
        ImageIcon head = null;
        //判断当前方向
        if(direction.equals("R")) {
             head = right;
        }else if(direction.equals("L")) {
             head = left;
        }else if(direction.equals("U")) {
             head = up;
        }else if(direction.equals("D")) {
             head = down;

}        
//        g.drawImage(head.getImage(), localList.get(0)[0], localList.get(0)[1], 25, 25,null);
        head.paintIcon(this, g,localList.get(0)[0], localList.get(0)[1]);
        
        //绘制身体
        g.setColor(Color.white);
        for (int i = 1; i < localList.size(); i++) {
//            g.fillRect(localList.get(i)[0], localList.get(i)[1], 25, 25);
//            g.drawImage(body.getImage(), localList.get(i)[0], localList.get(i)[1], 25, 25,null);
            body.paintIcon(this, g, localList.get(i)[0], localList.get(i)[1]);
        }
//        g.fillRect(localList.get(1)[0], localList.get(1)[1], 25, 25);
//        g.fillRect(localList.get(2)[0], localList.get(2)[1], 25, 25);
//绘制分数和长度
        //长度
        g.setColor(Color.GREEN);
        g.setFont(new Font("宋体", Font.BOLD, 18));
        g.drawString("长度:" + (localList.size() - 1), 25, 30);
        
        //分数
        g.drawString("分数:" + score, 25, 48);
        
        if(!start) {//如果游戏未启动,结束移动和重绘
            g.setColor(Color.white);
            g.setFont(new Font("宋体", Font.BOLD, 30));
            g.drawString("暂停/开始(请按任意键开始,空格键暂停)", 150, 300);
            time.stop();
}else {
            time.start();
        }
            
//        speed();
        //移动后进行下一次绘制        
//      move();//移动
//        repaint();//重新绘制        
    }
    
//    //解决闪烁问题
//    //如果为JFrame 为重量级  程序不会调用update()方法
//    //如果为Frame 为轻量级  重写update()方法 做双缓冲
//    //如果为JPanel 不会闪烁
//      @Override
//        public void update(Graphics g)
//        {
//            System.out.println("update");
//               if(offScreenImage == null)
//                  offScreenImage = this.createImage(900, 700);  //新建一个图像缓存空间,这里图像大小为800*600
//                  Graphics gImage = offScreenImage.getGraphics();  //把它的画笔拿过来,给gImage保存着
//                  paint(gImage);                                   //将要画的东西画到图像缓存空间去
//                  g.drawImage(offScreenImage, 0, 0, null);         //然后一次性显示出来
//        }
  @Override
    public void actionPerformed(ActionEvent e) {        
        //移动后进行下一次绘制        
        move();//移动
        repaint();//重新绘制        
        
    }
 
    /**
     * 绘制速度
     */
//    private void speed() {
//        try {//按一定速度进行移动
//            Thread.sleep(speed);//控制移动速度
//        } catch (InterruptedException e) {
//            // TODO 自动生成的 catch 块
//            e.printStackTrace();
//        }
//    }
 
    /**
     * 初始化图片
     */
 private void drawImage() {
        up = new ImageIcon("images/up.png");
        down = new ImageIcon("images/down.png");
        right = new ImageIcon("images/right.png");
        left = new ImageIcon("images/left.png");
        body = new ImageIcon("images/body.png");
        food = new ImageIcon("images/food.png");
        title = new ImageIcon("images/title.jpg");
    }
    
    private boolean eat() {
if(localList.get(0)[0] == x && localList.get(0)[1] == y) {//如果当前蛇头吃到了豆子
            System.out.println("eat");
            num++;
            if(foodType == 0) {
                score += 10;
            }else {
                score += (rand.nextInt(5) * 10 + 10);
            }
            int last = localList.size() - 1;//蛇尾            
            //在蛇尾后面添加一节身体
 localList.add(new int[] {localList.get(last)[0],localList.get(last)[1]});
            return true;
        }
        return false;
    }
 
    //移动方法
    public void move() {
        //判断是否游戏结束

 if(isbody()) {
            System.out.println("game over");
            start = false;//结束游戏移动
            JOptionPane.showMessageDialog(null,"游戏已结束!");
            time.stop();
            init();        
        }
            
        if(flag && localList != null) {//如果长度不为空且游戏未结束                
            int last = localList.size() - 1;//记录蛇尾
            
            for (int i = last; i > 0; i--) {//从蛇尾开始,每节身体移动到前一节身体的位置上
                localList.set(i,new int[] {localList.get(i - 1)[0],localList.get(i - 1)[1]});
            }
//记录头位置
            int[] local = localList.get(0);
            //判断当前方向,并进行模拟移动,判断是否与边界重合
            if(direction.equals("R")) {
                if(local[0] >= 850) {
                    local[0] = 25;
                }else {
                    local[0] += 25;
                }
                
            }else if(direction.equals("L")) {
 if(local[0] <= 25) {
                    local[0] = 850;
                }else {
                    local[0] -= 25;
                }
                
            }else if(direction.equals("U")) {
                
                if(local[1] <= 75) {
                    local[1] = 650;
                }else {
                    local[1] -= 25;
                }

  }else if(direction.equals("D")) {
                if(local[1] >= 650) {
                    local[1] = 75;
                }else {
                    local[1] += 25;
                }
                
            }            
                        
            //更改头的位置
            localList.set(0, local);        
        }    
    }
 //判断下一步是否为蛇身
    private boolean isbody() {
        // TODO 自动生成的方法存根
        //记录头位置
        int x = localList.get(0)[0];
        int y = localList.get(0)[1];
 
        //判断当前方向,并进行模拟移动,判断是否与边界重合
        if(direction.equals("R")) {
            x += 25;
        }else if(direction.equals("L")) {
            x -= 25;
        }else if(direction.equals("U")) {
            y -= 25;

 }else if(direction.equals("D")) {
            y += 25;
        }            
        
        for (int i = 1; i < localList.size(); i++) {
            if(localList.get(i)[0] == x && localList.get(i)[1] == y) {
                return true;
            }
        }
        return false;
 
    }
    
//    //判断下一步是否为边界
//    private boolean isborder() {
//        // TODO 自动生成的方法存根
//        //记录头位置
//        // TODO 自动生成的方法存根
//        //记录头位置
//        int x = localList.get(0)[0];
//        int y = localList.get(0)[1];
//
//        //判断当前方向,并进行模拟移动,判断是否与边界重合
//        if(direction.equals("R")) {
//            x += 25;
//        }else if(direction.equals("L")) {
//            x -= 25;
//        }else if(direction.equals("U")) {
//            y -= 25;
//        }else if(direction.equals("D")) {
//            y += 25;
//        }    
//                
//        if(x < 25 || x > (33 * 25 + 25)) {
//            return true;//当x坐标超出边界,则返回true
//        }
//        if(y < 105 || y > (23 * 25 + 105)) {
//            return true;//当y坐标超出边界,则返回true
//        }
//        return false;//蛇头移动后未超出边界,返回false
//        
//    }
 
    /**
     * Create the frame.
     */
    public SnakeJPanel(int speed) {
this.speed = speed; //初始化速度
        
        //初始化游戏面板的基本信息
        this.setSize(900, 700);
        this.setLocation(0, 30);
        this.setFocusable(true);
        
        init();//初始化界面
        drawImage();//绘制图片
        moveByKey();//给界面添加一个键盘监听
                
    }
  /*
     * 键盘监听
     * 通过键盘输入上下左右来控制当前蛇头移动的方向
     * 先判断当前蛇头方向,再来改变引导方向
     * 当进行绘制时再修改蛇的方向
     * 保证不会因为在短时间内快速变换方向导致蛇头逆向转向
     */
    private void moveByKey() {
        addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                int key = e.getKeyCode();

 //边界值判断
                switch(key) {
                case 65:
                case 37:{//向左走
                    if(!direction.equals("R")) {
                        direction2 = "L";
                        
                    }
                    break;
                }                
                case 87:
                case 38:{//向上走
                    if(!direction.equals("D")) {

 direction2 = "U";
                    }                
                    break;
                }                
                case 68:
                case 39:{//向右走
                    if(!direction.equals("L")) {
                        direction2 = "R";
                    }
                    break;
                }
                case 83:
                case 40:{//向下走
                    if(!direction.equals("U")) {
                        direction2 = "D";
 }                    
                    break;
                }
                case KeyEvent.VK_SPACE:{//如果当前键盘输入为空格
                    start = !start;//调整游戏状态
                    System.out.println("暂停/开始");
                    repaint();//重绘
                }
                }
                
                //任意键开始
                if(!start && key != KeyEvent.VK_SPACE) {//如果当前状态为暂停状态,且键盘输入不是空格
 start = true;
                    repaint();//重绘
                    
                }                
            }
        });
    }
 
    /**
     * 初始化游戏基本信息

 */
    private void init() {
        start = false;
 
        exist = true;
 
        direction2 = "U";
 
        flag = true;
 
        localList = new ArrayList<int[]>();
localList.add(0,new int[] {75,125});//蛇头
        localList.add(1,new int[] {75,150});//蛇身1
        localList.add(2,new int[] {75,175});//蛇身2
 
        //创建第一个食物的位置
        //通过循环保证当前生成的食物不在身体所在的坐标上
        boolean isProduce = true;
        while(isProduce) {//循环生成食物坐标
            isProduce = false;//结束本次循环
            x = rand.nextInt(33) * 25 + 25;        
            y = rand.nextInt(23) * 25 + 75;            
            for (int[] arr:localList) {//循环遍历蛇头及蛇身的坐标
                if(x == arr[0] && y == arr[1]) {//如果食物坐标和蛇的某一节坐标重合
 isProduce = true;//跳转循环状态,继续下一次食物生成
                    break;    
                }
            }
            //蛇身遍历完成,没有重合坐标,结束食物坐标生成
                            
        }
 
        time = new Timer(speed, this);
        setLayout(null);
 
        score = 0;
num = 0;
        
        foodType = 0;
        
//        repaint();
        
    }
 }

三、构造启动类 

package com.snake.controller;
 
import javax.swing.JFrame;
import javax.swing.JOptionPane;
 
import com.snake.view.SnakeJPanel;
 
public class SnakeStart {
    public static void main(String[] args) {
        
int speed = 0;
        String showInputDialog = null;//初始化时间
        //得到速度
        while(true) {
            showInputDialog = JOptionPane.showInputDialog("蛇移动速度(1 - 5)","3");
            
            if(showInputDialog == null) {
                showInputDialog = "3";//默认速度
                break;
            }
            if(showInputDialog.length() > 1) {
                continue;
            }
char[] a = showInputDialog.toCharArray();
            if(a[0] >= '1' && a[0] <= '5') {
                break;
            }
        }
            
        speed = Integer.parseInt(showInputDialog) * 50;
        
        
        SnakeJPanel snakeJPanel = new SnakeJPanel(speed);
    

  //创建一个JFrame窗口,将游戏面板添加进行窗口中
        JFrame jFrame = new JFrame();
        //设置窗口的某些属性
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jFrame.setSize(920, 750);
        jFrame.add(snakeJPanel);
        jFrame.setLocationRelativeTo(null);
        jFrame.setVisible(true);
    }

}
 

四、游戏测试 

以上步骤都做好就可以进行测试了,先激活Fn键,然后ctrl+F11快捷运行。

1.弹出窗口,设置贪吃蛇移动速度
2.按任意键开始游戏,空格键暂停:

3.上下左右(↑↓←→)移动:

4.头部碰到身体任何部位游戏结束:

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

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

相关文章

js-webApi 笔记2之DOM事件

目录 1、表单事件 2、键盘事件 3、事件对象 4、排他思想 5、事件流 6、捕获和冒泡 7、阻止默认和冒泡 8、事件委托 9、事件解绑 10、窗口加载事件 11、窗口尺寸事件 12、元素尺寸和位置 13、窗口滚动事件 14、日期对象 15、节点 16、鼠标移入事件 1、表单事件 获取…

SourceTree提示128错误

错误&#xff1a; SourceTree打开报错&#xff1a;git log 失败&#xff0c;错误代码128 错误截图&#xff1a; 解决方法&#xff1a; 第一种&#xff1a; 打开电脑路径 C:\Users\Administrator &#xff0c;删除下面的【.gitconifg】文件 第二种&#xff1a; 如果上述方法…

场景交互与场景漫游-场景漫游器(6)

场景漫游 在浏览整个三维场景时&#xff0c;矩阵变换是非常关键的&#xff0c;通过适当的矩阵变换可以获得各种移动或者渲染效果。因此&#xff0c;在编写自己的场景漫游操作器时&#xff0c;如何作出符合逻辑的矩阵操作器是非常重要的&#xff0c;但这对初学者来说还是有一定难…

黑马React18: 基础Part 1

黑马React: 基础1 Date: November 15, 2023 Sum: React介绍、JSX、事件绑定、组件、useState、B站评论 React介绍 概念: React由Meta公司研发&#xff0c;是一个用于 构建Web和原生交互界面的库 优势: 1-组件化的开发方式 2-优秀的性能 3-丰富的生态 4-跨平台开发 开发环境搭…

鸿蒙ToastDialog内嵌一个xml页面会弹跳到一个新页面《解决》

ToastDialog 土司组件 1.问题展示2.代码展示3.问题分析 1.问题展示 0.理想效果 错误效果: 1.首页展示页面 (未点击按钮前) 2.点击按钮之后&#xff0c;弹窗不在同一个位置 2.代码展示 1.点击按钮的 <?xml version"1.0" encoding"utf-8"?> <…

Jmeter 如何监控目标服务的系统资源

下载Jmeter插件管理下载 perfmon 将这个插件管理放到Jmeter的\lib\ext目录下 然后重启Jmeter jmeter-plugins-manager-1.10.jar 下载 perfmon插件 添加 io 内存 磁盘的监听 并且添加监听 在宿主机中安装代理监听程序 并启动 ServerAgent.tar.gz

Linux常用命令——bzcat命令

在线Linux命令查询工具 bzcat 解压缩指定的.bz2文件 补充说明 bzcat命令解压缩指定的.bz2文件&#xff0c;并显示解压缩后的文件内容。保留原压缩文件&#xff0c;并且不生成解压缩后的文件。 语法 bzcat(参数)参数 .bz2压缩文件&#xff1a;指定要显示内容的.bz2压缩文…

任正非说:公司要逐步实行分灶吃饭,我们在管理上不能过于整齐划一,否则缺少战斗力。

你好&#xff01;这是华研荟【任正非说】系列的第42篇文章&#xff0c;让我们聆听任正非先生的真知灼见&#xff0c;学习华为的管理思想和管理理念。 一、我们必须在混沌中寻找战略方向。规划就是要抓住机会点&#xff0c;委员会是火花荟萃的地方&#xff0c;它预研的方向是可做…

贝加莱MQTT功能

贝加莱实现MQTT Client端的功能库和例程 导入库和例程&#xff0c;AS Logical View中分别通过Add Object—Library&#xff0c;Add—Program插入MQTT库和例程。 将例程Sample放置于CPU循环周期中 定义证书存放路径&#xff0c;在AS Physical View 中&#xff0c;右击PLC—Con…

聚观早报 |零跑C10亮相广州车展;小鹏X9亮相广州车展

【聚观365】11月18日消息 零跑C10亮相广州车展 小鹏X9亮相广州车展 坦克700 Hi4-T开启预售 超A级家轿五菱星光正式预售 哪吒汽车发布山海平台2.0 零跑C10亮相广州车展 零跑汽车首款全球车型C10在广州车展首次亮相&#xff0c;同时该车也是零跑LEAP 3.0技术架构下的首款全…

C++菜鸟日记2

关于getline()函数&#xff0c;在char和string输入的区别 参考博客 1.在char中的使用&#xff1a; 2.在string中的使用&#xff1a; 关于char字符数组拼接和string字符串拼接方法 参考博客 字符串拼接方法&#xff1a; 1.直接用 号 2.利用append&#xff08;&#xff0…

【草料】uni-app ts vue 小程序 如何如何通过草料生成对应的模块化二维码

一、查看uni-app项目 1、找到路径 可以看到项目从 src-race-pages-group 这个使我们目标的查询页面 下面我们将这个路径copy到草料内 2、找到进入页面入参 一般我们都会选择 onload() 函数下的入参 这里我们参数的是 id 二、草料 建议看完这里的教程文档 十分清晰&#xff01…

详解自动化测试之 Selenium

目录 1. 什么是自动化 2.自动化测试的分类 3. selenium&#xff08;web 自动化测试工具&#xff09; 1&#xff09;选择 selenium 的原因 2&#xff09;环境部署 3&#xff09;什么是驱动&#xff1f; 4. 一个简单的自动化例子 5.selenium 常用方法 5.1 查找页面元素&…

【STM32】RTC(实时时钟)

1.RTC简介 本质&#xff1a;计数器 RTC中断是外部中断&#xff08;EXTI&#xff09; 当VDD掉电的时候&#xff0c;Vbat可以通过电源--->实时计时 STM32的RTC外设&#xff08;Real Time Clock&#xff09;&#xff0c;实质是一个 掉电 后还继续运行的定时器。从定时器的角度…

三十分钟学会zookeeper

zookeeper 一、前提知识 集群与分布式 ​ 集群&#xff1a;将一个任务部署在多个服务器&#xff0c;每个服务器都能独立完成该任务。 ​ 分布式&#xff1a;将一个任务拆分成若干个子任务&#xff0c;由若干个服务器分别完成这些子任务&#xff0c;每个服务器只能完成某个特…

Vite -静态资源处理 - SVG格式的图片

特点 Vite 对静态资源是开箱即用的。 无需做特殊的配置。项目案例 项目结构 study-vite| -- src| -- assets| -- bbb.svg # 静态的svg图片资源| -- index.html # 主页面| -- main.js # 引入静态资源| -- package.json # 脚本配置| -- vite.co…

探索Scrapy中间件:自定义Selenium中间件实例解析

简介 Scrapy是一个强大的Python爬虫框架&#xff0c;可用于从网站上抓取数据。本教程将指导你创建自己的Scrapy爬虫。其中&#xff0c;中间件是其重要特性之一&#xff0c;允许开发者在爬取过程中拦截和处理请求与响应&#xff0c;实现个性化的爬虫行为。 本篇博客将深入探讨…

Pycharm之配置python虚拟环境

最近给身边的人写了脚本&#xff0c;在自己电脑可以正常运行。分享给我身边的人&#xff0c;却运行不起来&#xff0c;然后把报错的截图给我看了&#xff0c;所以难道不会利用pycharm搭建虚拟的环境&#xff1f;记录一下配置的过程。 第一步&#xff1a;右键要打开的python的代…

什么是单域名SSL安全证书?

单域名证书是什么&#xff1f; 单域名证书是指只包含一个具体域名的SSL/TLS证书&#xff0c;它可以用于保护单个主机名的HTTPS通信。例如&#xff0c;如果您有一个网站http://www.example.com&#xff0c;则单域名证书将仅为该域名颁发。 这种证书在保护单个域的安全方面很有…

hash 哈希表

哈希表是一种期望算法。 一般情况下&#xff0c;哈希表的时间复杂度是 O(1)。 作用 将一个复杂数据结构映射到一个较小的空间 0~N&#xff08;10^5~10^6&#xff09;&#xff0c;最常用的情景&#xff1a;将 0~10^9 映射到 0~10^5。 离散化是一种及其特殊的哈希方式。离散化…