之前写了程序分析,接下来进入程序编写,这次写程序跟之前的项目不一样,之前是新增一个功能,就根据这个功能新增一部分内容,而这次有了程序分析,我打算即使还没有加入新功能,也预留好新功能需要的各种属性和方法,即使某个方法还没有实现,也会在应该调用它的地方以注释的形式加入调用语句。

V0.1:这个版本主要是绘制界面。根据程序设计中的属性和方法,实现如下:

SnakeFrame.java
import java.awt.*;
 import java.awt.event.*;public class SnakeFrame
 {
  public static void main(String[] args){
   new SnakeWindow("Snake").launch();
  }
 }class SnakeWindow extends Frame
 {
  public static final int WINDOW_W = 800;
  public static final int WINDOW_H = 600;
  public static final int UNIT_SIZE = 20;
  public static final int SPEED = 1000;//刷新速度 //private Snake snake = null;
  //private Bean bean = null; public SnakeWindow(String s){
   super(s);
  } public void launch(){
   setLocation(100,100);
   setSize(WINDOW_W,WINDOW_H);
   setBackground(Color.GRAY);
   addWindowListener(new WindowAdapter(){
    public void windowClosing (WindowEvent e){
     System.exit(0);
    }
   });
   addKeyListener(new KeyMonitor());
   setVisible(true);  new Thread(new PaintThread()).start();
  } public void paint(Graphics g){
   Color c = g.getColor();
   g.setColor(Color.DARK_GRAY);
   for(int i = 1; i < WINDOW_H/UNIT_SIZE ; i++) {
    g.drawLine(0,UNIT_SIZE * i,WINDOW_W ,UNIT_SIZE * i);
   }
   for (int i = 1; i < WINDOW_W/UNIT_SIZE ; i++ )
   {
    g.drawLine(UNIT_SIZE * i, 0, UNIT_SIZE * i, WINDOW_H);
   }
   g.setColor(c);
  } class KeyMonitor extends KeyAdapter
  {
   public void keyPressed (KeyEvent e){
    //snake.keyPressed(e);
   }
  }
  
  class PaintThread implements Runnable
  {  repaint();
   public void run() {
    while(true){
     repaint();
     try{
      Thread.sleep(SPEED);
     }catch (InterruptedException e){
      e.printStackTrace();
     }
    }
   } }
 }

 V0.2:这个版本里先把比较容易实现的豆类加入进去,另外,为了培养一个良好的习惯,从这个版本开始加入各种注释,有些地方有些失误,给private 也加上了文档注释

在实现豆类的时候,发现用坐标产生一个豆并不是特别方便,因为有可能产生在界面的两个格子之间,所以我改换x,y为豆所在格子的行列,row、column

比较复杂的是产生新豆方法newBean(),这里需要添加一个随机数产生器,用来随机产生新豆的位置,同时还要判断新位置是否为空,只有空位置才能添加新豆。若是一时不好实现,我们可以暂时先空着,仅仅声明该方法。

根据程序分析设计中的豆类描述,写好豆类之后,再在主类中添加豆类对象,修改paint方法,加入豆类的draw方法。具体代码如下:

SnakeFrame.java
import java.awt.*;
 import java.awt.event.*;public class SnakeFrame
 {
  public static void main(String[] args){
   new SnakeWindow("Snake").launch();
  }
 }class SnakeWindow extends Frame
 {
  /**
   * 窗口宽度
   */
  public static final int WINDOW_W = 800;
  /**
   * 窗口高度
   */
  public static final int WINDOW_H = 600;
  /**
   * 正方形单元格边长
   */
  public static final int UNIT_SIZE = 20;
  /**
   * 界面行数
   */
  public static final int ROWS = WINDOW_H/UNIT_SIZE;
  /**
   * 界面列数
   */
  public static final int COLUMNS = WINDOW_W/UNIT_SIZE;
  /**
   * 界面每隔SPEED毫秒刷新一次
   */
  public static final int SPEED = 1000;//刷新速度 //private Snake snake = null;
  private Bean bean = Bean.newBean();
  
  /**
   * 构造方法
   * @param s:标题栏名称
   */
  public SnakeWindow(String s){
   super(s);
  } /**
   * 窗口初始化
   */
  public void launch(){
   setLocation(100,100);
   setSize(WINDOW_W,WINDOW_H);
   setBackground(Color.GRAY);
   addWindowListener(new WindowAdapter(){
    public void windowClosing (WindowEvent e){
     System.exit(0);
    }
   });
   addKeyListener(new KeyMonitor());
   setVisible(true);  new Thread(new PaintThread()).start();
  } /**
   * 窗口绘制方法
   */
  public void paint(Graphics g){
   Color c = g.getColor();
   g.setColor(Color.DARK_GRAY);
   for(int i = 1; i < WINDOW_H/UNIT_SIZE ; i++) {
    g.drawLine(0,UNIT_SIZE * i,WINDOW_W ,UNIT_SIZE * i);
   }
   for (int i = 1; i < WINDOW_W/UNIT_SIZE ; i++ )
   {
    g.drawLine(UNIT_SIZE * i, 0, UNIT_SIZE * i, WINDOW_H);
   }
   bean.draw(g);
   g.setColor(c);
  } /**
   * 键盘事件监听器类
   */
  class KeyMonitor extends KeyAdapter
  {
   /**
    * 键盘按下时调用该方法
    * @param e:键盘事件
    */
   public void keyPressed (KeyEvent e){
    //snake.keyPressed(e);
   }
  }
  
  /**
   * 界面刷新线程类
   */
  class PaintThread implements Runnable
  {  repaint();
   public void run() {
    while(true){
     repaint();
     try{
      Thread.sleep(SPEED);
     }catch (InterruptedException e){
      e.printStackTrace();
     }
    }
   } }
 }Bean.java
import java.awt.*;
 import java.util.*;/**
  * 豆类
  */
 public class Bean
 {
  /**
   * 豆子的直径
   */
  public static final int SIZE = 20;
 //豆横坐标 private int row = 0;
  
  // 豆纵坐标
  private int column = 0;
  /**
   * 随机数产生器
   */
  static Random r = new Random(); /*
   * 私有的构造方法,不许别的类调用
   */
  private Bean(int col, int row){
   this.row = row;
   this.column = col;
  } /**
   * 豆的绘制方法
   */
  public void draw(Graphics g){
   Color c = g.getColor();
   g.setColor(Color.GREEN);
   g.fillOval(column * SnakeWindow.UNIT_SIZE, row * SnakeWindow.UNIT_SIZE, SIZE, SIZE);
   g.setColor(c);
  } /**
   * 配合eat方法完成检测
   */
  public Rectangle getRectangle(){
   return new Rectangle(column * SnakeWindow.UNIT_SIZE, row * SnakeWindow.UNIT_SIZE, SIZE, SIZE);
  }
  
  /**
   * 静态方法,用来产生一个新豆
   */
  public static Bean newBean(){
   Bean b = new Bean(r.nextInt(SnakeWindow.COLUMNS), r.nextInt(SnakeWindow.ROWS));
   //缺省判断逻辑,该位置是否为空
   return b;
  }
 }

 V0.3:在这个版本中将蛇类添加进去

蛇类稍微有些麻烦,我们可以先写出一个静止不动的蛇,其他动态的方法实现暂时为空。实现之后再写move方法动起来,再写keyPressed方法控制方向,再之后写eat方法实现吃豆,再之后则是碰撞检测,撞墙死和撞自己死。写完方向控制之后的代码如下:

SnakeFrame.java
import java.awt.*;
 import java.awt.event.*;public class SnakeFrame
 {
  public static void main(String[] args){
   new SnakeWindow("Snake").launch();
  }
 }class SnakeWindow extends Frame
 {
  /**
   * 窗口宽度
   */
  public static final int WINDOW_W = 800;
  /**
   * 窗口高度
   */
  public static final int WINDOW_H = 600;
  /**
   * 正方形单元格边长
   */
  public static final int UNIT_SIZE = 20;
  /**
   * 界面行数
   */
  public static final int ROWS = WINDOW_H/UNIT_SIZE;
  /**
   * 界面列数
   */
  public static final int COLUMNS = WINDOW_W/UNIT_SIZE;
  /**
   * 界面每隔SPEED毫秒刷新一次
   */
  public static final int SPEED = 500;//刷新速度
  /**
   * 蛇类对象
   */
  private Snake snake = new Snake();
  /**
   * 豆类对象
   */
  private Bean bean = Bean.newBean();
  
  /**
   * 构造方法
   * @param s:标题栏名称
   */
  public SnakeWindow(String s){
   super(s);
  } /**
   * 窗口初始化
   */
  public void launch(){
   setLocation(100,100);
   setSize(WINDOW_W,WINDOW_H);
   setBackground(Color.GRAY);
   addWindowListener(new WindowAdapter(){
    public void windowClosing (WindowEvent e){
     System.exit(0);
    }
   });
   addKeyListener(new KeyMonitor());
   setVisible(true);  new Thread(new PaintThread()).start();
  } /**
   * 窗口绘制方法
   */
  public void paint(Graphics g){
   Color c = g.getColor();
   g.setColor(Color.DARK_GRAY);
   for(int i = 1; i < WINDOW_H/UNIT_SIZE ; i++) {
    g.drawLine(0,UNIT_SIZE * i,WINDOW_W ,UNIT_SIZE * i);
   }
   for (int i = 1; i < WINDOW_W/UNIT_SIZE ; i++ )
   {
    g.drawLine(UNIT_SIZE * i, 0, UNIT_SIZE * i, WINDOW_H);
   }
   snake.draw(g);
   bean.draw(g);
   g.setColor(c);
  } /**
   * 键盘事件监听器类
   */
  class KeyMonitor extends KeyAdapter
  {
   /**
    * 键盘按下时调用该方法
    * @param e:键盘事件
    */
   public void keyPressed (KeyEvent e){
    snake.keyPressed(e);
   }
  }
  
  /**
   * 界面刷新线程类
   */
  class PaintThread implements Runnable
  {
   public void run() {
    while(true){
     repaint();
     try{
      Thread.sleep(SPEED);
     }catch (InterruptedException e){
      e.printStackTrace();
     }
    }
   }
  }
 }Snake.java
import java.awt.*;
 import java.awt.event.*;
 import java.util.*;public class Snake
 {
  /**
   * 盛放组成蛇的各个节点
   */
  public LinkedList<Node> nodeList = new LinkedList<Node>();
  /**
   * 蛇头行进的方向
   */
  private Direction direction = Direction.Left; /**
   * 方向枚举类型
   */
  public enum Direction {Up,Down,Left,Right} /**
   * 构造方法
   */
  public Snake(){
   nodeList.addFirst(new Node(19,14));
  } /**
   * 自身绘制
   */
  public void draw(Graphics g){
   move();  Color c = g.getColor();
   g.setColor(Color.GREEN);
   for(int i = 0; i < nodeList.size(); i++){
    Node n = nodeList.get(i);
    n.draw(g);
   }
   g.setColor(c);
  } /**
   * 根据方向移动
   */
  public void move() {
   switch(direction){
   case Up :
    nodeList.addFirst(new Node(nodeList.getFirst().getColumn(), nodeList.getFirst().getRow() - 1));
    break;
   case Down :
    nodeList.addFirst(new Node(nodeList.getFirst().getColumn(), nodeList.getFirst().getRow() + 1));
    break;
   case Left :
    nodeList.addFirst(new Node(nodeList.getFirst().getColumn() - 1, nodeList.getFirst().getRow()));
    break;
   case Right :
    nodeList.addFirst(new Node(nodeList.getFirst().getColumn() + 1, nodeList.getFirst().getRow()));
    break;
   }
   nodeList.removeLast();
  } public void eat() {
   //
  } public void addNode() {
   //
  } public void collide () {
   //
  } /**
   * 方向键按键事件的处理,改变移动方向
   */
  public void keyPressed (KeyEvent e) {
   int keyCode = e.getKeyCode();
   switch(keyCode){
   case KeyEvent.VK_UP :
    direction = Direction.Up;
    break;
   case KeyEvent.VK_DOWN :
    direction = Direction.Down;
    break;
   case KeyEvent.VK_LEFT :
    direction = Direction.Left;
    break;
   case KeyEvent.VK_RIGHT :
    direction = Direction.Right;
    break;
   }
  } /**
   * 节点类
   */
  class Node
  {
   /**
    * 豆子列坐标
    */
   private int row = 0;
   /**
    * 豆子行坐标
    */
   private int column = 0;
   /**
    * 豆子行坐标
    */
   public static final int SIZE = SnakeWindow.UNIT_SIZE;  /**
    * 私有的构造方法,不许别的类调用
    */
   public Node(int col, int row){
    this.row = row;
    this.column = col;
   }  public int getRow(){
    return row;
   }  public int getColumn() {
    return column;
   }  /**
    * 节点的绘制方法
    */
   public void draw(Graphics g){
    Color c = g.getColor();
    g.setColor(Color.BLACK);
    g.fillRect(column * SnakeWindow.UNIT_SIZE, row * SnakeWindow.UNIT_SIZE, SIZE, SIZE);
    g.setColor(c);
   }  /**
    * 配合完成碰撞检测
    */
   public Rectangle getRectangle(){
    return new Rectangle(column * SnakeWindow.UNIT_SIZE, row * SnakeWindow.UNIT_SIZE, SIZE, SIZE);
   }
  }
 }Bean.java
import java.awt.*;
 import java.util.*;/**
  * 豆类
  */
 public class Bean
 {
  /**
   * 豆子的直径
   */
  public static final int SIZE = SnakeWindow.UNIT_SIZE;
  /**
   * 豆子行坐标
   */
  private int row = 0;
  /**
   * 豆子列坐标
   */
  private int column = 0;
  /**
   * 随机数产生器
   */
  static Random r = new Random(); /**
   * 私有的构造方法,不许别的类调用
   */
  private Bean(int col, int row){
   this.row = row;
   this.column = col;
  } /**
   * 豆的绘制方法
   */
  public void draw(Graphics g){
   Color c = g.getColor();
   g.setColor(Color.GREEN);
   g.fillOval(column * SnakeWindow.UNIT_SIZE, row * SnakeWindow.UNIT_SIZE, SIZE, SIZE);
   g.setColor(c);
  } /**
   * 配合eat方法完成检测
   */
  public Rectangle getRectangle(){
   return new Rectangle(column * SnakeWindow.UNIT_SIZE, row * SnakeWindow.UNIT_SIZE, SIZE, SIZE);
  }
  
  /**
   * 静态方法,用来产生一个新豆
   */
  public static Bean newBean(){
   Bean b = new Bean(r.nextInt(SnakeWindow.COLUMNS), r.nextInt(SnakeWindow.ROWS));
   //缺省判断逻辑,该位置是否为空
   return b;
  }
 }