首页 > 编程语言 >java-贪吃蛇小游戏

java-贪吃蛇小游戏

时间:2022-11-16 13:47:09浏览次数:47  
标签:25 java snakex snakey 小游戏 snake 贪吃蛇 new public

java GUI编程实现贪吃蛇小游戏

一 准备

首先准备三张图片,分别是:蛇头,蛇身体,食物。(尺寸 25 * 25 像素)
我创建的是一个springboot项目,所以我把这些静态资源放在了resources目录下的static文件夹下,创建个文件夹叫snake,再把这三张图片放进去。

image

二 新建一个Data类,作为图片资源


import cn.hutool.core.io.resource.ClassPathResource;

import javax.swing.*;
import java.net.URL;

/**
 * create by fzg
 * 2022/11/15 13:07
 * 获取数据,图片
 */

// 数据:图片资源
public class Data {

    // 图片资源放哪里都可以,只要能找到,并且将类型转化为URL
    // 我用的是胡图工具的 ClassPathResource

    // 蛇头
    public static URL headerURL = new ClassPathResource("/static/snake/header.png").getUrl();
    // 蛇身体
    public static URL bodyURL = new ClassPathResource("/static/snake/body.png").getUrl();
    // 食物
    public static URL foodURL  = new ClassPathResource("/static/snake/food.png").getUrl();


    public static ImageIcon header = new ImageIcon(headerURL);
    public static ImageIcon body = new ImageIcon(bodyURL);
    public static ImageIcon food = new ImageIcon(foodURL);

    

}

三 Snake类,定义蛇的数据结构和初始化方法


/**
 * create by fzg
 * 2022/11/15 13:33
 */

// 贪吃蛇基本类
public class Snake {

    // 定义蛇的数据结构
    int length; // 蛇的长度
    int[] snakex = new int[600]; // 蛇的x坐标
    int[] snakey = new int[500]; // 蛇的y坐标

    // 初始化方向
    String fx;

    // 初始化方法
    public void init(){
        // 初始化长度3
        length = 3;
        // 脑袋的坐标
        snakex[0] = 100;
        snakey[0] = 100;
        // 第一个身体的坐标
        snakex[1] = 75;
        snakey[1] = 100;
        // 第二个身体的坐标
        snakex[2] = 50;
        snakey[2] = 100;
        // 初始化方向往右
        fx = "D";

    }

    public Snake(){
        init();
    }

}

四 游戏面板类 GamePanel(核心代码)

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;
import java.util.Random;

/**
 * create by fzg
 * 2022/11/15 13:39
 */

/**
 * 游戏面板
 * 键盘监听器
 * 定时器
 */
public class GamePanel extends JPanel implements KeyListener, ActionListener {

    // 游戏当前的状态
    boolean isStart = false; // 默认不开始
    // 游戏失败,默认FALSE
    boolean isFail = false;
    // 初始化成绩
    int score = 0;
    Snake snake = new Snake();

    // 定时器(小蛇快慢)
    Timer timer = new Timer(500,this);

    // 食物坐标
    int foodx;
    int foody;

    // 前一个按键
    String prePressKey = "";

    Random random = new Random();

    public GamePanel(){
        // 获取焦点
        this.setFocusable(true);
        // 获取键盘的监听事件
        this.addKeyListener(this);
        timer.start();
        createFood();
    }

    /**
     * 绘制面板,
     * 绘制游戏的画面
     */
    @Override
    protected void paintComponent(Graphics g){
        // 清屏,没有这段代码就会不停闪屏
        super.paintComponent(g);
        // 绘制静态面板,背景颜色:白色
        this.setBackground(Color.WHITE);
        // 默认游戏界面
        g.fillRect(25,75,850,600);

        // 食物
        Data.food.paintIcon(this,g,foodx,foody);

        // 画积分
        g.setColor(Color.blue);
        g.setFont(new Font("微软雅黑",Font.BOLD,18));
        g.drawString("长度: ",750,35);
        g.drawString("分数: ",750,53);
        g.setColor(Color.RED);
        g.drawString(snake.length + "",800,35);
        g.drawString(score + "",800,53);

        // 画小蛇
        for (int i = 0; i < snake.length; i++) {
            if (i == 0){
                // 画脑袋
                Data.header.paintIcon(this,g,snake.snakex[i],snake.snakey[i]);
            }else {
                Data.body.paintIcon(this,g,snake.snakex[i],snake.snakey[i]);
            }
        }

        // 游戏未开始时
        if (!isStart){
            g.setColor(Color.WHITE);
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("按下空格开始游戏", 200,300);
            // 说明一下:请确保键盘的WSAD键处于英文状态下
            g.setColor(Color.GREEN);
            g.setFont(new Font("微软雅黑",Font.PLAIN,25));
            g.drawString("请确保键盘上的WSAD键处于英文状态",200,350);
            g.drawString("除了键盘上的WSAD键,还可以按up,down,left,right键",200,400);
        }

        // 游戏失败
        if (isFail){
            g.setColor(Color.RED);
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("游戏结束",300,200);
            g.setFont(new Font("微软雅黑",Font.PLAIN,30));
            g.drawString("按下空格重新开始游戏", 300,300);
            timer.setDelay(500);
        }

    }

    /**
     * Invoked when an action occurs.
     *
     * @param e
     *
     * 事件监听
     * 通过固定事件监听
     */
    @Override
    public void actionPerformed(ActionEvent e) {

        if (isStart && !isFail){
            // 如果游戏状态是开始,就让小蛇动起来
            for (int i = snake.length - 1; i > 0; i--) {
                snake.snakex[i] = snake.snakex[i - 1];
                snake.snakey[i] = snake.snakey[i - 1];
            }
            // 走向
            switch (snake.fx){
                case "D":
                    snake.snakex[0] = snake.snakex[0] + 25;
                    // 边界判断(撞墙就死)
                    if (snake.snakex[0] > 850){
                        // 这是小蛇撞墙不会死,从另一边出来
                        // snake.snakex[0] = 25;
                        // 让小蛇撞墙就game over
                        isFail = true;
                    }
                    break;
                case "A":
                    snake.snakex[0] = snake.snakex[0] - 25;
                    // 边界判断(撞墙就死)
                    if (snake.snakex[0] < 25){
                        // snake.snakex[0] = 850;
                        isFail = true;
                    }
                    break;
                case "W":
                    snake.snakey[0] = snake.snakey[0] - 25;
                    // 边界判断(撞墙就死)
                    if (snake.snakey[0] < 75){
                        // snake.snakey[0] = 650;
                        isFail = true;
                    }
                    break;
                case "S":
                    snake.snakey[0] = snake.snakey[0] + 25;
                    // 边界判断(撞墙就死)
                    if (snake.snakey[0] > 650){
                        // snake.snakey[0] = 75;
                        isFail = true;
                    }
                    break;
            }

            // 把下面的代码注释,让小蛇可以掉头,并且吃到自己的身体也不会死
            // 判断失败,撞到自己就算失败
//            for (int i = 1; i < snake.length; i++) {
//                if (snake.fx.equals("W") && prePressKey.equals("S")){
//                    isFail = false;
//                }else if (snake.fx.equals("S") && prePressKey.equals("W")){
//                    isFail = false;
//                }else if (snake.fx.equals("A") && prePressKey.equals("D")){
//                    isFail = false;
//                }else if(snake.fx.equals("D") && prePressKey.equals("A")){
//                    isFail = false;
//                }else {
//                    if (snake.snakex[0] == snake.snakex[i] && snake.snakey[0] == snake.snakey[i]) {
//                        isFail = true;
//                    }
//                }
//            }

            // 吃到食物
            if (snake.snakex[0] == foodx && snake.snakey[0] == foody){
                // 长度加1
                snake.length++;
                // 加分
                score = addCarry(score,10);

                /**
                 * 分数等级:
                 * 0 - 100:原速度      0.5s
                 * 100 - 150:-100毫秒  0.4s
                 * 150 - 200:-100毫秒  0.3s
                 * 200 - 250:-100毫秒  0.2s
                 * 250 - 300: -100毫秒  0.1s
                 * 300 - 350: -50毫秒   0.05s 最快速度了
                 */
                if (score >= 10 && score < 40){
                    timer.setDelay(400);
                }else if (score >= 40 && score < 60){
                    timer.setDelay(300);
                }else if (score >= 60 && score < 80){
                    timer.setDelay(200);
                }else if (score >= 80 && score < 100){
                    timer.setDelay(100);
                }else if (score >= 100){
                    timer.setDelay(50);
                }

                // 再次生成食物
                createFood();
            }
            // 重画页面
            repaint();
        }
        // 开启定时
        timer.start();
    }

    /**
     * 生成食物
     *  g.fillRect(25,75,850,600); 界面的位置和宽高,
     *  游戏界面坐标:25,75 ;距离左边25,距离上边75
     *  宽度:850 ; 高度: 600
     *  食物必须随机生成在游戏界面内
     *  所以:食物的x坐标 必须在25 到 850 + 25之间,
     *  如果刚好等于850 + 25那么食物就生成在游戏界面的右边所以不能等于850 + 25
     *  y坐标也是
     *  所以食物的生成坐标区间
     *  x: [25,850] 
     *  y: [75,675-25]
     */
    public void createFood(){
         /**
         * 把食物随机分配到界面上
         * random.nextInt生成随机整数
         * 850 / 25 = 34 最大值
         * 650 / 25 = 26 最大值
         */
        foodx = 25 + 25 * random.nextInt(34);
        foody = 75 + 25 * random.nextInt(26);
    }

    /**
     * 位运算实现相加
     */
    int addCarry(int a,int b){
        int result;
        int sum,carry;
        sum = a ^ b;
        carry = (a & b) << 1;
        if (carry == 0){
            result = sum;
        }else {
            result = addCarry(sum,carry);
        }
        return result;
    }


    /**
     * Invoked when a key has been typed.
     * See the class description for {@link KeyEvent} for a definition of
     * a key typed event.
     *
     * @param e
     */
    @Override
    public void keyTyped(KeyEvent e) {

    }

    /**
     * Invoked when a key has been pressed.
     * See the class description for {@link KeyEvent} for a definition of
     * a key pressed event.
     * 键盘按下
     * @param e
     */
    @Override
    public void keyPressed(KeyEvent e) {
        // 获取键盘按键是哪个
        int keyCode = e.getKeyCode();
        /**
         * 上:38
         * 下:40
         * 左:37
         * 右:39
         */
        if (keyCode == KeyEvent.VK_SPACE){
            // 按下的是空格
            if (isFail){
                // 重新开始
                isFail = false;
                snake.init();
                createFood();
                score = 0;
            }else {
                isStart = !isStart;
            }
            repaint();
        }

        // 小蛇移动
        switch (keyCode){
            case KeyEvent.VK_W:
                snake.fx = "W";
                break;
            case KeyEvent.VK_S:
                snake.fx = "S";
                break;
            case KeyEvent.VK_A:
                snake.fx = "A";
                break;
            case KeyEvent.VK_D:
                snake.fx = "D";
                break;
            case 37:
                snake.fx = "A";
                break;
            case 38:
                snake.fx = "W";
                break;
            case 39:
                snake.fx = "D";
                break;
            case 40:
                snake.fx = "S";
                break;
        }
        prePressKey = snake.fx;
    }

    /**
     * Invoked when a key has been released.
     * See the class description for {@link KeyEvent} for a definition of
     * a key released event.
     *
     * @param e
     */
    @Override
    public void keyReleased(KeyEvent e) {

    }
}

1. 游戏规则:蛇头撞墙结束游戏

2. 根据积分等级,蛇的移动速度加快,积分越高,移动速度越快

3. 键盘:WSAD 和 上下左右键 都可以控制蛇的移动方向

4. 蛇可以撞到自己的身体,可以直接反方向移动,都不会结束游戏

五 最后一个类,启动类 StartGame


import javax.swing.*;

/**
 * create by fzg
 * 2022/11/15 14:23
 */

// 游戏启动类
public class StartGame {

    public static void main(String[] args) {
        JFrame jFrame = new JFrame();
        jFrame.setBounds(500, 280,950,800);
        jFrame.setResizable(false); // 窗口大小不准拉伸
        // 正常的游戏都在面板上
        GamePanel gamePanel = new GamePanel();
        jFrame.add(gamePanel);
        jFrame.setVisible(true);
        jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

    }

}

六. 这样一个贪吃蛇小游戏就做成了



标签:25,java,snakex,snakey,小游戏,snake,贪吃蛇,new,public
From: https://www.cnblogs.com/Fantasyfzg/p/16895387.html

相关文章

  • Java实现5种负载均衡算法
    Java实现5种负载均衡算法1.轮询算法importcom.google.common.collect.Lists;importjava.util.List;importjava.util.concurrent.atomic.AtomicInteger;/***......
  • JAVA随机数的产生
     产生随机数的类和方法Randomrandom=newRandom();//Random(longseed):使用单个long类型的参数创建一个新的随机数生成器。random.nextBoolean();random.nextDoub......
  • 力扣374(java&python)-猜数字大小(简单)
    题目:猜数字游戏的规则如下:每轮游戏,我都会从 1 到 n随机选择一个数字。请你猜选出的是哪个数字。如果你猜错了,我会告诉你,你猜测的数字比我选出的数字是大了还是小了......
  • 【Java】zuul
    报错com.netflix.zuul.exception.ZuulException:HystrixReadedtimeout解决办法,zuul模块的yml配置文件增加ribbon:ConnectTimeout:5000#连接超时时间(ms)......
  • Java SpringBoot FTP 上传下载文件
    POM添加依赖<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.7</version></dependency><!--......
  • QT客户端与Java服务端进行https通信_F_hawk189_新浪博客
    网上也有一部分教程,不过我还是踩了许多的坑,所以整理了一下网上看到的和自己的一些收获。首先:,Tomcat.truststore(包含信任库),首先说明一下这个两个文件   cl......
  • 7.jenkins调用maven工具管理java代码实现滚动发布
    1.配置多jdk环境2.配置多maven环境3.配置maven项目3.1配置部署主机[root@jenkinswar_update]#cathosts_test[webservice]192.168.1.191[root@jenkinswar_......
  • Java-10接口与抽象类
    Java-10接口与抽象类抽象方法abstractmethod机制这是一个不完整的方法,它只有一个声明,没有方法体abstractvoidf();包含抽象方法的类被称为抽象类:如果一个类包含一......
  • 如何理解Java中眼花缭乱的各种并发锁?
    在互联网公司面试中,很多小伙伴都被问到过关于锁的问题。今天,我给大家一次性把Java并发锁的全家桶彻底讲明白。包括互斥锁、读写锁、重入锁、公平锁、悲观锁、自旋锁、偏向......
  • Java反应式编程(2)
    您好,我是湘王,这是我的51CTO博客,欢迎您来,欢迎您再来~看了前面对反应式编程的说明,可能很多人都会打退堂鼓了。Lambda都已经够难理解的了,又来了一个反应式编程!实在卷不动了~其实......