黑马程序员技术交流社区

标题: 贪吃蛇的java源码分析(四) [打印本页]

作者: EnterEn_RllVn    时间: 2016-6-9 18:41
标题: 贪吃蛇的java源码分析(四)
接下来我们需要实现eat()方法。其实这个方法的思路很简单,就是往集合中新添加一个元素,然后将蛇身中每一个元素的坐标向前进一位。稍加思索,我们就可以理解:除了集合的第一个元素,集合的剩余元素就是前一个集合的排列,无论顺序,坐标还有颜色都相同。我们在把随机点的坐标赋给集合的第一个元素,那么集合的吞吃功能就完成了,吃掉的点变成了蛇头。代码体现:
  1. public void eat() {
  2.         snake.add(new snakeNode());//往集合中新增加一个元素,不用具体赋值
  3.         Length++;
  4.         for(int x = Length-1; x >0; x--) {
  5.                         snake.get(x).setX(snake.get(x-1).getX());
  6.                         snake.get(x).setY(snake.get(x-1).getY());
  7.                         snake.get(x).setColor(snake.get(x-1).getColor());//变化坐标时,颜色也要进行变换,这样顺序才能一致
  8.                 }
  9.                 snake.get(0).setX(newNode.getX());
  10.                 snake.get(0).setY(newNode.getY());
  11.                 snake.get(0).setColor(newNode.getColor());
  12.                 CreateNode();//吞吃完毕后要继续创造新的随机点,让游戏得以继续
  13.                 }
  14.                
  15. ```
复制代码

这里有一个细节问题要注意**。在Move()方法匹配调用eat()方法后,一定要使用return退出Move()方法。因为如果不退出,那么eat()方法会把随机点的坐标赋值给蛇头,然后程序会继续运行。当运行到if语句查看是否撞到自己的方法时,由于之前定义的firstX与firstY的值与随机点的值相同,那么蛇头的第一个元素的值也就与firstX与firstY的值相同,这就会符合if语句的条件,导致出现不必要的错误。这是一个很隐蔽的错误,我之前在这里卡主了很长时间,希望大家能好好理解这一点**。之前我们还定义过一个Dead方法,用于在游戏结束时弹出相关界面。这个方法相对而言比较简单,我直接贴出代码:

  1. public void Dead(String str) {//弹出当前的时间,并提示游戏结束
  2.         Date date = new Date();
  3.         SimpleDateFormat sd = new SimpleDateFormat();
  4.         String str2 = sd.format(date);
  5.         String str3 = str + "\n" + "很遗憾,游戏要结束了~~~";
  6.         JOptionPane.showMessageDialog(this, str2 + "\n" + str3        );
  7.                 System.exit(0);
  8.         }
  9. ```
复制代码

目前来说我们已经完成了全部的贪吃蛇代码,是不是很简单?一开始做的时候没有什么思路,但随着我们一步一步分析,整个项目的流程也就非常清晰。最后我们还要完善两个方面,第一个是每当我们的集合发生位置移动时,我们需要调用repaint()方法进行重绘,防止出现坐标变化时的残留现象。在我们的代码要对集合中元素的坐标产生改变时,就调用repaint()方法进行重绘,防止可能出现的残留或者闪烁现象。第二点就是我们目前只是在坐标轴上进行移动,无法直接在图案上观测到。如何画出贪吃蛇的图形?这里就要用到java绘图类——paint()方法。java中任何一个图形界面,都需要paint函数来负责专门显现。paint()方法一般由父类自动维护,一旦子类重写,子类就必须自己完成所有的界面显示工作。paint()有三个受保护的方法,我们因为是要绘制组件,所以调用PaintComponent()方法即可。具体的绘制思路就是以每一个snakeNode为圆心,成员变量中定义的unit为半径画园,将贪吃蛇的图形全部绘制出来。之后再以width,length,两者乘以unit来做一个矩形。因为集合中元素与随机产生的元素都在width与length的限制中,所以当绘制的圆碰到绘制的边框时,就代表着集合中的元素与边框(width,length)产生了交界,到达了边界值,在移动就会超出边界,游戏也就会失败。

  1. protected void paintComponent(Graphics g) {
  2.                 super.paintComponent(g);//调用super是因为文中调用了repaint方法,需要每一次都清空再进行重绘
  3.                 g.setColor(newNode.getColor());
  4.                 g.fillOval(newNode.getX()*unit, newNode.getY()*unit, unit, unit);
  5.                 g.setColor(newNode.getColor());
  6.                 g.drawRect(0, 0, width*unit, length*unit);
  7.                 for(int x = 0; x < Length; x++) {
  8.                         g.setColor(snake.get(x).getColor());
  9.                         g.fillOval(snake.get(x).getX()*unit, snake.get(x).getY()*unit, unit, unit);
  10.                 }
  11.                
  12.         }
  13. ```
复制代码

最后给出完整的实现代码:

  1. package game;

  2. import java.awt.Color;

  3. public class SnakeNode {//定义蛇身集合中的各个元素点
  4.         private int x;
  5.         private int y;
  6.         private Color color;
  7.         public SnakeNode() {
  8.                 super();
  9.                
  10.         }
  11.         public SnakeNode(int x, int y, Color color) {
  12.                 super();
  13.                 this.x = x;
  14.                 this.y = y;
  15.                 this.color = color;
  16.         }
  17.         public int getX() {
  18.                 return x;
  19.         }
  20.         public void setX(int x) {
  21.                 this.x = x;
  22.         }
  23.         public int getY() {
  24.                 return y;
  25.         }
  26.         public void setY(int y) {
  27.                 this.y = y;
  28.         }
  29.         public Color getColor() {
  30.                 return color;
  31.         }
  32.         public void setColor(Color color) {
  33.                 this.color = color;
  34.         }
  35.        
  36.        

  37. }

  38. ```

  39. ```
  40. package game;

  41. import java.awt.Color;
  42. import java.awt.Graphics;
  43. import java.awt.event.ActionEvent;
  44. import java.awt.event.ActionListener;
  45. import java.awt.event.KeyAdapter;
  46. import java.awt.event.KeyEvent;
  47. import java.text.SimpleDateFormat;
  48. import java.util.ArrayList;
  49. import java.util.Date;
  50. import java.util.Random;

  51. import javax.swing.JOptionPane;
  52. import javax.swing.JPanel;
  53. import javax.swing.Timer;

  54. public class MainGame extends JPanel{
  55.         private final int length = 20;//定义活动范围
  56.         private final int width = 30;//定义活动范围
  57.         private final int unit = 20;//定义单位长度
  58.         private ArrayList<SnakeNode> snake = new ArrayList<>();//定义蛇身的集合
  59.         private int Direction;//定义蛇头的方向
  60.         private int Length ;//定义蛇身的长度
  61.         private SnakeNode newNode = new SnakeNode(1,1,Color.BLACK);//定义随机点
  62.         Timer time = new Timer(1000,new ThingsListener());
  63.        
  64.        

  65.         public MainGame() {//初始化各项数据与方法
  66.                 snake.add(new SnakeNode(width/2,length/2,Color.GREEN));
  67.                 snake.add(new SnakeNode(width/2,length/2+1,Color.BLUE));
  68.                 snake.add(new SnakeNode(width/2,length/2+2,Color.RED));
  69.                
  70.                 Direction = 1;//定义初始方向为向上
  71.                 Length = 3;//蛇身长度为3
  72.                 CreateNode();//产生随机点
  73.                 time.start();
  74.                
  75.                 this.addKeyListener(new KeyAdapter() {//捕捉键盘的按键事件
  76.                         public void keyPressed(KeyEvent e) {
  77.                                 int direction = 0;//定义一个按下按钮后要去的方向
  78.                                 switch(e.getKeyCode()) {
  79.                                         case KeyEvent.VK_UP://按下向上,返回1
  80.                                                 direction = 1;
  81.                                                 break;
  82.                                         case KeyEvent.VK_DOWN://按下向下,返回-1
  83.                                                 direction = -1;
  84.                                                 break;
  85.                                         case KeyEvent.VK_LEFT://按下相左,返回2
  86.                                                 direction = 2;
  87.                                                 break;
  88.                                         case KeyEvent.VK_RIGHT://按下向右,返回-2
  89.                                                 direction = -2;
  90.                                                 break;
  91.                                         default:
  92.                                                 break;
  93.                                 }
  94.                                 if(direction + Direction !=0) {//不能反向运动
  95.                                         Direction = direction;
  96.                                         Move(direction);
  97.                                         repaint();
  98.                                 }
  99.                         }
  100.                 });
  101.                
  102.         }
  103.         public void Move(int direction) {//定义蛇身移动的方法
  104.                 int FirstX = snake.get(0).getX();//获取蛇第一个点
  105.                 int FirstY = snake.get(0).getY();//获取蛇第二个点
  106.                
  107.                 switch(direction) {
  108.                         case 1:
  109.                                 FirstY--;
  110.                                 break;
  111.                         case -1:
  112.                                 FirstY++;
  113.                                 break;
  114.                         case 2:
  115.                                 FirstX--;
  116.                                 break;
  117.                         case -2:
  118.                                 FirstX++;
  119.                                 break;
  120.                         default:
  121.                                 break;
  122.                 }
  123.                
  124.                 if(FirstX == newNode.getX()&&FirstY == newNode.getY()) {//当碰到随机点时
  125.                         getNode();
  126.                         return;
  127.                 }
  128.                
  129.                 for(int x = 0; x < Length; x++) {//当碰到蛇身自己时
  130.                         if((FirstX==snake.get(x).getX())&&(FirstY == snake.get(x).getY())) {
  131.                                 Dead("你碰到自己啦~~~");
  132.                         }
  133.                 }
  134.                
  135.                 if(FirstX < 0 || FirstX > width-1  || FirstY < 0 || FirstY > length -1) {
  136.                         Dead("菜鸡,你撞墙啦~~~~~");
  137.                 }
  138.                
  139.                
  140.                
  141.                 for(int x = Length - 1; x > 0; x--) {
  142.                         snake.get(x).setX(snake.get(x-1).getX());
  143.                         snake.get(x).setY(snake.get(x-1).getY());
  144.                 }
  145.                 snake.get(0).setX(FirstX);
  146.                 snake.get(0).setY(FirstY);
  147.                 repaint();
  148.         }
  149.        
  150.         public void getNode() {
  151.                 snake.add(new SnakeNode());
  152.                 Length++;
  153.                 for(int x = Length-1; x >0; x--) {
  154.                         snake.get(x).setX(snake.get(x-1).getX());
  155.                         snake.get(x).setY(snake.get(x-1).getY());
  156.                         snake.get(x).setColor(snake.get(x-1).getColor());
  157.                 }
  158.                 snake.get(0).setX(newNode.getX());
  159.                 snake.get(0).setY(newNode.getY());
  160.                 snake.get(0).setColor(newNode.getColor());
  161.                 CreateNode();
  162.                 repaint();
  163.                
  164.                
  165.         }
  166.        
  167.         public void Dead(String s) {
  168.                 Date date = new Date();
  169.                 SimpleDateFormat sd = new SimpleDateFormat();
  170.                 String str2 = sd.format(date);
  171.                 String str = s +"\n" +"所以说游戏不得已将结束了";
  172.                 JOptionPane.showMessageDialog(this, str2 + "\n" + str        );
  173.                 System.exit(0);
  174.         }
  175.        
  176.         public void CreateNode() {//创造随机点的方法
  177.                 int newX = 0;
  178.                 int newY = 0;
  179.                 Boolean flag = true;
  180.                 while(flag) {
  181.                 newX = new Random().nextInt(width);
  182.                 newY = new Random().nextInt(length);
  183.                
  184.                 for(int i = 0; i < Length; i++) {
  185.                         if(snake.get(i).getX()==newX && snake.get(i).getY()==newY) {
  186.                                 flag = true;
  187.                                 break;
  188.                         }
  189.                                 flag= false;
  190.                         }
  191.                 }
  192.                
  193.                 Color color = new Color(new Random().nextInt(255),new Random().nextInt(255),new Random().nextInt(255));
  194.                 newNode.setX(newX);
  195.                 newNode.setY(newY);
  196.                 newNode.setColor(color);
  197.                 this.setBackground(new Color(new Random().nextInt(255),new Random().nextInt(255),new Random().nextInt(255)));//这里给画板的背景换随机色
  198.         }
  199.        
  200.         class ThingsListener implements ActionListener {//设置一个监听器事件
  201.                 public void actionPerformed(ActionEvent e) {
  202.                         Move(Direction);
  203.                         repaint();
  204.                 }
  205.         }
  206.        
  207.         protected void paintComponent(Graphics g) {
  208.                 super.paintComponent(g);
  209.                 g.setColor(newNode.getColor());
  210.                 g.fillOval(newNode.getX()*unit, newNode.getY()*unit, unit, unit);
  211.                 g.setColor(newNode.getColor());
  212.                 g.drawRect(0, 0, width*unit, length*unit);
  213.                 for(int x = 0; x < Length; x++) {
  214.                         g.setColor(snake.get(x).getColor());
  215.                         g.fillOval(snake.get(x).getX()*unit, snake.get(x).getY()*unit, unit, unit);
  216.                 }
  217.                
  218.         }
  219.        
  220.                
  221.        

  222. }

  223. ```

  224. ```
  225. package game;

  226. import java.awt.Color;

  227. import javax.swing.JFrame;

  228. public class Test {
  229.         public static void main(String[] args) {
  230.                 JFrame frame = new JFrame("贪吃蛇————————————made by chenjiaheng");
  231.                 frame.setBounds(0,0,800,500);
  232.                 MainGame sn = new MainGame();
  233.                 frame.add(sn);
  234.                 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  235.                 frame.setVisible(true);
  236.                 sn.requestFocus();
  237.         }

  238. }

  239. ```
复制代码

写到这里,整个贪吃蛇的项目,包括思路,实现,代码都算是完成了。其实代码如何实现并不困难,重要的是如何通过方法和技巧将大问题分解成一个一个小问题,最后再加以解决。在这里完成的只是贪吃蛇的基本功能,在以后自己可能会继续实现更多的功能,包括插入图片,加入排行榜,记录个数等功能。从0到1,从无到有,希望自己的文章能给各位朋友带来帮助。如果有什么好的想法和思路,自己也会继续在博客中和大家分享。


作者: hero_king    时间: 2016-6-9 20:24
不错不错,学习了




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2