之前写了程序分析,接下来进入程序编写,这次写程序跟之前的项目不一样,之前是新增一个功能,就根据这个功能新增一部分内容,而这次有了程序分析,我打算即使还没有加入新功能,也预留好新功能需要的各种属性和方法,即使某个方法还没有实现,也会在应该调用它的地方以注释的形式加入调用语句。
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
/**
* 蛇头行进的方向
*/
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;
}
}
暂时写到这,太长换下一篇