使用GUI编程,代码实现贪吃蛇小游戏Code_demo分享

在开始着手去做这个游戏之前,首先我们需要知道,我们看到的动态图画是一张张图片 然后通过一帧帧拼凑出来的。

什么叫做帧 ? 帧率的概念

那什么叫做帧呢?
帧数就是在1秒钟时间里传输的图片的帧数,也可以理解为图形处理器每秒钟能够刷新几次,它的单位通常用FPS(Frames Per Second)表示

打比方 例如用帧率这个量词来为字画,就可以称其为 一帧,
每一帧都是静止的图象,快速连续地显示帧便形成了运动的假象。高的帧率可以得到更流畅、更逼真的动画。每秒钟帧数 (fps) 愈多,所显示的动作就会愈流畅。

  1. 那我们人类的眼睛视觉暂留现象是每秒24帧,所以理论上大于24帧就可以视为流畅。
  2. 但是由于游戏画面的变化速率很高,所以一般是越高越好。如果开启垂直同步,帧数一般应维持在60左右。
  3. 游戏的选项中一般会有“显示帧数”这一选项的,勾选之后,回到游戏画面,应该可以在四角之一看到一个FPS值,这就是当前的帧数。
前期准备

现在我们有了帧率的概念,有了帧率的概念,我们可以脑补到贪吃蛇的游戏界面是怎么制作成动态的了。那还需要键盘监听的事件,因为我们需要用键盘来进行游戏的控制,然后还需要一个定时器 Timer


1. 游戏界面绘制

先进行一波分析,界面绘制,我们会需要一个启动类,用来启动程序,然后肯定需要一个窗口容器的,窗口容器中,我们需要放进去我们的图片素材,素材要放在面板上,把面板在放进容器中,这样我们的界面大体就能绘制出来了。

经思路分析了之后,我们来具体实现一下。

1.1 启动类
package com.Gluttonous_Snake;

import javax.swing.*;

public class GameStart {
    public static void main(String[] args) {
        JFrame jFrame = new JFrame("兔C-贪吃蛇");

        jFrame.setVisible(true); //可见性
        jFrame.setResizable(false); //不可拉伸
        jFrame.setBounds(220,200,900,720); //窗口弹出位置及大小

        //将面板添加进容器当中
        jFrame.add(new GamePanel());

        jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); //关闭事件
    }
}
1.2 素材数据准备

有了窗口容器,我们就可以设置面板了,把面板添加到容器当中,我们就可以实现贪吃蛇的第一步,但是再此之前,还需要先将准备的素材给设置好。
在这里插入图片描述
上面的图片 是我素材的存储,
然后现在需要创建一个类,将素材全部存进类里,然后面板需要素材的时候,通过调用这个类就能获取到里面的内容。

package com.Gluttonous_Snake;

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

//贪吃蛇数据存储中心
public class GameData {



    public static URL headerURL = GameData.class.getResource("statics/header.png");
    public static Icon header = new ImageIcon(headerURL);


    public static URL upURL = GameData.class.getResource("statics/up.png");
    public static URL downURL = GameData.class.getResource("statics/down.png");
    public static URL leftURL = GameData.class.getResource("statics/left.png");
    public static URL rightURL = GameData.class.getResource("statics/right.png");
    public static Icon up = new ImageIcon(upURL);
    public static Icon down = new ImageIcon(downURL);
    public static Icon left = new ImageIcon(leftURL);
    public static Icon right = new ImageIcon(rightURL);

    public static URL bodyURL = GameData.class.getResource("statics/body.png");
    public static Icon body = new ImageIcon(bodyURL);

    public static URL foodURL = GameData.class.getResource("statics/food.png");
    public static Icon food = new ImageIcon(foodURL);
}

1.3 面板设置

好了,现在有了容器,有了对素材的封装准备,我们可以设置面板了。

package com.Gluttonous_Snake;

import javax.swing.*;
import java.awt.*;

public class GamePanel extends JPanel {


    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); //不删此行,可实现屏幕清屏刷作用

        //准备好图片后,开始绘制静态的面板
        this.setBackground(Color.WHITE);
        GameData.header.paintIcon(this,g,25,11); //绘制广告栏

        g.fillRect(25,75,850,600); //绘制默认游戏界面
    }
}

1.4 效果

第一步做完了之后,我们来看一下效果
在这里插入图片描述
一个静态的界面我们准备完成了,现在马上开始第二步的下一阶段准备。
但是再次之前,需要注意图片的获取,路径的存放要规避好,是相对路径的获取方法,还是绝对路径的获取。不然会报 找不到图片资源的。

2. 绘制静态的小蛇

绘制静态的小蛇,我们知道小蛇的长度不是恒常不变的,所以可以借助数据结构来进行设置,我们用数组来存储静态的小蛇,而且还要给小蛇设置初始化值,那初始化值就会涉及到小蛇的初始长度,还有坐标位置。

具体操作我们还是先来看一下代码怎么写

//小蛇需要画在面板上,所以关于小蛇的代码 写在了面板类里
package com.Gluttonous_Snake;

import sun.security.util.LegacyAlgorithmConstraints;

import javax.swing.*;
import java.awt.*;

public class GamePanel extends JPanel {

    //定义蛇的数据结构
    int length; //蛇的长度
    int[] snakeX = new int[600]; //蛇的 x 坐标 25*25
    int[] snakeY = new int[500]; //蛇的 Y 坐标 25*25
    String fx;
    boolean flag = false;


    //利用构造器调用初始化方法
    public GamePanel(){
        init();
    }

    //定义初始化方法,设置小蛇的初始化值
    public void init(){
        length = 3;
        snakeX[0] =100; snakeY[0] =100; //小蛇脑袋的坐标
        snakeX[1] =75; snakeY[1] =100; //小蛇第一节身体坐标
        snakeX[2] =50; snakeY[2] =100; //小蛇第二节身体坐标
        fx = "R";
    }


    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); //不删此行,可实现屏幕清屏刷作用

        //准备好图片后,开始绘制静态的面板
        this.setBackground(Color.WHITE);
        GameData.header.paintIcon(this,g,25,11); //绘制广告栏
        g.fillRect(25,75,850,600); //绘制默认游戏界面

        //把小蛇画到面板上

        if (fx.equals("R")){
            GameData.right.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("L")){
            GameData.left.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("U")){
            GameData.up.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("D")){
            GameData.down.paintIcon(this,g,snakeX[0],snakeY[0]);
        }


        for (int i = 1; i < length; i++) {
            GameData.body.paintIcon(this,g,snakeX[i],snakeY[i]);
        }

        if (flag == false){
            g.setColor(Color.white);
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("按下空格开始游戏!",300,300);
        }

    }
}

我们在来梳理一下流程,在原来的游戏面板上,加入小蛇的相关代码。借助数组的数据结构对小蛇进行设置。
先设置小蛇长度的初始化变量,然后是坐标位置。
在初始化方法当中,设置小蛇的初始化长度,还有小蛇的第一节身体,和第二节身体,因为小蛇的默认长度是3节(加上脑袋)。设置好了小蛇长度的初始化方法,利用构造器进行调用,因为我们知道,对象的初始化值是通过构造器来赋值的。
紧接着,在到绘画方法当中,将小蛇画出来,因为小蛇的默认移动方向是向右的,所以又加了一个控制方向的变量,然后进行判断,如果是向右的那头向右,反正向左,,向下,,或者是上。

设置好了小蛇的相关内容,我们还需要一个游戏暂停或者开始的判断状态,如果是暂停的,就在游戏界面上展示相关提示,所以加了一个flag,然后进行判断,将提示游戏画到了游戏界面上。

对上述代码进行了总结,我们在看一下现在的代码状态,展示效果图::
在这里插入图片描述

3. 让游戏界面的提示信息可控

顾名思义,就是点击空格的时候,提示的按下空格开始游戏的字体会消失,在按会出现。

这个操作很简单,实现监听接口,重写个方法,最后在初始化方法中加个焦点事件和监听事件就好了。看Code,对喽,就是继续在面板中设置就好了。

package com.Gluttonous_Snake;


import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class GamePanel extends JPanel implements KeyListener {

    //定义蛇的数据结构
    int length; //蛇的长度
    int[] snakeX = new int[600]; //蛇的 x 坐标 25*25
    int[] snakeY = new int[500]; //蛇的 Y 坐标 25*25
    String fx;
    boolean flag = false;


    //利用构造器调用初始化方法
    public GamePanel(){
        init();
        //获得焦点和键盘事件
        this.setFocusable(true);
        this.addKeyListener(this);
    }

    //定义初始化方法,设置小蛇的初始化值
    public void init(){
        length = 3;
        snakeX[0] =100; snakeY[0] =100; //小蛇脑袋的坐标
        snakeX[1] =75; snakeY[1] =100; //小蛇第一节身体坐标
        snakeX[2] =50; snakeY[2] =100; //小蛇第二节身体坐标
        fx = "R";
    }


    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); //不删此行,可实现屏幕清屏刷作用

        //准备好图片后,开始绘制静态的面板
        this.setBackground(Color.WHITE);
        GameData.header.paintIcon(this,g,25,11); //绘制广告栏
        g.fillRect(25,75,850,600); //绘制默认游戏界面

        //把小蛇画到面板上

        if (fx.equals("R")){
            GameData.right.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("L")){
            GameData.left.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("U")){
            GameData.up.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("D")){
            GameData.down.paintIcon(this,g,snakeX[0],snakeY[0]);
        }


        for (int i = 1; i < length; i++) {
            GameData.body.paintIcon(this,g,snakeX[i],snakeY[i]);
        }

        if (flag == false){
            g.setColor(Color.white);
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("按下空格开始游戏!",300,300);
        }

    }

    //键盘监听事件
    @Override
    public void keyPressed(KeyEvent e) {
	  int keyCode = e.getKeyCode();
	   if (keyCode == KeyEvent.VK_SPACE){
	      flag = !flag; //取反值,如果直接取值会写死的
	      repaint();
	   }
    }


    @Override
    public void keyTyped(KeyEvent e) {
        
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
}

我们这里就把这个思路梳理清楚就好了,效果图展示与不展示都没有什么意义,无非就是按下空格 游戏界面上没有了提示的字样,在按一下 字样就显示出来了,这里还没有设置别的东西,没有必要展示出来的意义。

3.1 踩到雷了!

这里我遇到了一个问题,如果朋友们是按照我这个博文思路也会遇到这个问题,这个问题就是:当我们的代码开发到这一步的时候,按下空格 看效果的时候,发现游戏界面上的提示信息并没有受控,按爆了空格也没好用。

开始我以为是监听不到,单独定义了监听类,然后在面板中添加监听事件的时候,new 了一下自己写的监听类,结果还是不好用。

其实这个问题是因为焦点事件没有成功,在不改代码的情况下,按一下table键 然后在进行操作也有用。其实根本的问题是因为在启动类中设置的窗口可见性,写早了,应该放在代码的最后,这个不用太过思索,窗口可见性 程序走完在可见很合乎逻辑的啊,没走完就可见了,代码里面的优先级问题牵扯到,肯定会有部分功能实现不出来的。毕竟没可见的。把窗口可见性放到最后一行,即 setVisible(true);
.

4. 实现可以让键盘控制小蛇的移动
4.1 先让小蛇动起来

这里我们需要添加一个事件监听,然后这个监听需要通过定时器来进行辅助,之后的效果就是让其根据固定的时间来进行刷新。

所以:
这里我们就需要在面板类中添加个全局变量—> Time;这个对象会有两个参数,第一个参数是设置的毫秒数,第二个参数是要监听的对象。
有了这个定时器对象,我们在用对象名 调用它的start 开启方法,就可以开启一个定时器了。

在下一步,需要在新添加的监听事件方法里写逻辑了。
就是判断监听器是否是开启的状态,如果是开启的状态,就让小蛇动起来。
我们先来看一下代码吧

package com.Gluttonous_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;

public class GamePanel extends JPanel implements KeyListener, ActionListener {

    //定义蛇的数据结构
    int length; //蛇的长度
    int[] snakeX = new int[600]; //蛇的 x 坐标 25*25
    int[] snakeY = new int[500]; //蛇的 Y 坐标 25*25
    String fx;
    boolean flag = false;

    //定时器 以毫秒为单位,1000ms =1s
    Timer timer = new Timer(100,this); //100毫秒执行一次定时器


    //利用构造器调用初始化方法
    public GamePanel(){
        init();
        //获得焦点和键盘事件
        this.setFocusable(true);
        this.addKeyListener(this);
        timer.start(); //开启定时器
    }

    //定义初始化方法,设置小蛇的初始化值
    public void init(){
        length = 3;
        snakeX[0] =100; snakeY[0] =100; //小蛇脑袋的坐标
        snakeX[1] =75; snakeY[1] =100; //小蛇第一节身体坐标
        snakeX[2] =50; snakeY[2] =100; //小蛇第二节身体坐标
        fx = "R";

    }


    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); //不删此行,可实现屏幕清屏刷作用

        //准备好图片后,开始绘制静态的面板
        this.setBackground(Color.WHITE);
        GameData.header.paintIcon(this,g,25,11); //绘制广告栏
        g.fillRect(25,75,850,600); //绘制默认游戏界面

        //把小蛇画到面板上

        if (fx.equals("R")){
            GameData.right.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("L")){
            GameData.left.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("U")){
            GameData.up.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("D")){
            GameData.down.paintIcon(this,g,snakeX[0],snakeY[0]);
        }


        for (int i = 1; i < length; i++) {
            GameData.body.paintIcon(this,g,snakeX[i],snakeY[i]);
        }

        if (flag == false){
            g.setColor(Color.white);
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("按下空格开始游戏!",300,300);
        }

    }


    @Override
    public void actionPerformed(ActionEvent e) {
        if (flag){ //如果游戏是开始状态,让小蛇开始动起来
            //右移
            for (int i = length-1; i >0 ; i--) {
                snakeX[i] = snakeX[i-1];
                snakeY[i] = snakeY[i-1];
            }
            snakeX[0] = snakeX[0]+25;

            //加边界判断,为防止小蛇一去不复返
            if (snakeX[0] >850){
                snakeX[0] = 25; //重新赋值,让其归位
            }

            repaint(); //重画界面
        }
        timer.start(); //定时器开始!
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        if (keyCode == KeyEvent.VK_SPACE){
            flag = (!flag); //取反值,如果直接取值会写死的
            repaint();
        }
    }


    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
}

嚯 ~ 整个面板对象里的代码 全贴了一遍。 有点乱奥,但是全贴出来 好梳理,我怕文章的节奏会乱。
来梳理一下:

  1. 先实现了一个监听接口 ActionLisenter 然后重写了一个 actionPerformed。是因为我们要在监听的基础下添加定时器,然后来刷新小蛇的动作。
  2. 第二步就是添加了定时器Timer对象,给了两个参数,第一个参数是毫秒数,第二个是监听的对象。
  3. 有了定时器,我们就可以对事件进行监听了,在监听中判断定时器是否是已经开启的状态,如果为开启状态,就循环控制小蛇的向右移动,重画界面,开启定时器。
  4. 因为一开始的时候也需要让它动起来。所以在构造器中也开启了一下定时器,就是游戏一开始的时候就让定时器启动。

这里先不放效果图了,一条蛇无限循环的一直向右来回的跑,没意义。现在小蛇是能动了。所以我们赶快进行下一步,让键盘操控小蛇上下左右进行移动。

4.2 让键盘操控小蛇进行上下左右移动

我们需要现在键盘监听中 监听键盘的↑↓←→按键,然后给之前方向的变量重新赋值即可。

 if (keyCode == KeyEvent.VK_UP){
            fx ="U";
        }else if (keyCode == KeyEvent.VK_DOWN){
            fx = "D";
        }else if (keyCode == KeyEvent.VK_LEFT){
            fx = "L";
        }else if (keyCode == KeyEvent.VK_RIGHT){
            fx = "R";
        }
    }   

加上了新的按键监听事件,然后还需要在小蛇的走向控制中设置方向改变后的走向。 就是修改一下监听事件方法里的逻辑。

 @Override
    public void actionPerformed(ActionEvent e) {
        if (flag){ //如果游戏是开始状态,让小蛇开始动起来
            //右移
            for (int i = length-1; i >0 ; i--) {
                snakeX[i] = snakeX[i-1];
                snakeY[i] = snakeY[i-1];
            }

            if (fx.equals("R")){
                snakeX[0] = snakeX[0]+25;
                //加边界判断,为防止小蛇一去不复返
                if (snakeX[0] >850){snakeX[0] = 25;}
            }else if (fx.equals("L")){
                snakeX[0] = snakeX[0]-25;
                if (snakeX[0] <25){snakeX[0] =850;}
            }else if (fx.equals("U")){
                snakeY[0] = snakeY[0]-25;
                if (snakeY[0] <75){snakeX[0] =650;}
            }else if(fx.equals("D")){
                snakeY[0] = snakeY[0]+25;
                if (snakeY[0] >650){snakeX[0] =75;}
            }


            repaint(); //重画界面
        }
        timer.start(); //定时器开始!
    }

现在的小蛇就可以进行上下左右的按键控制移动了

5. 小蛇吃了食物以后可以长大

…我还没把文章叙述出来,写完忍不住…就先玩起来了。
在这里插入图片描述
很简单,先定义事物的坐标,x y 坐标都要有,然后创建一个随机数对象,因为食物是在随机的位置上出来的,之后将计算好的食物坐标画到面板上,然后在监听事件里加个判断,如果小蛇的坐标和食物的坐标重合,那小蛇的长度就加1。

看下面的代码自己梳理一下目前为止的逻辑::

package com.Gluttonous_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;

public class GamePanel extends JPanel implements KeyListener, ActionListener {

    //定义蛇的数据结构
    int length; //蛇的长度
    int[] snakeX = new int[600]; //蛇的 x 坐标 25*25
    int[] snakeY = new int[500]; //蛇的 Y 坐标 25*25
    String fx;

    //小蛇的食物设置
    int foodx;
    int foody;
    Random random = new Random();

    boolean flag = false;

    //定时器 以毫秒为单位,1000ms =1s
    Timer timer = new Timer(100,this); //100毫秒执行一次定时器


    //利用构造器调用初始化方法
    public GamePanel(){
        init();
        //获得焦点和键盘事件
        this.setFocusable(true);
        this.addKeyListener(this);
        timer.start(); //开启定时器
    }

    //定义初始化方法,设置小蛇的初始化值
    public void init(){
        length = 3;
        snakeX[0] =100; snakeY[0] =100; //小蛇脑袋的坐标
        snakeX[1] =75; snakeY[1] =100; //小蛇第一节身体坐标
        snakeX[2] =50; snakeY[2] =100; //小蛇第二节身体坐标
        fx = "R";

        //将食物分布在界面上
        foodx = 25 + 25*random.nextInt(34);
        foody = 75 + 25*random.nextInt(24);

    }


    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); //不删此行,可实现屏幕清屏刷作用

        //准备好图片后,开始绘制静态的面板
        this.setBackground(Color.WHITE);
        GameData.header.paintIcon(this,g,25,11); //绘制广告栏
        g.fillRect(25,75,850,600); //绘制默认游戏界面

        //把小蛇画到面板上
        if (fx.equals("R")){
            GameData.right.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("L")){
            GameData.left.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("U")){
            GameData.up.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("D")){
            GameData.down.paintIcon(this,g,snakeX[0],snakeY[0]);
        }

        GameData.food.paintIcon(this,g,foodx,foody);


        for (int i = 1; i < length; i++) {
            GameData.body.paintIcon(this,g,snakeX[i],snakeY[i]);
        }

        if (flag == false){
            g.setColor(Color.white);
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("按下空格开始游戏!",300,300);
        }

    }


    @Override
    public void actionPerformed(ActionEvent e) {
        if (flag){ //如果游戏是开始状态,让小蛇开始动起来

            //吃食物的操作
            if (snakeX[0] == foodx && snakeY[0] == foody){
                length++;
                foodx = 25 + 25*random.nextInt(34);
                foody = 75 + 25*random.nextInt(24);
            }

            //右移
            for (int i = length-1; i >0 ; i--) {
                snakeX[i] = snakeX[i-1];
                snakeY[i] = snakeY[i-1];
            }

            if (fx.equals("R")){
                snakeX[0] = snakeX[0]+25;
                //加边界判断,为防止小蛇一去不复返
                if (snakeX[0] >850){snakeX[0] = 25;}
            }else if (fx.equals("L")){
                snakeX[0] = snakeX[0]-25;
                if (snakeX[0] <25){snakeX[0] =850;}
            }else if (fx.equals("U")){
                snakeY[0] = snakeY[0]-25;
                if (snakeY[0] <75){snakeY[0] =650;}
            }else if(fx.equals("D")){
                snakeY[0] = snakeY[0]+25;
                if (snakeY[0] >650){snakeY[0] =75;}
            }

            repaint(); //重画界面
        }
        timer.start(); //定时器开始!
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        if (keyCode == KeyEvent.VK_SPACE){
            flag = (!flag); //取反值,如果直接取值会写死的
            repaint();
        }
        if (keyCode == KeyEvent.VK_UP){
            fx ="U";
        }else if (keyCode == KeyEvent.VK_DOWN){
            fx = "D";
        }else if (keyCode == KeyEvent.VK_LEFT){
            fx = "L";
        }else if (keyCode == KeyEvent.VK_RIGHT){
            fx = "R";
        }
    }


    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
}


我已经非常努力的再写了,我不知道这样写博客是好还是不好,太过冗长,可能有很多没用的内容,或者说是可以省略的内容,但是我还是尽全力在叙述着。。过于啰嗦。。太追求文章的完整性了。

6. 增加游戏的判断失败和积分计算。

三部曲:

  1. 添加全局变量 boolean isFile = false;
  2. 画上失败的提示信息
  3. 键盘监听事件中添加 游戏失败初始化 并且在监听事件的条件处也添加一个条件,最后在监听事件的条件判断内,在加上一个for循环,遍历出身体的所有位置,然后 使用if 去判断一下, 如果身体坐标重合了 就设置为失败。

一会在贴代码,上边叙述的有点乱。
其实所谓的三部曲就是 先设置遍历,设置好了变量,就给它画到面板上,画好之后在监听事件中添加新的逻辑,无非就是键盘监听或者是事件监听里进行添加。
上述内容有点乱的问题,看一下代码就明白了,写好积分计算一起贴出来,就几行代码,没必要重新贴一遍了。

6.1 添加积分计算

同上 三部曲,直接看代码吧,我贴出来

package com.Gluttonous_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;

public class GamePanel extends JPanel implements KeyListener, ActionListener {

    //定义蛇的数据结构
    int length; //蛇的长度
    int[] snakeX = new int[600]; //蛇的 x 坐标 25*25
    int[] snakeY = new int[500]; //蛇的 Y 坐标 25*25
    String fx;

    //小蛇的食物设置
    int foodx;
    int foody;
    Random random = new Random();

    boolean flag = false;
    boolean isFile = false;

    int score;

    //定时器 以毫秒为单位,1000ms =1s
    Timer timer = new Timer(115,this); //100毫秒执行一次定时器


    //利用构造器调用初始化方法
    public GamePanel(){
        init();
        //获得焦点和键盘事件
        this.setFocusable(true);
        this.addKeyListener(this);
        timer.start(); //开启定时器
    }

    //定义初始化方法,设置小蛇的初始化值
    public void init(){
        length = 3;
        snakeX[0] =100; snakeY[0] =100; //小蛇脑袋的坐标
        snakeX[1] =75; snakeY[1] =100; //小蛇第一节身体坐标
        snakeX[2] =50; snakeY[2] =100; //小蛇第二节身体坐标
        fx = "R";

        //将食物分布在界面上
        foodx = 25 + 25*random.nextInt(34);
        foody = 75 + 25*random.nextInt(24);

    }


    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); //不删此行,可实现屏幕清屏刷作用

        //准备好图片后,开始绘制静态的面板
        this.setBackground(Color.WHITE);
        GameData.header.paintIcon(this,g,25,11); //绘制广告栏
        g.fillRect(25,75,850,600); //绘制默认游戏界面

        g.setColor(Color.white);
        g.setFont(new Font("微软雅黑",Font.BOLD,18));
        g.drawString("长度:"+length,765,30);
        g.drawString("积分:"+score,765,55);

        GameData.food.paintIcon(this,g,foodx,foody);//食物的绘画放在小蛇的上边,不然会压住小蛇

        //把小蛇画到面板上
        if (fx.equals("R")){
            GameData.right.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("L")){
            GameData.left.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("U")){
            GameData.up.paintIcon(this,g,snakeX[0],snakeY[0]);
        }else if (fx.equals("D")){
            GameData.down.paintIcon(this,g,snakeX[0],snakeY[0]);
        }


        for (int i = 1; i < length; i++) {
            GameData.body.paintIcon(this,g,snakeX[i],snakeY[i]);
        }

        if (flag == false){
            g.setColor(Color.white);
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("按下空格开始游戏!",300,300);
        }

        if (isFile){
            g.setColor(Color.red);
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("按下空格重新开始!",300,300);
        }

    }


    @Override
    public void actionPerformed(ActionEvent e) {
        if (flag && isFile == false){ //如果游戏是开始状态,让小蛇开始动起来

            //吃食物的操作
            if (snakeX[0] == foodx && snakeY[0] == foody){
                length++;
                score+=10;
                foodx = 25 + 25*random.nextInt(34);
                foody = 75 + 25*random.nextInt(24);
            }

            //右移
            for (int i = length-1; i >0 ; i--) {
                snakeX[i] = snakeX[i-1];
                snakeY[i] = snakeY[i-1];
            }

            if (fx.equals("R")){
                snakeX[0] = snakeX[0]+25;
                //加边界判断,为防止小蛇一去不复返
                if (snakeX[0] >850){snakeX[0] = 25;}
            }else if (fx.equals("L")){
                snakeX[0] = snakeX[0]-25;
                if (snakeX[0] <25){snakeX[0] =850;}
            }else if (fx.equals("U")){
                snakeY[0] = snakeY[0]-25;
                if (snakeY[0] <75){snakeY[0] =650;}
            }else if(fx.equals("D")){
                snakeY[0] = snakeY[0]+25;
                if (snakeY[0] >650){snakeY[0] =75;}
            }

            for (int i = 1; i < length; i++) {
                if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]){
                    isFile = true;
                }
            }

            repaint(); //重画界面
        }
        timer.start(); //定时器开始!
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        if (keyCode == KeyEvent.VK_SPACE){
            if (isFile){
                isFile = false;
                init();
            }else {
                flag = (!flag); //取反值,如果直接取值会写死的
            }
            repaint();
        }
        if (keyCode == KeyEvent.VK_UP){
            fx ="U";
        }else if (keyCode == KeyEvent.VK_DOWN){
            fx = "D";
        }else if (keyCode == KeyEvent.VK_LEFT){
            fx = "L";
        }else if (keyCode == KeyEvent.VK_RIGHT){
            fx = "R";
        }
    }


    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
}


.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兔C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值