20.贪吃蛇,GUI最后的项目

总结:

  1. 1.定义数据
    2.画上去
    3.监听事件
    键盘事件
    (动作)事件
  2. 把一个大项目分成很多个小项目,依次完成小项目即可,掳清思虑路!
package snake;

import javax.swing.*;

// 主界面,         正常的游戏界面都应该在面板上
public class StartGame {
    public static void main(String[] args) {
        JFrame frame = new JFrame();


        frame.add(new GamePanel());  // 添加面板,//正常的游戏界面都应该在面板上

        frame.setVisible(true);
        frame.setBounds(10,10,900,720);
        frame.setResizable(false);  // 窗口大小不可以改变
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

    }
}
package snake;

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;
/*1.定义数据
2.画上去
3.监听事件
   键盘事件
   (动作)事件

 */

//游戏的面板  正常的游戏界面都应该在面板上
public class GamePanel extends JPanel implements KeyListener, ActionListener {

    //定义蛇的数据结构
    int length;//蛇的长度(包括蛇头)
    int[] snakeX = new int[600];//蛇的X坐标,包括蛇头和每一截身体
    int[] snakeY = new int[500];//蛇的Y坐标
    String direction;   // 蛇头的方向(上下左右)
    boolean isStart;  // 游戏状态默认为false未启动
    boolean isFail;   // 开启游戏后默认为游戏尚未失败
    Timer timer=new Timer(100,this); //每100ms执行一次,监听对象为自己this,需要用到ActionListener

    // 食物坐标
    int foodx,foody;
    Random random=new Random();

    // 分数
    int scroe;

    // 构造器,初始化
    public GamePanel() {
        init();
        setFocusable(true);  // 获得焦点事件,,, 不理解为什么要聚焦才行
        addKeyListener(this);  // 添加键盘监听器,为本身
        timer.start(); //计时器启动

    }

    // 初始化
    public void init() {
        isStart = false;  // 游戏状态默认为false
        isFail = false;
        scroe=0;
        direction = "R";   // 小蛇初始方向朝右
        length = 3;       // 小蛇的长度
        snakeX[0] = 100;snakeY[0] = 100; // 蛇头的初始坐标
        snakeX[1] = 75; snakeY[1] = 100;  // 第一段蛇身的坐标,25一小格
        snakeX[2] = 50; snakeY[2] = 100;  // 第二段蛇身的坐标
        // 随机生成食物
        foodx=25+25* random.nextInt(34); //横坐标有34个格子,800/25=34
        foody=75+25* random.nextInt(24); // 600/25=24
    }

    //绘制面板,我们游戏中的所有东西,都是用这个画笔来画的
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);//清屏
        //绘制静态的面板
        setBackground(Color.white);
        Data.header.paintIcon(this, g, 25, 11);//头部广告栏放上去
        g.fillRect(25, 75, 850, 600);//默认的游戏界面 默认为黑色,为一个黑色的矩形
        // 长度和积分
        g.setColor(Color.blue);
        g.setFont(new Font("微软雅黑", Font.BOLD, 20));
        g.drawString("长度: "+length,750,30);
        g.drawString("积分: "+scroe,750,50);


        // 画出snake

        // 画出蛇头的方向
        switch (direction) {
            case "U": Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);break;  // 蛇头向上,记得break;
            case "D": Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);break;  // 蛇头向下
            case "L": Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);break; // 蛇头向左
            case "R": Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);break; // 蛇头向右
        }
        // 画出食物
        Data.food.paintIcon(this,g,foodx,foody);

        //画出蛇身
        for (int i = 1; i < length; i++) {
            Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);  // 动态地画出蛇身
        }

        //游戏状态
        if (isStart == false) {  // 是否启动
            g.setColor(Color.white);
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("按下空格开始游戏", 300, 350);
        }
        if (isFail == true) {  // 是否失败
            g.setColor(Color.red);
            g.setFont(new Font("微软雅黑", Font.BOLD, 40));
            g.drawString("游戏失败,按下空格重新开始游戏", 200, 350);
        }

    }

//键盘监听事件
    @Override
    public void keyPressed(KeyEvent e) {


        int keyCode = e.getKeyCode();//获得键盘事件是哪一个
        if(keyCode == KeyEvent.VK_SPACE  ){//如果按下的是空格键
            if (isFail==true){
                // 重新开始
                init();
            }else {
                isStart = !isStart;//取反 否则isStart就失效了}
            }
            repaint();
        }
        //键盘移动
        if(keyCode == KeyEvent.VK_UP){
            direction = "U";
        }else if(keyCode == KeyEvent.VK_DOWN){
            direction = "D";
        }else if(keyCode == KeyEvent.VK_LEFT){
            direction = "L";
        }else if(keyCode == KeyEvent.VK_RIGHT){
            direction = "R";
        }
    }


    public void actionPerformed(ActionEvent e) {
        if(isStart && isFail==false){


            // 小蛇吃到食物
            if (snakeX[0]==foodx && snakeY[0]==foody){
                length++; // 长度加一
                scroe+=10; // 分数加10
                // 重新随机生成食物
                foodx=25+25* random.nextInt(34); //横坐标有34个格子,800/25=34
                foody=75+25* random.nextInt(24); // 600/25=24

            }

            // 蛇身移动
            for (int i = length - 1; i > 0 ; i--) {//后一节移到前一节的位置 snakeX[1] = snake[0];
                snakeX[i] = snakeX[i-1];
                snakeY[i] = snakeY[i-1];
            }

            //控制小蛇上下左右移动
            if(direction.equals("R")){
                snakeX[0] = snakeX[0] + 25;
                if(snakeX[0] > 850){ snakeX[0] = 25; }//边界判断
            }else if(direction.equals("L")){
                snakeX[0] = snakeX[0] - 25;
                if(snakeX[0] < 25){ snakeX[0] = 850; }//边界判断
            }else if(direction.equals("U")){
                snakeY[0] = snakeY[0] - 25;
                if(snakeY[0] < 75){ snakeY[0] = 650; }//边界判断
            }else if(direction.equals("D")){
                snakeY[0] = snakeY[0] + 25;
                if(snakeY[0] > 650){ snakeY[0] = 75; }//边界判断
            }

            // 判断是否失败,且要放在最后(美观直观),why?
            for (int i = 1; i <length ; i++) {
                if (snakeX[0]==snakeX[i] && snakeY[0]==snakeY[i]){
                    isFail=true;
                }
            }

            repaint();//重画页面
        }
        timer.start();//定时器开启

    }

    @Override
    public void keyTyped (KeyEvent e){}   // 直接实现接口,最为暴力
    @Override
    public void keyReleased (KeyEvent e){}

}

package snake;

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

public class Data {
    // 相对路径  picture.png
    // 绝对路径: / 相当于当前的项目
    // 获得头部的素材
    public static URL headerURL = Data.class.getResource("statics/header.png");
    public static ImageIcon header = new ImageIcon(headerURL);
    //上下左右
    public static URL upURL = Data.class.getResource("statics/up.png");
    public static ImageIcon up = new ImageIcon(upURL);
    public static URL downURL = Data.class.getResource("statics/down.png");
    public static ImageIcon down = new ImageIcon(downURL);
    public static URL leftURL = Data.class.getResource("statics/left.png");
    public static ImageIcon left = new ImageIcon(leftURL);
    public static URL rightURL = Data.class.getResource("statics/right.png");
    public static ImageIcon right = new ImageIcon(rightURL);
    //身体还有食物
    public static URL bodyURL = Data.class.getResource("statics/body.png");
    public static ImageIcon body = new ImageIcon(bodyURL);
    public static URL foodURL = Data.class.getResource("statics/food.png");
    public static ImageIcon food = new ImageIcon(foodURL);
}

**GamePanel重置优化版本**

package snake;

import javax.swing.;
import java.awt.
;
import java.awt.event.;
import java.util.Random;
//游戏的面板 正常的游戏界面都应该在面板上
public class GamePanel extends JPanel implements ActionListener{
// 游戏的状态
boolean isStart,isFail;
Timer timer=new Timer(100,this);
// 小蛇的数据
int length;
int scroe;
String direction;
int snakeX[]=new int[816] ;// 34
14=816
int snakeY[]=new int[816]; // 34*14=816,小蛇最大的长度
// 食物
int foodX,foodY; // 食物的坐标

public GamePanel() {
    init();
    timer.start();
    setFocusable(true);  // 获得焦点事件,,, 不理解为什么要聚焦才行,只有聚焦才能进行互动
    // 使用匿名内部类进行代码优化,都是监听自己本身
    addKeyListener(new KeyAdapter() {
        public void keyPressed(KeyEvent e) {
            int keycode=e.getKeyCode();
            if (keycode==KeyEvent.VK_SPACE){
                if (isFail==true){
                    init();
                }
                isStart=!isStart;
                repaint();
            } else if (keycode==KeyEvent.VK_UP) {
                direction="U";
            } else if (keycode==KeyEvent.VK_DOWN) {
                direction="D";
            } else if (keycode==KeyEvent.VK_LEFT) {
                direction="L";
            } else if (keycode==KeyEvent.VK_RIGHT) {
                direction="R";
            }
        }
    });
}
//初始化
public  void init(){
    length=3;scroe=0;isStart=false;isFail=false;
    direction="R";
    snakeX[0]=snakeY[0]=100;   //蛇头初始化坐标
    snakeX[1]=75;snakeY[1]=100;   //第一节蛇身初始化坐标
    snakeX[2]=50;snakeY[2]=100;   // 第二节蛇身初始化坐标
    foodX=25+25*new Random().nextInt(34);
    foodY=75+25*new Random().nextInt(24);   // 随机的食物
}

//绘制面板,我们游戏中的所有东西,都是用这个画笔来画的
@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);//清屏
    //绘制静态的面板
    setBackground(Color.white);
    Data.header.paintIcon(this, g, 25, 10);
    g.drawString("长度: "+length,750,30);
    g.drawString("分数: "+scroe,750,50);
    g.fillRect(25, 75, 850, 600);
    if (isStart==false){
        g.setColor(Color.green);
        g.setFont(new Font("微软雅黑", Font.BOLD, 40));
        g.drawString("按下空格开始游戏", 200, 350);
    }
    if (isFail==true){
        g.setColor(Color.red);
        g.setFont(new Font("微软雅黑", Font.BOLD, 40));
        g.drawString("失败,按下空格重新开始游戏", 200, 350);
    }

/////////////////////////////////////////////////////////////////////////////////
// 画蛇
if (direction.equals("U")){
Data.up.paintIcon(this,g,snakeX[0],snakeY[0]);
} else if (direction.equals("D")) {
Data.down.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if (direction.equals("L")) {
Data.left.paintIcon(this,g,snakeX[0],snakeY[0]);
}else if (direction.equals("R")) {
Data.right.paintIcon(this,g,snakeX[0],snakeY[0]);
}
for (int i = 1; i < length; i++) {
Data.body.paintIcon(this,g,snakeX[i],snakeY[i]);
}
/////////////////////////////////////////////////////////////////////////////////
Data.food.paintIcon(this,g,foodX,foodY);// 画出食物
}

@Override
public void actionPerformed(ActionEvent e) {
    if (isStart==true && isFail==false){  // 处于启动和不失败的状态下不断进行游戏

//////////////////////////////////////////////////////////////////////////////////////////
// 移动的小蛇
//先执行身体的移动再执行头部的移动,不然第一节身体会与头部重叠
for (int i = length-1; i >0; i--) {
snakeX[i]=snakeX[i-1];
snakeY[i]=snakeY[i-1];
} // 身体移动
// 头部的移动
if (direction.equals("U")){
snakeY[0]-=25;
if (snakeY[0]<75) snakeY[0]=650;
} else if (direction.equals("D")) {
snakeY[0]+=25;
if (snakeY[0]>650 ) snakeY[0]=75;
}else if (direction.equals("L")) {
snakeX[0]-=25;
if (snakeX[0]<25) snakeX[0]=850;
}else if (direction.equals("R")) {
snakeX[0]+=25;
if (snakeX[0]>850) snakeX[0]=25;
}
//////////////////////////////////////////////////////////////////////////////
//小蛇吃到食物的情况
if (snakeX[0]foodX&&snakeY[0]foodY){
length++;
scroe+=10;
foodX=25+25new Random().nextInt(34);
foodY=75+25
new Random().nextInt(24); // 重新生成随机的食物
repaint();
}
// 游戏失败的判定
/*判断是否失败,且要放在最后(美观直观),why?
第二次独立完成后,要是直接原地掉头的话,就会首先使得第二节身体与头部重叠,此时直观明了,并且没有下一次的运动
要是放在开头的话,此时虽然isFail=true,但是还会继续执行接下来的移动操作,就不好看了
*/
for (int i = 1; i <length ; i++) {
if (snakeX[0]snakeX[i] && snakeY[0]snakeY[i]){
isFail=true;
}
}

    }
    repaint();
    timer.start();
}

}

posted @   被占用的小海海  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示