前言:上周二老师突然给布置了一个任务,让做贪吃蛇,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!!!!