Java 贪吃蛇(简单版)

前言:上周二老师突然给布置了一个任务,让做贪吃蛇,swing才学了一点点,就接到了这个任务(可能老师以为我Java基础学完了就给我发了这个任务),做了有一周吧,其实一开始就没什么思路,就是一个一个功能去实现,虽然周五其实就算做好了(Timer里的schedule方法一个参数一直没改,导致最后一直按太快会撞到自己死掉- - ),但是老师提了一下设计思想的问题,我就把他分成了两个类来实现的,原本一个类就给干到底了,真滴服气,好了,吐槽就到这。

思路:

1.蛇类:

字段:

1.蛇身(矩形数组)

2.蛇头坐标

3.矩形边长

4.第一次按下

方法:

0.初始化蛇身

1.移动

2.是否死亡

3.吃豆子

4.重构蛇身

package snake.v3;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import com.sun.glass.events.KeyEvent;

/**
 * 蛇
 * @author  bb
 * @date    2018年10月20日 下午7:58:31
 * @remarks
 */
public class Snake {
	//蛇身
	public ArrayList<Rectangle2D> snakeBody;
	//蛇头坐标
	private int x;
	private int y;
	//格子大小
	private final static int side = 20;
	//第一次按下
	private int count;

	public Snake(){//默认构造
		initSnake();//初始化蛇身
	}
	
	/**
	 * 移动
	 * 1.可动
	 * 2.不能反向动
	 * @param key
	 */
	public void move(int key){
		if(count == 0){//第一次的时候只能按右上下
			if(key == KeyEvent.VK_W || key == KeyEvent.VK_S || key == KeyEvent.VK_D){
				count++;
			}else{
				return;
			}
		}
		switch(key){
		case KeyEvent.VK_W://VK_NUMPAD8 | VK_UP | VK_W | 上
			y -= side;
			break;
		case KeyEvent.VK_S://VK_NUMPAD5 | VK_DOWN | VK_S | 下
			y += side;	
			break;
		case KeyEvent.VK_A://VK_NUMPAD4 | VK_LEFT | VK_A | 左
			x -= side;
			break;
		case KeyEvent.VK_D://VK_NUMPAD6 | VK_RIGHT | VK_D | 右
			x += side;
			break;
		
		}
		reconsitution();//每移动一次重构一次蛇身
	}
	
	/**
	 * 是否死亡
	 * @return
	 */
	public boolean isDie(){
        //撞墙
		boolean isDie = x < 0 || x > 780 || y < 0 || y > 780;
        //撞到自己
		boolean isHit = false;
		out://如果身子有两处是一样的就说明撞到了自己
		for (int i = 0; i < snakeBody.size(); i++) {
			for (int j = 0; j < snakeBody.size(); j++) {
				if(i != j){
					if(snakeBody.get(i).equals(snakeBody.get(j))){
						isHit = true;
						break out;
					}
				}
			}
		}
		if(isDie || isHit) return true;
		return false;
	}
	
	/**
	 * 吃豆子 - 吃一个在尾巴加一个
	 * @param bean
	 * @return
	 */
	public boolean eatBean(Rectangle2D bean){
		if(snakeBody.get(0).equals(bean)){
			snakeBody.add(bean);//说实话这里是懵逼的
			return true;
		}
		return false;
	}
	
	
	/**
	 * 重构蛇身 -因为是矩形数组,所以每次移动都是身体往后覆盖,就相当于蛇头插入下一个将要到的位置
	 */
	public void reconsitution(){
		for (int i = snakeBody.size() - 1; i > 0; i--) {
			snakeBody.get(i).setFrame(snakeBody.get(i - 1));
		}
        //每次移动x, y都会变到下一个位置,所以蛇身往后覆盖,蛇头新赋值
		snakeBody.get(0).setFrame(new Rectangle2D.Double(x, y, side, side));
	}
	
	/**
	 * 初始化蛇身 - 蛇每次都是在同一个地方出生
	 */
	public void initSnake(){
		x = 200;
		y = 200;
		count = 0;
		snakeBody = new ArrayList<>();
		for (int i = 0; i < 5; i++) {
			snakeBody.add(new Rectangle2D.Double(x - i * 20, y, side, side));
		}
	}
	
}

2.面板类

字段:

1.颜色

2.矩形数组(就是用此来操作蛇身)

3.蛇

4.计时器

5.当前按键

6.上次按键

7.豆子

8.分数

9.速度

方法:

1.初始化面板

2.重新游戏

3.改变速度

4.生成豆子

5.画蛇

内部类:时间监听

package snake.v3;
/**
 * 面板类
 * @author  bb
 * @date    2018年10月20日 下午8:03:35
 * @remarks
 */
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class MyPanel extends JPanel {
    //蛇身颜色 - 人称亮瞎您的眼
	private Color[] color = {Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GRAY, Color.GREEN, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.WHITE, Color.YELLOW};
	//蛇身
	private ArrayList<Rectangle2D> snakeBody;
	//蛇 - 调用方法
	private Snake snake;
	//计时器
	private Timer timer;
	//当前按下
	private int currentKey;
	//上次按下
	private int lastKey;
	//豆子
	private Rectangle2D bean;
	//分数
	private int score;
	//速度
	private int speed;
	
    //传入蛇 - 对此蛇进行操作
	public MyPanel(Snake snake){
		initPanel(snake);
		addKeyListener(new MyKeyListener());
	}
	
	/**
	 * 初始化画板
	 * @param snake
	 */
	public void initPanel(Snake snake){
		this.snake = snake;//蛇对象
		this.snakeBody = snake.snakeBody;//蛇身
		generateBean();//生成豆子
		currentKey = -10;//当前按键(赋值是随意的)
		lastKey = -5;//上次按键
		speed = 80;//初始速度
	}
	
	/**
	 * 时间监听
	 * @author  bb
	 * @date    2018年10月20日 下午8:31:33
	 * @remarks
	 */
	public class MyKeyListener extends KeyAdapter{
		@Override
		public void keyPressed(KeyEvent e) {
			currentKey = e.getKeyCode();//获取本次按键
            //用来判断是否按了相反的键
			int abs = Math.abs(currentKey - lastKey);
			//按了上下左右不能按相反或者相同的键
            //***看个人给的按键是什么,通过键的值可以找出差值,就可以防止按键停止,反向走等***
            //如果按了相反或像同的键,就一直按上次按的那个键的方向走就完事
			if(abs == 3 || abs == 0 || abs == 4) {
				currentKey = lastKey;//本次的键就赋值为上次的键
				return;
			}
			if(timer != null) timer.cancel();//每次重新创建计时器,否则会越来越快
			timer = new Timer();
			timer.schedule(new TimerTask(){
				@Override
				public void run() {
					snake.move(currentKey);
					if(snake.isDie()){//判断游戏是否结束
						timer.cancel();
						System.out.println("游戏结束");
						isReGame();//是否重新游戏
					}
					if(snake.eatBean(bean)) generateBean();
					repaint();					
				}
            //麻蛋的,就是这个B的第二个参数,原来一直是(speed, speed),然后感觉这个bug解决不了
            //最后老师说查一下这B,结果懂了,麻蛋
			}, 0, speed);
            //如果按的不是相反的键或者一样的键,就把此次的当成上次的按键
			if(abs != 2 && abs != 0) lastKey = e.getKeyCode();
		}
	}
	
	/**
	 * 是否重新开始游戏
	 */
	public void isReGame(){
        //简单用了JOptionPane做了一下
		int choice = JOptionPane.showConfirmDialog(null, "是否继续");
		if(choice == 0){
			initPanel(new Snake());
		}else{
			System.exit(0);
		}
	}
	
	/**
	 * 改变速度 - 看分数变化速度
	 */
	public void addSpeed(){
		switch(score / 10){
		case 0:
			speed = 70;
			break;
		case 1:
			speed = 60;
			break;
		case 2:
			speed = 50;
			break;
		case 3:
			speed = 40;
			break;
		case 4:
			speed = 30;
			break;
		}
	}
	
	/**
	 * 生成豆子 - 如果生成在蛇身上就换地方生成
	 */
	public void generateBean(){
		while(true){
			int x = (int)(Math.random() * 39);
			int y = (int)(Math.random() * 38);
			//在范围内随机生成豆子
			bean = new Rectangle2D.Double(x * 20, y * 20, 20, 20);
			if(!snakeBody.contains(bean)) break;//如果豆子不在蛇身上就生成此豆子
		}
        //分数是打印的- - 
		System.out.println("当前分数:" + score++);
		addSpeed();
	}
	
	/**
	 * 画蛇但不添足
	 */
	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D gg = (Graphics2D)g;
		if(bean != null) gg.draw(bean);
		for (Rectangle2D r : snakeBody) {
            //颜色闪烁
			int num = (int)(Math.random() * color.length);
			gg.setColor(color[num]);
			gg.fill(r);
		}
	}
	
	@Override
	public Dimension getPreferredSize() {
		return new Dimension(800, 800);
	}
	
}

最后就是实现类了:

package snake.v3;

import java.awt.event.KeyEvent;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;

import javafx.util.Pair;

/**
 * 测试类
 * @author  bb
 * @date    2018年10月20日 下午8:08:39
 * @remarks
 */
public class Test {
	public static void main(String[] args) {
		JFrame frame = new JFrame("贪吃蛇");
		MyPanel panel = new MyPanel(new Snake());
		//设置按键则就退出程序
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//获得焦点
		panel.setFocusable(true);
		frame.add(panel);
        //设置不可变更大小
		frame.setResizable(false);
		frame.pack();
		frame.setLayout(null);
        //居中
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);	
	}
}

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

以上就是这次的小任务了

过程:上周的那三天过的真实苦逼,每天做一个功能我都想死了,一开始想着动起来,动了就想着自动走路,然后又想着正常游戏,因为一直这么想,所以都没用上对象的思想,直接一个类给干完了(其实Timer这个类听都没听过,借鉴网上的俄罗斯方块看到这个方法,兴奋的直接用,查都没查,所以就遗漏了那个bug,真滴蠢了),这次教训真实血淋淋,以后学到新的类一定要把内容给看一下,用的方法一定要了解清楚,这次就栽在了最开始上,哈哈哈哈!!!

还有就是做项目前一定要构思、画类图、画思维图,构思、画类图、画思维图,构思、画类图、画思维图,重要的事情说三遍!!

这是老师第一次布置的小任务,虽然完成的不是很完美,但是过程真是舒服,懵逼一整天,然后做出一个小功能,开心几分钟,又继续懵逼状态,真滴蛇皮,循环几天,不过最后做完真是美滋滋!!!!

加油加油bb!!!!

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值