用JAVA写贪吃蛇游戏 超详细注释及原理讲解

snake.java
package snake;
import javax.swing.JFrame;
public class snake2 {
public static void main(String[] args) {
//框架三步骤
JFrame frame = new JFrame();
frame.setBounds(10,10,900,720);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//先建的框架 然后在框架上铺上画布 就是SnakePanel.java类
//后续的所有工作都在这个画布上画
SnakePanel panel = new SnakePanel();
frame.add(panel);
frame.setVisible(true);
}
}

SnakePanel.java
package snake2;

import java.awt.Color; // eclipse 用 shift+ctrl+O 自动添加包、类
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;

public class SnakePanel extends JPanel implements KeyListener,ActionListener{
//图片导入方法: 直接拖住 然后添加到工程的文件夹中 这个方便 但是不规范
// 可以在工程文件下建立一个专门放图片的文件夹 我的是image 这样看起来清晰 但是路径要写清楚 文件夹/图片名
ImageIcon up = new ImageIcon(“image/up.png”);
ImageIcon down = new ImageIcon(“image/down.png”);
ImageIcon right = new ImageIcon(“image/right.png”);
ImageIcon left = new ImageIcon(“image/left.png”);
ImageIcon snake = new ImageIcon(“image/snake.png”);
ImageIcon food = new ImageIcon(“image/food.png”);
ImageIcon body = new ImageIcon(“image/body.png”);

//所有要用的变量都定义在一起 最好放在方法前面 方便查找
int[] snakex = new int[750]; //放蛇的数组
int[] snakey = new int[750];
Random rand = new Random();  // 随机产生食物
int foodx = rand.nextInt(34)*25+25;  //放事物的数组
int foody = rand.nextInt(24)*25+75;
int len = 3;    // 蛇身体的长度
int score = 0;  //游戏积分
String fangxiang = "R";   //蛇头的方向

boolean isStarted = false;  //判断游戏是否开始  关系到后面的空格键事件以及 Timer是否开始
boolean isFailed = false;  //判断游戏是否失败 即蛇头是否撞到身体

//蛇移动的原理:就是不断的重画 即下面的热paint()函数 重画的频率由timer控制 单位是毫秒
Timer timer = new Timer(100,this); //控制蛇的移动的速度


public SnakePanel() {  //构造函数 即给类初始化  即运行工程后的初始状态
    this.setFocusable(true);  //选中焦点 即对画布 上的按钮起作用
    this.addKeyListener(this);  //键盘监听 即你按键盘的时候我们的程序有反映
    setup();     //重置 初始化
    timer.start();   //Timer类的启动 相当于定闹钟 然后工程开始repaint 蛇才能运动

}

public void paint(Graphics g) {  //此函数相当于一支画笔 在panel画布上画各种东西(蛇 事物 文字等)
    this.setBackground(Color.white); 
    snake.paintIcon(this, g, 25, 11);  //上面导入的图片就是用paintIcon函数来画出来 这是在画标题图片
    g.fillRect(25, 75, 850, 600);  //填写指定的矩形

    //画蛇头
    if(fangxiang.equals("R")) { //键盘监听后对蛇头方向的判断后 这这里画出
        right.paintIcon(this, g, snakex[0], snakey[0]);
    }else if(fangxiang.equals("L")) {
        left.paintIcon(this, g, snakex[0], snakey[0]);
    }else if(fangxiang.equals("U")) {
        up.paintIcon(this, g, snakex[0], snakey[0]);
    }else if(fangxiang.equals("D")) {
        down.paintIcon(this, g, snakex[0], snakey[0]);
    }

    //画蛇身体
    for(int i=1;i<len;i++) { //身体的画发就是把body.png图片画在数组中
        body.paintIcon(this, g, snakex[i], snakey[i]);
    }

    if(!isStarted) {  //数据结构中判读出没开始  就在这里画出下面的东西 即出现一行提示字
        g.setColor(Color.red);//每次画东西想要不懂的颜色 都要重新 占墨水 即重新给画笔设置颜色
        g.setFont(new Font("宋体",Font.BOLD,30)); //同样 设置画笔的字体 
        g.drawString("按空格键开始游戏!", 300, 300);//然后画出文字 后面的300 300 是指文字出现的位置
    }

    if(isFailed) {  //数据结构判断失败了 就在这里画出下面的东西 每次画东西都是基于数据的判断 即这是画表面
        g.setColor(Color.red);
        g.setFont(new Font("宋体",Font.BOLD,30));
        g.drawString("游戏失败!", 300, 300);
    }

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

    g.setColor(Color.RED);   //重置画笔 画分数表
    g.setFont(new Font("arial",Font.BOLD,10));
    g.drawString("Score:"+score, 800, 100);
    g.drawString("Length:"+len, 800, 120);

}

public void setup() { //重置初始化函数 
    len = 3;
    score = 0;
    isStarted = false;
    isFailed = false;
    fangxiang = "R";   //蛇头默认方向为右
    snakex[0] = 100;  //蛇刚出发的位置
    snakey[0] = 100;
    snakex[1] = 75;
    snakey[1] = 100;
    snakex[2] = 50;
    snakey[2] = 100;
}


//这三个函数是在实现KeyListener类时生成的函数 作用是监听键盘 我们是需要用第二 即键盘按下时的反映
@Override
public void keyTyped(KeyEvent e) {  
    // TODO Auto-generated method stub
}

@Override
public void keyPressed(KeyEvent e) { //键盘按下时 工程需要的操作 
    int keyCode = e.getKeyCode();  //获取按键的keycode值 每个按键都有不同的值
    if(keyCode == KeyEvent.VK_SPACE) {   //判断按键是否为空格键 VK_SPACE就是空格键默认的值
        if(isFailed) {
            setup();   //如果是空格键且游戏结束了 就调用这个函数重置初始化
        }else {
            isStarted = !isStarted; //如果是空格键且游戏没结束了 判断变量变成相反量
        }


    }else if(keyCode == KeyEvent.VK_UP && fangxiang !="D" ) { //如果不是空格键而是方向建 则蛇头变成相应的方向
        fangxiang = "U";
    }else if(keyCode == KeyEvent.VK_DOWN && fangxiang !="U") {
        fangxiang  = "D";
    }else if(keyCode == KeyEvent.VK_RIGHT && fangxiang !="L") {
        fangxiang = "R";
    }else if(keyCode == KeyEvent.VK_LEFT && fangxiang !="R") {
        fangxiang = "L";
    }
}

@Override
public void keyReleased(KeyEvent e) {
    // TODO Auto-generated method stub
}

@Override
public void actionPerformed(ActionEvent e) { //这个函数是在实现ActionListener类时生成的功能 当事件发生时,当事件发生时调用
    //再定1个闹钟   事件开始是以闹钟响起开始 即 开始paint
    timer.start();

    //先移动数据 只有数据发生变化了 画笔才能画出相应的画
    if(isStarted && !isFailed) {
        for(int i=len;i>0;i--) { //蛇身体的移动 数据的变化 真正的移动在下面的repaint中实现
            snakex[i] = snakex[i-1];
            snakey[i] = snakey[i-1];
        }

    if(fangxiang.equals("R")) {  //蛇头的移动 数据的变化 真正的变化在下面的repaint 中的实现
        snakex[0] = snakex[0]+25;
        if(snakex[0]>850) snakex[0] = 25;  //四个if语句 判断蛇头是否超出画布 
    }else if(fangxiang.equals("L")) {
        snakex[0] = snakex[0]-25;
        if(snakex[0]<25) snakex[0] = 850;
    }else if(fangxiang.equals("U")) {
        snakey[0] = snakey[0]-25;
        if(snakey[0]<75) snakey[0] = 650;
    }else if(fangxiang.equals("D")) {
        snakey[0] = snakey[0]+25;
        if(snakey[0]>650) snakey[0] = 75;
    }

        if(snakex[0] == foodx && snakey[0] == foody) {  //判断蛇是否吃到食物
            len++;
            score++;
            foodx = rand.nextInt(34)*25+25;//食物出现的 x y坐标 在画布内
            foody = rand.nextInt(24)*25+75;
        }

        for(int i=1;i<len;i++) {  //判断蛇头是否撞到蛇身体
            if(snakex[0]==snakex[i] && snakey[0]==snakey[i]) {
                isFailed = true; //如果撞到 判断游戏失败的变量变为真 即游戏失败
            }       
        }
    }
    //重画
    repaint();  
}

}
开始界面

结束界面

总结
1.所有的运动 图片都是基于数据的变换
2. 简单的操作就是数据操作变换个可视化呈现分开函数
3. 细节的推敲非常重要

  • 12
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
A: 下面是一个简单的贪吃蛇游戏Java代码注释中有详细解释。这个代码实现了一个基础的轮廓,你可以根据自己的需要添加更多的功能。 ```java import javax.swing.*; import java.awt.*; import java.awt.event.*; public class SnakeGame extends JFrame implements KeyListener { // 游戏面板的大小 private static final int WIDTH = 20, HEIGHT = 20; // 蛇的长度、蛇的位置、食物的位置 private int length; private int[] snakeX = new int[WIDTH * HEIGHT]; private int[] snakeY = new int[WIDTH * HEIGHT]; private int foodX; private int foodY; // 方向 private boolean up, down, left, right; // 游戏是否进行中 private boolean isPlaying; public SnakeGame() { // 初始化 isPlaying = true; length = 3; up = false; down = false; left = false; right = true; // 设置窗口属性 setTitle("贪吃蛇小游戏"); setSize(500, 500); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); // 添加键盘监听器 addKeyListener(this); // 设置画布属性 JPanel gamePanel = new JPanel(); gamePanel.setPreferredSize(new Dimension(500, 500)); gamePanel.setBackground(Color.gray); add(gamePanel); // 设置食物的位置 generateFood(); // 开始游戏 playGame(gamePanel); } // 绘制游戏面板 public void drawGamePanel(Graphics g) { // 绘制蛇的头部和身体 for (int i = 0; i < length; i++) { if (i == 0) { g.setColor(Color.red); g.fillRect(snakeX[i] * 20, snakeY[i] * 20, 20, 20); } else { g.setColor(Color.green); g.fillRect(snakeX[i] * 20, snakeY[i] * 20, 20, 20); } } // 绘制食物 g.setColor(Color.yellow); g.fillRect(foodX * 20, foodY * 20, 20, 20); } // 生成食物 public void generateFood() { foodX = (int) (Math.random() * WIDTH); foodY = (int) (Math.random() * HEIGHT); } // 游戏逻辑 public void playGame(JPanel gamePanel) { while (isPlaying) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // 移动蛇的身体 for (int i = length; i > 0; i--) { snakeX[i] = snakeX[i - 1]; snakeY[i] = snakeY[i - 1]; } // 移动蛇的头部 if (up) snakeY[0]--; if (down) snakeY[0]++; if (left) snakeX[0]--; if (right) snakeX[0]++; // 判断是否吃到食物 if (snakeX[0] == foodX && snakeY[0] == foodY) { length++; generateFood(); } // 判断是否撞墙或撞到自己 if (snakeX[0] < 0 || snakeX[0] >= WIDTH || snakeY[0] < 0 || snakeY[0] >= HEIGHT) { isPlaying = false; JOptionPane.showMessageDialog(gamePanel, "游戏结束!"); } for (int i = 1; i < length; i++) { if (snakeX[i] == snakeX[0] && snakeY[i] == snakeY[0]) { isPlaying = false; JOptionPane.showMessageDialog(gamePanel, "游戏结束!"); } } // 重新绘制游戏面板 gamePanel.repaint(); } } // 键盘事件 @Override public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_UP && !down) { up = true; right = false; left = false; } if (key == KeyEvent.VK_DOWN && !up) { down = true; right = false; left = false; } if (key == KeyEvent.VK_LEFT && !right) { left = true; up = false; down = false; } if (key == KeyEvent.VK_RIGHT && !left) { right = true; up = false; down = false; } } @Override public void keyReleased(KeyEvent e) { } @Override public void keyTyped(KeyEvent e) { } public static void main(String[] args) { new SnakeGame().setVisible(true); } } ```
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值