创建屏幕

开始游戏的窗口,首先引入窗口,然后在窗口画布上进行添加各类动画。

JFrame frame=new JFrame("My SnakeGame");

Jframe 是个类,引入了窗口frame,该窗口的标题是:My SnakeGame

frame.setBounds(100,100,800,500);//设置零点坐标,窗口大小

参数1: 窗口的左上角点的横坐标

参数2:窗口的左上角点的纵坐标

参数3:窗口的宽度

参数4:窗口的高度

单位为像素

frame.setResizable(false); //窗口大小不可随意改变大小

这样设置后不能够随意改变窗口的大小。主要是牵扯到画面的自动扩展延伸。

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口即关闭程序,无后台

这样设置后关闭窗口后自动关闭程序,否则关闭窗口后,程序依旧在运行。

frame.setVisible(true);    //窗口可被展示

窗口可以被看到

frame.add(new GameJPanel());//引入画板

通过这个语句引入画板,图片,操作都在画板上进行。

画板

定义我们自己的画布类必须继承自JPanel系统画板类,我们需要重写方法

protected void paintComponent(Graphics g){} //在这个方法里编写我们想要画在画板上的东西

这里的Graphics g参数是画笔g,我们可以设置画笔的颜色,字体等。

在我们重写的这个方法里先调用父类的方法 :super.paintComponent(g);

需要在画面上进行某些改变的话都要写在这个重写的方法里。

this.setBackground(Color.WHITE);  //定义背景颜色

WHITE可选该为其他颜色,其他部分为固定语法法

this表示当前画布,可以理解为当前画布设置背景颜色为白色

 g.setColor(Color.WHITE);  //画笔颜色设置
g.setFont(new Font("宋体",Font.BOLD,40));

设置画笔的写出的颜色,字体格式,字体类型,字体大小。

g.drawString("按下空格开始",300,250); 

语法:g.drawString("要打印的字体",x,y);

写入打印的字符串,打印起始点的坐标。

Date.header.paintIcon(this,g,2,0);//在画板里打印图片
//参数分别为,窗口this(本窗口),画笔g,(2,0)表示坐标距离

语法:Date.名字.paintIcon(画面,画笔,x,y);

这里的header是我们在下面的数据引入时引入的图片资源名称。

paomtIcon();指打印图片的方法。

g.fillRect(25,75,750,400); //填充窗口画面

25,75表示起始的点坐标。750,400表示从那点开始要打印的画面的宽度,高度

这部主要实现游戏窗口的背景打印。

按键监听

这里我们需要实现两个接口,第一个是键盘监听接口 KeyListener。这个接口依旧在我们自己定义的画板上进行实现,实现了这个接口我们就需要实现三个方法。

  1. public void keyTyped(KeyEvent e) { //按一下
    
    }
    

    这个方法主要监听键盘上按键仅按一下的情况下。可以理解为按一下记一下。

  2. public void keyReleased(KeyEvent e) { //弹起
    
    }
    

    这个方法监听的是键盘按下后弹起的过程。

  3. public void keyPressed(KeyEvent e) { //一直按
    }
    

    监听长按的键盘。

这里的KeyEvent e是接受的参数,e.getKeyCode();表示得到的键盘输入情况

定义个数keyCode接受这个按键。 int keyCode=e.getKeyCode();后面都是用keyCode与按键进行比较

KeyEvent.VK_UP //表示上键

语法:KeyEvent.VK_按键; 表示这个案件 //按键部分需要全大写字母。例如LEFT

if(keyCode==KeyEvent.VK_DDWN)

{           

 //当按键为“下”时做什么,一般为改变一个变量(在这个监听按键的方法里改变),通过这个改变这个变量改变打印的内容(在重写的画板方法里改变打印的内容),从而实现画面里方向的改变。

 }
repaint(); //重新绘制界面的方法

repaint();作用是刷新打印的界面,可以在监听按键的方法里调用,从而实现按键后画面的改变。

一般会定义个初始化变量的方法,用来实现游戏重新加载或第一次加载时的变量初始化。

定时操作

我们需要实现重写的第二个接口是定时活动操作ActionListener,会随着时间变化而变化,设置定时执行该方法的的方法体。我们需要重写方法

public void actionPerformed(ActionEvent e) {//执行定时操作
    //随时间变化的操作,如想让蛇头朝一个方向走动只需要其坐标(X或Y)自增。这里面的操作都会随着我们设置的时间间隔不断重复执行。
    timer.start(); //计时器开始
}

在方法的左后要加上timer.start(); //计时器开始确保计时器开始执行,即执行完第一次后不断重复执行开始

数据引入

在这之前需要先建个static包用来存放我们的资源数据,直接将所需要的照片包拷进该文件夹即可。

定义Data类,通过Date类引入我们需要使用的图片,音乐等外部资源。

语法: 修饰符 URL 地址名称=类名.class.getResource("地址"); //地址名称是自己按规则随意取的,地址是文件所在的文件夹和文件名称和类型。

public static URL headerURL=Date.class.getResource("地址");
//引入资源的地址

eg:public static URL upURL=Date.class.getResource("/static/up.png");

需要注意的是这里只是引入资源的地址,并没有说明这是什么资源类型。

声明引入资源的类型。如图片的声明。

语法:修饰符 ImageIcon 名称=new ImageIcon(地址名称);

public static ImageIcon header=new ImageIcon(headerURL);
//引入资源为图片

屏幕框架引入

package com.cyqf.snake.www;

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

public class StartGame {
    public static void main(String[] args) {
        JFrame frame=new JFrame("My SnakeGame");
        frame.setBounds(100,100,800,500);//设置零点坐标,窗口大小
        frame.setResizable(false); //窗口大小不可随意改变
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口即关闭程序,无后台
        frame.setVisible(true);    //窗口可被展示

        frame.add(new GameJPanel());//引入画板

        //在我们的窗口里可以引入画板画的东西

    }
}

数据引入

package com.cyqf.snake.www;

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

public class Date { //引入数据,引入后才能被直接调用

    public static URL headerURL=Date.class.getResource("/static/header.png");
    //引入资源的地址
    public static ImageIcon header=new ImageIcon(headerURL);
    //引入资源为图片
    public static URL upURL=Date.class.getResource("/static/up.png");
    public static URL downURL=Date.class.getResource("/static/down.png");
    public static URL leftURL=Date.class.getResource("/static/left.png");
    public static URL rightURL=Date.class.getResource("/static/right.png");
    public static URL foodURL=Date.class.getResource("/static/food.png");
    public static URL bodyURL=Date.class.getResource("/static/body.png");

    public static ImageIcon up=new ImageIcon(upURL);
    public static ImageIcon down=new ImageIcon(downURL);
    public static ImageIcon left=new ImageIcon(leftURL);
    public static ImageIcon right=new ImageIcon(rightURL);
    public static ImageIcon food=new ImageIcon(foodURL);
    public static ImageIcon body=new ImageIcon(bodyURL);


}

画板

package com.cyqf.snake.www;

import com.sun.xml.internal.ws.policy.EffectiveAlternativeSelector;

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 GameJPanel extends JPanel implements KeyListener , ActionListener {
    //2.Jpanel画板类,写入我们需要的东西如图片,背景颜色等,
    //这里我们自己定义一个画板,继承来自系统的画板
    int length=600;//定义蛇的长度
    String fx;//蛇头方向
    int[] snakex=new int[600]; //定义横坐标
    int[] snakey=new int[500]; //定义纵坐标
    boolean isstart; //开始结束变量
    boolean isfail;  //成功失败变量
    //设置定时器
    Timer timer=new Timer(100,this);//每个多少毫秒,监听哪个窗口
   Random random=new Random(); //引入随机方法
    int foodx; //食物x坐标
    int foody; //食物y坐标
    int score; //分数

    GameJPanel(){  //构造器
        init();  //初始化方法
        this.setFocusable(true);  //获取键盘的焦点,将键盘的焦点聚集在游戏上
        this.addKeyListener(this);  //实现类
        timer.start();  //计时器引入
    }

    void init(){//初始化方法
        length=3;    //初始长度
        fx="R";      //默认朝向
        snakex[0]=100; //头的初始位置
        snakey[0]=100;
        snakex[1]=75;snakey[1]=100; //第一节身体
        snakex[2]=50;snakey[2]=100; //第二节身体
         isstart=false;  //判断游戏是否开始
         isfail=false;
        foodx=75+25*random.nextInt(28); //给食物坐标随机
        foody=75+25*random.nextInt(13);
         score=0;

    }




    @Override
      protected void paintComponent(Graphics g) { //重写方法,画板
        super.paintComponent(g);  //这里的Graphics g就是画笔
        this.setBackground(Color.WHITE);  //定义背景颜色

        Date.header.paintIcon(this,g,2,0);//在画板里打印图片
        //参数分别为,窗口this(本窗口),画笔g,坐标距离
        g.fillRect(25,75,750,350); //填充窗口画面

        if (fx.equals("R")){ //通过监听键盘改变fx的值,改变fx的值后打印不同画面,实现蛇头的转向
            Date.right.paintIcon(this,g,snakex[0],snakey[0]);
        }
       else if (fx.equals("L"))
        {
            Date.left.paintIcon(this,g,snakex[0],snakey[0]);
        }
        else if (fx.equals("U"))
        {
            Date.up.paintIcon(this,g,snakex[0],snakey[0]);
        }
        else if (fx.equals("D"))
        {
            Date.down.paintIcon(this,g,snakex[0],snakey[0]);
        }

        for (int i = 1; i <length; i++) {
            Date.body.paintIcon(this,g,snakex[i],snakey[i]); //初始值为3,两节身体,
            // 随着长度增加,可以不断打印
        }
         if (isfail==false){
             if (isstart==false){ //提示信息
                 g.setColor(Color.WHITE);  //画笔颜色设置
                 g.setFont(new Font("宋体",Font.BOLD,40)); //字体格式,粗体,大小
                 g.drawString("按下空格开始",300,250);
             }

         }else {
             g.setColor(Color.red);  //设置画笔的颜色
             g.setFont(new Font("宋体",Font.BOLD,40)); //
             g.drawString("游戏失败按空格重新开始",250,250);
             init();
         }


        Date.food.paintIcon(this,g,foodx,foody);
         g.setColor(Color.blue);
         g.setFont(new Font("微软雅黑",Font.BOLD,18));
         g.drawString("分数:"+score,675,25);
         g.drawString("长度:"+length,675,39);


    }

    @Override //按键控制监听
    public void keyPressed(KeyEvent e) { //一直按
       int keyCode=e.getKeyCode();
       if (keyCode==KeyEvent.VK_SPACE) //接受到了空格,给状态取反,从而实现开始暂停
       {
           isstart=!isstart;
       }
       if (keyCode==KeyEvent.VK_UP)     //由键盘输入的值改变fx
       {
           if(!fx.equals("D"))  //不能回头的判断语句
           fx="U";
       }else if (keyCode==KeyEvent.VK_DOWN)
       {
           if(!fx.equals("U")) //不能回头的判断语句
           fx="D";
       }else if (keyCode==KeyEvent.VK_RIGHT)
       {
           if(!fx.equals("L")) //不能回头的判断语句
           fx="R";
       }else if (keyCode==KeyEvent.VK_LEFT)
       {
           if(!fx.equals("R")) //不能回头的判断语句
           fx="L";
       }
       repaint(); //重新绘制界面的方法
    }

    @Override
    public void actionPerformed(ActionEvent e) {//执行定时操作
        if (isstart){
            for (int i = length-1; i >0; i--) { //遵循上一节的足迹往前走
              snakex[i]=snakex[i-1];
              snakey[i]=snakey[i-1];
            }
            if (fx.equals("R")){       //在这里进行打印判断,由fx的值判断蛇头的打印
                snakex[0]=snakex[0]+25;
                if (snakex[0]>750)
                {
                    snakex[0]=25;     //超过边界后从另一边出来
                }
            } else if (fx.equals("L")){
                snakex[0]=snakex[0]-25;
                if (snakex[0]<25)
                    snakex[0]=750;      //超过边界后从另一边出来
            }else if (fx.equals("U")){
                snakey[0]=snakey[0]-25;
                if (snakey[0]<75)
                    snakey[0]=400;    //超过边界后从另一边出来
            }else if (fx.equals("D")){
                snakey[0]=snakey[0]+25;
                if (snakey[0]>400){
                    snakey[0]=75;  //超过边界后从另一边出来
                }
            }

            if (foodx==snakex[0]&&foody==snakey[0]) //吃到食物的哦按段
            {
                length++;  //身体增加
                score+=10; //分数增加
                foodx=50+25*random.nextInt(28); //食物坐标刷新坐标随机
                foody=75+25*random.nextInt(13);
            }
            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) { //弹起

    }


}

所需要的资源:

​ right.png

​ left.png

​ up.png

​ down.png

​ food.png

header.png

​ body.png