Java教程

Java实验——贪吃蛇小游戏

本文主要是介绍Java实验——贪吃蛇小游戏,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、项目简介

贪吃蛇能够被键盘wasd控制移动,小蛇每吃到一个食物,分数加1,撞到自己,则显示游戏结束。游戏可以被space键暂停继续。

 

二、功能架构图

 

 

 

 

三、功能详解

  在600*600像素的方框中(蛇的主要移动范围),定义蛇移动一次以30*30的像素移动。

  •  窗口的创建,单独创建一个窗口类,用来设置窗口的信息,后续信息逐步添加
public class GameWin extends JFrame {
    public void init(){
        this.setVisible(true);
        this.setSize(600,600);
        this.setLocationRelativeTo(null);
        this.setTitle("贪吃蛇小游戏");
    }

    public static void main(String[] args) {
        GameWin gameWin = new GameWin();
        gameWin.init();
    }
}
  •  创建整个游戏的物体的父类,大部分是get和set方法,还有构造方法,唯一注意的地方就只有一个paintSelf的方法,绘制自身的方法
import java.awt.*;

public class GameObj {
    Image img;
    int x;
    int y;
    int width = 30;
    int height = 30;
    GameWin frame;

    public Image getImg() {
        return img;
    }

    public void setImg(Image img) {
        this.img = img;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public GameWin getFrame() {
        return frame;
    }

    public void setFrame(GameWin frame) {
        this.frame = frame;
    }

    public GameObj() {
    }

    public GameObj(Image img, int x, int y,GameWin frame) {
        this.img = img;
        this.x = x;
        this.y = y;
        this.frame = frame;
    }

    public void paintSelf(Graphics g){
        g.drawImage(img,x,y,null);
    }
}
  •   创建游戏的工具类,将蛇的头部,身体还有食物,放进工作区中,将贴图 引入工具类中,再定义一个方法用来绘制文字。
import java.awt.*;

public class GameUtils {
    public static Image headImage = Toolkit.getDefaultToolkit().getImage("img/head.png");
    public static Image bodyImage = Toolkit.getDefaultToolkit().getImage("img/body.png");
    public static Image foodImage = Toolkit.getDefaultToolkit().getImage("img/food.png");

    public static void drawWord(Graphics g,String str,Color color,int size,int x,int y){
        g.setColor(color);
        g.setFont(new Font("仿宋",Font.BOLD,size));
        g.drawString(str,x,y);
    }

}
  •   创建蛇的头部(蛇的头部要继承游戏物体的父类GameObj),在蛇的头部类中,要增加一个蛇头的默认方向,默认方向向右。
  • 先在窗口类中重写一个paint方法,然后在窗口创建蛇头对象,在paint方法中绘制蛇头,调用paintSelf方法。
import java.awt.*;

public class HeadObj extends GameObj {
    private String direction = "right";

    public String getDirection() {
        return direction;
    }

    public void setDirection(String direction) {
        this.direction = direction;
    }

    public HeadObj(Image img, int x, int y, GameWin frame) {
        super(img, x, y, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
    }
}
  •   在蛇的头部类中添加一个move 方法,对蛇的方向进行判断。在paintSelf中调用move方法。在蛇头移动的过程中,要不停的repaint,在窗口类中的初始化窗口中不停的repaint,线程休眠200毫秒

 

  •  实现键盘控制蛇头的简单移动,在蛇的头部类的构造方法中添加键盘监听事件。在蛇的头部类中,新加一个改变蛇头部移动的方法,在键盘监听事件中添加changeDirection方法
  public void changeDirection(KeyEvent e){
        switch(e.getKeyCode()){
            case KeyEvent.VK_A:
                if(!direction.equals("right")){
                    direction = "left";
                }
                break;
            case KeyEvent.VK_D:
                if(!direction.equals("left")){
                    direction = "right";
                }
                break;
            case KeyEvent.VK_W:
                if(!direction.equals("down")){
                    direction = "up";
                }
                break;
            case KeyEvent.VK_S:
                if(!direction.equals("up")){
                    direction = "down";
                }
                break;
            default:
                break;

        }

  

  •  对蛇越界进行处理,如果蛇从一个边界穿出,从相反方向穿进。在paintSelf方法中对蛇头坐标进行判断

 

  •  对蛇身的添加,先创建蛇身的类,在窗口类中定义蛇身的集合,在init中对蛇身进行初始化,在paint中反向遍历身体的集合(以防止身体的重叠)。在蛇头移动的代码之前,添加蛇身的移动,从第二个身体开始,都是上一个身体的坐标,第一个是在头移动之前,头部的坐标。
import java.awt.*;

public class BodyObj extends GameObj {
    public BodyObj(Image img, int x, int y, GameWin frame) {
        super(img, x, y, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
    }
}
  •   蛇食物随机生成,蛇的食物随机生成范围是x:0-570,y:30-570。并且只能是30的倍数。新建一个食物类,在类中定义一个随机函数,写一个获取食物的方法,返回值是食物类。在窗口类中获取食物类的对象,在paint方法中绘制食物。
import java.awt.*;
import java.util.Random;

public class FoodObj extends GameObj {
    Random r = new Random();
    public FoodObj() {
        super();
    }
    public FoodObj getFood(){
        return new FoodObj(GameUtils.foodImage,r.nextInt(20)*30,( r.nextInt(19)+1)*30,this.frame );
    }

    public FoodObj(Image img, int x, int y, GameWin frame) {
        super(img, x, y, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
    }
}
  •   蛇吃食物,吃完后,食物再次随机生成。在头部类中paintSelf方法中获取窗口类中的食物对象(一定要在蛇头移动之前),添加判断,判断食物是否和蛇头重合。如果重合就把新的随机生成的食物对象,赋值给窗口类中的foodObj。

 

  •  蛇身体的增长,蛇吃到食物,蛇就应该增长一节,在头的paintSelf方法中,定义两个变量用来记录蛇身的最后一节坐标,获取蛇身最后一节的对象,把对象的坐标给X和Y。在move后,给一个新的身体对象,添加到蛇的身体的集合中。

 

  •  设置计分面板(之前先将窗口扩大),蛇每吃一个食物,分数加一,在窗口类中添加静态变量分数,在paint方法中,把分数绘制出来。在头部类中,找到蛇吃食物的地方,分数累加。在paint中画线,以区分蛇的移动区域和计分面板
  • 添加游戏开始的提示语,首先在窗口类中定义游戏状态的变量,有五种状态(0未开始,1游戏中,2暂停,3失败,4通关),绘制提示语,新建一个方法prompt(在里面绘制提示语),然后在paint方法中调用prompt方法
  • 添加游戏开始和暂停的键盘事件,在窗口类的初始化窗口中,添加键盘事件,在键盘事件中添加对状态的判断,状态的相应改变

 

 

  • 蛇头与身体的碰撞判断,在头部类的move方法中,判断蛇头的坐标与身体的坐标重合,则游戏失败,把游戏状态改为3.在prompt中添加游戏失败的提示语

 

 

  •  简单处理一下零散的地方,排除bug......

四、课程设计感想

 

有利于工程开发思维的培养,稳固知识点,增强团队分工合作的意识。
意识到了自己知识水平还有很大缺陷,和高手之间还有很长的路要走。

  

 

五、展望

1,界面字有闪烁问题:双缓存解决

游戏单调问题:增加关卡设置,增加障碍物,增加通关界面

游戏运行效率不高:改进小蛇移动时候,贴图的移动方法,可以只改变蛇头和蛇尾使小蛇移动,增加多线程的应用

2,进一步学习Java高级开发工具类,学习多线程,学习网络开发, docker容器,学习一些开发web技术

 

  

 

六、完整代码呈现,以及游戏截图,代码块有注释

 

  •  GameWin.java
package com.draw.test01;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;

public class GameWin extends JFrame {
    //继承了Jframe类便具有了创建窗口,监听鼠标键盘事件的功能

    //记录分数
    public static int score = 0;
    //游戏状态 0未开始 1游戏中 2暂停 3失败 4通关
    public static int state = 0;
    int winWidth = 800;
    int winHeight = 600;

    //蛇的头部
    HeadObj headObj = new HeadObj(GameUtils.rightImg,30,30,this);//窗口引用是当前对象this

    //蛇身体的集合
    public List<BodyObj> bodyObjList = new ArrayList<BodyObj>();

    //food
    public FoodObj foodObj = new FoodObj().getFood();
    public void init(){
        //设置窗口是否可见
        this.setVisible(true);
        //设置窗口大小
        this.setSize(winWidth,winHeight);
        //设置窗口标题
        this.setTitle("贪吃蛇小游戏");
        //蛇身体的初始化
        bodyObjList.add(new BodyObj(GameUtils.bodyImg,30,570,this));
        bodyObjList.add(new BodyObj(GameUtils.bodyImg,0,570,this));

        //添加键盘事件
        this.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if(e.getKeyCode() == KeyEvent.VK_SPACE){
                    switch(state){
                        case 0:
                        case 2:
                            state =1;
                            break;
                        case 1:
                            state = 2;
                            repaint();
                            break;
                        default:
                            break;
                    }
                }
            }
        });
        while(true){
            if(state == 1){
                repaint();
            }
            try {
                Thread.sleep(200);//每次重绘过后,线程休眠,1秒刷新5次
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    //重写paint方法
    @Override
    public void paint(Graphics g) {
        //super.paint(g);
        //灰色背景
        g.setColor(Color.GRAY);
        //填充矩形
        g.fillRect(0,0,winWidth,winHeight);
        //网格线
        g.setColor(Color.LIGHT_GRAY);
        //for(int i=1;i<=20;i++){

            g.drawLine(600,30,600,600);
        //    g.drawLine(30*i,0,30*i,600);
        //}
        //反向遍历身体的集合,防止想要的数据被覆盖
        for (int i = bodyObjList.size()-1; i >= 0 ; i--) {
            bodyObjList.get(i).paintSelf(g);
        }

        //在paint 方法中绘制蛇头
        headObj.paintSelf(g);
        //食物的绘制
        foodObj.paintSelf(g);
        //将分数绘制出来
        GameUtils.drawWord(g,score+" 分",Color.BLUE,50,650,300);
        g.setColor(Color.GRAY);
        prompt(g);
    }

    //绘制提示语
    void prompt(Graphics g){
        if(state == 0){
            g.fillRect(120,240,400,70);
            GameUtils.drawWord(g,"按下空格开始游戏",Color.yellow,35,150,290);

        }
        if(state == 2){
            g.fillRect(120,240,400,70);
            GameUtils.drawWord(g,"按下空格继续游戏",Color.BLUE,35,150,290);

        }
        if(state == 3){
            g.fillRect(120,240,400,70);
            GameUtils.drawWord(g,"回家种地去吧",Color.RED,35,150,290);

        }
    }

    public static void main(String[] args) {
        GameWin gamewin = new GameWin();
        gamewin.init();
    }
}

  

  • GameUtils.java
package com.draw.test01;

import java.awt.*;

//游戏的工具类
public class GameUtils {
    //蛇头
    //public static Image upImg = Toolkit.getDefaultToolkit().getImage("img/test.png");
    //public static Image dowmImg = Toolkit.getDefaultToolkit().getImage("img/test.png");
    //public static Image leftImg = Toolkit.getDefaultToolkit().getImage("img/test.png");
    public static Image rightImg = Toolkit.getDefaultToolkit().getImage("img/test.png");
    //蛇身
    public static Image bodyImg = Toolkit.getDefaultToolkit().getImage("img/body.png");
    //蛇的食物
    public static Image foodImg = Toolkit.getDefaultToolkit().getImage("img/food.png");

    public static void drawWord(Graphics g,String str,Color color,int size,int x,int y){
        g.setColor(color);
        g.setFont(new Font("仿宋",Font.BOLD,size));
        g.drawString(str, x, y);
    }

}
  •   GameObj.java
package com.draw.test01;

import java.awt.*;

//创建游戏物体的父类
public class GameObj {
    //picture
    Image img;
    //coordinate
    int x;
    int y;
    int width = 30;
    int height = 30;
    //窗口类的引用
    GameWin frame ;

    public Image getImg() {
        return img;
    }

    public void setImg(Image img) {
        this.img = img;
    }

    public int getX() {
        return x;
    }

    public GameObj(Image img, int x, int y, GameWin frame) {
        this.img = img;
        this.x = x;
        this.y = y;
        this.frame = frame;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public GameWin getFrame() {
        return frame;
    }

    public void setFrame(GameWin frame) {
        this.frame = frame;
    }
    //无参构造
    public GameObj() {
    }
    //有参构造
    public GameObj(Image img, int x, int y, int width, int height, GameWin frame) {
        this.img = img;
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        this.frame = frame;
    }

    //绘制自身
    public void paintSelf(Graphics g){
        g.drawImage(img,x,y,null);
    }
}
  •   HeadObj.java
package com.draw.test01;

import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.List;

//蛇的头部
public class HeadObj extends GameObj{


    //方向 up down left right
    private String direction = "right";


    public HeadObj(Image img, int x, int y, GameWin frame) {
        super(img, x, y, frame);
        //在构造方法中添加键盘监听事件
        this.frame.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                //super.keyPressed(e);//键盘按下、、获取到了键盘事件e
                changeDirection(e);
            }
        });
    }

    //控制移动方向
    public void changeDirection(KeyEvent e){
        switch(e.getKeyCode()){
            case KeyEvent.VK_A:
                if(!direction.equals("right")){
                    direction = "left";
                }
                break;
            case KeyEvent.VK_D:
                if(!direction.equals("left")){
                    direction = "right";
                }
                break;
            case KeyEvent.VK_S:
                if(!direction.equals("up")){
                    direction = "down";
                }
                break;
            case KeyEvent.VK_W:
                if(!direction.equals("down")){
                    direction = "up";
                }
                break;
            default:
                break;
        }
    }

    public String getDirection() {
        return direction;
    }

    public void setDirection(String direction) {
        this.direction = direction;
    }
    public void move (){
        //在蛇头移动之前先把蛇身移动
        List<BodyObj> bodyObjList = this.frame.bodyObjList;
        for (int i = bodyObjList.size() - 1; i >= 1; i--) {
            bodyObjList.get(i).x = bodyObjList.get(i-1).x;
            bodyObjList.get(i).y = bodyObjList.get(i-1).y;
            if(this.x == bodyObjList.get(i).x&&this.y == bodyObjList.get(i).y){
                GameWin.state = 3;
            }
        }
        bodyObjList.get(0).x = this.x;
        bodyObjList.get(0).y = this.y;
        switch(direction){
            case "up":
                y -= height;
                break;
            case "down":
                y += height;
                break;
            case "left":
                x -= width;
                break;
            case "right":
                x += width;
                break;
        }
    }
    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
        //获取窗口类中的食物对象,蛇吃食物
        FoodObj food = this.frame.foodObj;
            //身体最后一节的坐标
        Integer X = null;
        Integer Y = null;
        if(food.x == this.x&&food.y== this.y){
            //获取蛇最后一节的坐标
            BodyObj lastBody = this.frame.bodyObjList.get(this.frame.bodyObjList.size()-1);
            X = lastBody.x;
            Y = lastBody.y;
            //蛇每吃一个食物,分数加一
            GameWin.score++;
            this.frame.foodObj = food.getFood();
        }
        move();
        if(X != null && Y != null){
            this.frame.bodyObjList.add(new BodyObj(GameUtils.bodyImg,X,Y,this.frame));
        }

        //越界处理
        if(x<0){
            x = 570;
        }
        else if(x>570){
            x = 0;
        }
        else if(y<30){
            y = 570;
        }
        else if(y>570){
            y = 30;
        }
    }
}
  •   BodyObj.java
package com.draw.test01;

import java.awt.*;

public class BodyObj extends GameObj{

    public BodyObj(Image img, int x, int y, GameWin frame) {
        super(img, x, y, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
    }
}
  •   FoodObj.java
package com.draw.test01;

import java.awt.*;
import java.util.Random;

public class FoodObj extends GameObj{
    //定义随机函数
    Random random = new Random();
    public FoodObj() {
        super();
    }

    public FoodObj(Image img, int x, int y, GameWin frame) {
        super(img, x, y, frame);
    }
    //获取食物
    public FoodObj getFood(){
        return new FoodObj(GameUtils.foodImg,random.nextInt(20)*30,(random.nextInt(19)+1)*30,this.frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
    }
}
  •   运行效果截图,其他的自己去敲代码观察运行效果吧

 

  •  参考代码博客链接
这篇关于Java实验——贪吃蛇小游戏的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!