之前寫了程序分析,接下來進入程序編寫,這次寫程序跟之前的項目不一樣,之前是新增一個功能,就根據這個功能新增一部分內容,而這次有了程序分析,我打算即使還沒有加入新功能,也預留好新功能需要的各種屬性和方法,即使某個方法還沒有實現,也會在應該調用它的地方以註釋的形式加入調用語句。
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;
}
}
暫時寫到這,太長換下一篇