实验二:贪吃蛇游戏开发

实验目标

1)整体程序无bug,实现贪吃蛇游戏基本功能;

2)使用Java编程语言,在IntelliJ IDEA Community Edition上进行开发。

实验内容:

1)实现贪吃蛇游戏基本功能,屏幕上随机出现一个“食物”,称为豆子。玩家能利用上下左右键控制的移动,“蛇”吃到“豆子”后“蛇”身体加长一节,得分增加,“蛇”碰到边界或蛇头与蛇身相撞,“蛇”死亡,游戏结束。

2)进行交互界面的设计,要有开始键、暂停键和停止退出的选项,能够控制游戏进程。对蛇吃到豆子进行分值计算,可以设置游戏速度,游戏音乐等拓展元素。

分析;

可以实现贪吃蛇游戏的基本功能; 

可按键调速

不足:未具有计分功能,不可暂停

1.SnakeUI类

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

public class SnakeUI extends JFrame implements KeyListener {
    private BufferedImage uiImg = new BufferedImage(Constant.JFRAME_WIDTH,
            Constant.JFRAME_HEIGHT, BufferedImage.TYPE_3BYTE_BGR);
    private Rectangle rec;
    private SNode food;
    private Snake snake; // 定义蛇对象
    private static int SPEED = Constant.SNAKE_NORMAL;

    public SnakeUI() {
        this.rec = Constant.rec;
        this.food = new SNode(Color.BLUE);
        this.snake = new Snake(this);// 初始化蛇对象
        this.launchFrame();
    }

    // 获得当前界面的食物
    public SNode getFood() {
        return food;
    }

    private void launchFrame() {
        this.setTitle("贪吃蛇");
        this.setBounds(200, 100, Constant.JFRAME_WIDTH, Constant.JFRAME_HEIGHT);
        this.setVisible(true);
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        new Thread(new UIUpdate()).start(); // 启动更新线程
        this.addKeyListener(this); // 监听键盘事件
    }

    public void paint(Graphics g) {
        // 1.直接画入图形。g.drawRect画入闪烁效果[不可取]

        // 2.将图形画入到缓存图片,再讲图片画入到Frame中,避免闪烁效果
        // 设置矩形的颜色,并绘入到uiImg图片,将此图片直接写入到内存
        Graphics2D g2d = (Graphics2D) uiImg.getGraphics();
        // 在缓存图片画白色背景
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, this.getWidth(), this.getHeight());

        // 设置边框颜色
        g2d.setColor(Color.BLACK);
        g2d.drawRect((int) this.rec.getX(), (int) this.rec.getY(),
                (int) this.rec.getWidth(), (int) this.rec.getHeight());

        // 设置中间方格
        g2d.setColor(Color.CYAN);
        int startx = (int) this.rec.getX();
        int starty = (int) this.rec.getY();
        for (int i = 0; i < 35; i++) {
            for (int j = 0; j < 50; j++) {
                g2d.drawRect(startx + j * Constant.FOOD_WIDTH, starty + i
                                * Constant.FOOD_HEIGHT, Constant.FOOD_WIDTH,
                        Constant.FOOD_HEIGHT);
            }
        }

        // 设置版本文字
        g2d.setColor(Color.RED);
        g2d.setFont(new Font("宋体", Font.ITALIC, 16));// 设置字体
        g2d.drawString(" ", 580, 530);

        // 画食物,食物paint方法在SNode类定义,在UI界面中的paint方法被调用
        this.food.paint(g2d);

        // 画蛇
        this.snake.paint(g2d);

        // 蛇吃食物绘制:蛇已经在吃食物,食物被吃完后重新定义一个食物对象,并与蛇的身体做重叠判断
        if (this.snake.eat(this.food)) {
            this.food = new SNode(Color.BLUE);
            while (this.snake.hit(this.food)) {
                this.food = new SNode(Color.BLUE);
            }
        }
        g.drawImage(uiImg, 0, 0, null); // 写入到内存
    }

    public static void main(String[] args) {
        new SnakeUI();
    }

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

    }

    @Override
    // 按压键盘主要目的更改蛇运动方向
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();
        switch (key) {
            case KeyEvent.VK_LEFT:
                snake.setDir(Constant.L);
                System.out.println("dddl");
                break;
            case KeyEvent.VK_RIGHT:
                snake.setDir(Constant.R);
                System.out.println("dddr");
                break;
            case KeyEvent.VK_UP:
                snake.setDir(Constant.U);
                System.out.println("dddu");
                break;
            case KeyEvent.VK_DOWN:
                snake.setDir(Constant.D);
                break;
            case KeyEvent.VK_W:
                SPEED = Constant.SNAKE_ADD;
                break;
            case KeyEvent.VK_D:
                SPEED = Constant.SNAKE_NORMAL;
                break;
            case KeyEvent.VK_S:
                SPEED = Constant.SNAKE_SUB;
                break;
            case KeyEvent.VK_T:
                SPEED = Constant.SNAKE_SUPER;
                break;
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        SPEED = Constant.SNAKE_NORMAL;
    }

    // 不断刷新界面的线程
    class UIUpdate implements Runnable {
        @Override
        public void run() {
            while (SnakeUI.this.snake.isLive()) {
                System.out.println("..............thread....");
                SnakeUI.this.repaint();
                // 为了减缓更新的速度,设定每一次绘制之后停顿1秒。
                try {
                    Thread.sleep(SPEED);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            JOptionPane.showMessageDialog(SnakeUI.this, "游戏越界,game over.");
        }
    }
}

2.Snake蛇类

import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JOptionPane;

public class Snake {
    private SNode snakeHead; // 蛇头结点
    private int startx, starty; // 蛇头的坐标
    private List<SNode> nodes = new ArrayList<SNode>(); // 整条蛇,由SNode结点组成:
    // 线性表List
    private int dir; // 方位
    private boolean isLive = true; // 蛇处于运行状态

    //给蛇添加运行速度,每一次运行走3个像素
    private int speed = 3;

    // getter.setter蛇的方向
    public int getDir() {
        return dir;
    }

    public void setDir(int dir) {
        this.dir = dir;
    }

    // 传入蛇所在的界面,通过界面获得当前的食物对象
    public Snake(SnakeUI ui) {
        this.dir = Constant.U;
        this.noOverride(ui.getFood());
    }

    // 随机产生蛇头的坐标与食物不重复
    public void noOverride(SNode food) {
        this.generateSnake();
        while (this.hit(food)) { // 蛇与食物重叠
            this.generateSnake();
        }
    }

    private void generateSnake() {
        int x = SNode.generate(50); // [0,49)
        int y = SNode.generate(35);
        if (x == 0) {
            x += 1;
        } else if (x == 49) {
            x -= 1;
        }
        if (y == 0) {
            y += 1;
        } else if (y == 34) {
            y -= 1;
        }
        this.startx = ((int) Constant.rec.getX() + x * Constant.FOOD_WIDTH);
        this.starty = ((int) Constant.rec.getY() + y * Constant.FOOD_HEIGHT);
        this.snakeHead = new SNode(this.startx, this.starty, Color.GREEN);
        nodes.add(snakeHead);
        this.addNode(); // 默认是2个结点,需再添加1个结点
    }

    // 判定蛇是否与食物是否重叠:true:重叠 ,false:不重叠
    public boolean hit(SNode food) {
        boolean result = false;
        for (SNode snode : nodes) {
            boolean res = snode.getRec().intersects(food.getRec());
            if (res) {
                result = true;
                break;
            }
        }
        return result;
    }

    // 吃食物
    public boolean eat(SNode food) {
        boolean result = false;
        for (SNode snode : nodes) {
            boolean res = snode.getRec().intersects(food.getRec());
            if (res) {
                result = true;
                if (nodes.size() >= 2) { // 1.初始相遇,2.在移动中相遇
                    this.addNode(); // 蛇的身体增长1
                }
                break;
            }
        }
        return result;
    }

    // 给蛇的尾部添加一个结点,与蛇方位有关
    private void addNode() {
        int size = nodes.size();
        switch (dir) {
            case Constant.L: // 向左走,则右边添加第2个结点,且没有碰壁
                if (size == 1) { // 蛇静止,需要添加一个结点到蛇尾
                    int x = this.startx + Constant.FOOD_WIDTH;
                    int y = this.starty;
                    SNode snode = new SNode(x, y, Color.GREEN);
                    nodes.add(snode); // 添加到集合末尾
                } else {
                    int x = this.startx - Constant.FOOD_WIDTH;
                    int y = this.starty;
                    SNode snode = new SNode(x, y, Color.GREEN);
                    nodes.add(0, snode);
                    this.snakeHead = snode;
                    this.startx = x;
                    this.starty = y;
                    // 判断蛇头左越界
                    if (this.startx < Constant.rec.getX()) {
                        this.isLive = false;
                    }
                }
                break;
            case Constant.R:
                if (size == 1) { // 蛇静止,需要添加一个结点到蛇尾
                    int x = this.startx - Constant.FOOD_WIDTH;
                    int y = this.starty;
                    SNode snode = new SNode(x, y, Color.GREEN);
                    nodes.add(snode); // 添加到集合末尾
                } else {
                    int x = this.startx + Constant.FOOD_WIDTH;
                    int y = this.starty;
                    SNode snode = new SNode(x, y, Color.GREEN);
                    nodes.add(0, snode);
                    this.snakeHead = snode;
                    this.startx = x;
                    this.starty = y;
                    //判断蛇头右越界
                    if (this.startx > Constant.GAME_WIDTH) {
                        this.isLive = false;
                    }
                }
                break;
            case Constant.U:
                if (size == 1) { // 蛇静止,需要添加一个结点到蛇尾
                    int x = this.startx;
                    int y = this.starty + Constant.FOOD_HEIGHT;
                    SNode snode = new SNode(x, y, Color.GREEN);
                    nodes.add(snode); // 添加到集合末尾
                } else {
                    int x = this.startx;
                    int y = this.starty - Constant.FOOD_HEIGHT;
                    SNode snode = new SNode(x, y, Color.GREEN);
                    nodes.add(0, snode);
                    this.snakeHead = snode;
                    this.startx = x;
                    this.starty = y;
                    // 判断蛇头上越界
                    if (this.starty < Constant.rec.getY()) {
                        this.isLive = false;
                    }
                }
                break;
            case Constant.D:
                if (size == 1) { // 蛇静止,需要添加一个结点到蛇尾
                    int x = this.startx;
                    int y = this.starty - Constant.FOOD_HEIGHT;
                    SNode snode = new SNode(x, y, Color.GREEN);
                    nodes.add(snode); // 添加到集合末尾
                } else {
                    int x = this.startx;
                    int y = this.starty + Constant.FOOD_HEIGHT;
//				int y = this.starty + this.speed;
                    SNode snode = new SNode(x, y, Color.GREEN);
                    nodes.add(0, snode);
                    this.snakeHead = snode;
                    this.startx = x;
                    this.starty = y;
                    //判断蛇头下越界
                    if (this.starty > ((int)Constant.rec.getY()+ Constant.GAME_HEIGHT-Constant.FOOD_HEIGHT)) {
                        this.isLive = false;
                    }
                }
                break;
        }

    }

    public boolean isLive() {
        return isLive;
    }

    // 蛇的移动,不断变换蛇头,删除蛇尾
    private void move() {
        this.addNode(); // 再添加一个新结点,删除末尾结点
        int len = nodes.size();
        nodes.remove(len - 1);
    }

    // 画蛇及蛇的移动
    public void paint(Graphics2D g2d) {
        // 对nodes结点依次绘制
        g2d.setColor(this.snakeHead.getColor());

        // 对蛇的身体进行绘制
        for (SNode snode : nodes) {
            g2d.fillRect(snode.getX(), snode.getY(), Constant.FOOD_WIDTH,
                    Constant.FOOD_HEIGHT);
        }
        this.move();
    }
}

3.SNode食物类

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;

//食物类的定义:
public class SNode {
	private int x,y;     //食物的起始坐标,与游戏的起始点关联
	private Color color; //食物的颜色
	private Rectangle rec; //食物的矩形区域
	
	public Color getColor() {
		return color;
	}

	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 Rectangle getRec() {
		return rec;
	}

	public void setRec(Rectangle rec) {
		this.rec = rec;
	}

	//随机产生某个位置的食物
	public SNode(Color color) {
		this.x = (int)Constant.rec.getX()+this.generate(50)*Constant.FOOD_WIDTH;
		this.y = (int)Constant.rec.getY()+this.generate(35)*Constant.FOOD_HEIGHT;
		this.rec = new Rectangle(this.x, this.y, Constant.FOOD_WIDTH, Constant.FOOD_HEIGHT);
		this.color = color; 
	}
	
	//生成固定位置的结点,用于产生蛇头
	public SNode(int x,int y,Color color) {
		this.x = x;
		this.y = y;
		this.color = color;
		this.rec = new Rectangle(this.x, this.y, Constant.FOOD_WIDTH, Constant.FOOD_HEIGHT);
	}
	
	//随机产生数字
	public static int generate(int number) { //[0,numbder)
		int x = 0;
		x = (int)(Math.random()*number);
		return x;
	}
	
	//定义画食物的方法:基于图形缓存画法Graphics2D
	public void paint(Graphics2D g2d) {
		g2d.setColor(this.color);
        g2d.fillRect(this.x, this.y, Constant.FOOD_WIDTH, Constant.FOOD_HEIGHT);
	}
	
	public static void main(String[] args) {
		
	}

}

4.Constant常量类

import java.awt.Rectangle;


public class Constant {
    public static final int FOOD_WIDTH = 15; // 食物的宽度和高度

    public static final int FOOD_HEIGHT = 15;

    public static final int GAME_WIDTH = FOOD_WIDTH*50;

    public static final int GAME_HEIGHT = FOOD_HEIGHT*35;

    public static final int JFRAME_WIDTH = FOOD_WIDTH*52;

    public static final int JFRAME_HEIGHT = FOOD_HEIGHT*38+6;

    public static final int L=1,R=2,U=3,D=4;

    public static final int SNAKE_NORMAL = 500;

    public static final int SNAKE_ADD = 180;

    public static final int SNAKE_SUPER = 50;

    public static final int SNAKE_SUB = 850;

    //游戏方格区域
    public static final Rectangle rec = new Rectangle(FOOD_WIDTH, FOOD_HEIGHT*2+6, GAME_WIDTH, GAME_HEIGHT);

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值