北京电子科技学院(BESTI)
实 验 报 告
课程:Java程序设计 班级:1351 姓名:刘帅 学号:20135304
成绩: 指导教师:娄佳鹏 实验日期:2015.6.3
实验密级: 预习程度: 实验时间:15:00-21:00
仪器组次:10 必修/选修: 实验序号:3
实验名称: Java敏捷开发XP实践
实验内容:
1. XP基础
2. XP核心实践
3. 相关工具
实验仪器:
名称 |
型号 |
数量 |
PC |
MacBook(win7) |
1 |
虚拟Linux系统 |
实验楼虚拟机 |
1 |
实验步骤:
(一)敏捷开发与XP:
XP准则:
1.沟通 :XP认为项目成员之间的沟通是项目成功的关键,并把沟通看作项目中间协调与合作的主要推动因素。
2.简单 :XP假定未来不能可靠地预测,在现在考虑它从经济上是不明智的,所以不应该过多考虑未来的问题而是应该集中力量解决燃眉之急。
3.反馈 :XP认为系统本身及其代码是报告系统开发进度和状态的可靠依据。系统开发状态的反馈可以作为一种确定系统开发进度和决定系统下一步开发方向的手段。
4.勇气:代表了XP认为人是软件开发中最重要的一个方面的观点。在一个软件产品的开发中人的参与贯穿其整个生命周期,是人的勇气来排除困境,让团队把局部的最优抛之脑后,达到更重大的目标。表明了XP对“人让项目取得成功”的基本信任态度。
XP软件开发的活动:编码、测试、倾听、设计。其中的编码标准,结对编程,代码集体所有,测试,重构等实践过程较为重要。
(二)编码标准
编程标准包含:具有说明性的名字、清晰的表达式、直截了当的控制流、可读的代码和注释,以及在追求这些内容时一致地使用某些规则和惯用法的重要
1、缩进
【source->Format】 或用快捷键【Ctrl+Shift+F】就可以按Eclipse规定的规范缩进
2、命名规则
要体现各自的含义
包、类、变量用名词
方法名用动宾
包名全部小写,如:io,awt
类名第一个字母要大写,如:HelloWorldApp
变量名第一个字母要小写,如:userName
方法名第一个字母要小写:setName
标识符名字应当直观且可以拼读
一般采用英文单词或其组合,便于记忆和阅读
标识符的长度“min-length && max-information”的原则:去元音法
一般全局变量用具有说明性的名字,局部变量用短名字:单字符的名字,常见的如i,j,k等用作局部变量
3、代码标准:七个原则
不要预先花费太多时间
目标应该是团队中没有人辨认各自的代码
以团队为单位对某一标准达成协议,然后遵守这一标准
不是事无巨细的规则列表,而是去本报代码可交流的指导方针
编码标准开始时应该很简单,然后根据团队经验逐步优化
创建能够工作的最简单标准,然后逐步发展
只制定适合本团队的
(三)结对编程
结对编程是XP中的重要实践。在结对编程模式下,一对程序员肩并肩、平等地、互补地进行开发工作。
1、两个角色:
驾驶员(Driver)是控制键盘输入的人。
领航员(Navigator)起到领航、提醒的作用。
2、如何和为何
• 驾驶员:写设计文档,进行编码和单元测试等XP开发流程。
• 领航员:审阅驾驶员的文档、驾驶员对编码等开发流程的执行;考虑单元测试的覆盖率;思考是否需要和如何重构;帮助驾驶员解决具体的技术问题。
• 驾驶员和领航员不断轮换角色,不要连续工作超过一小时,每工作一小时休息15分钟。领航员要控制时间。
• 主动参与。任何一个任务都首先是两个人的责任,也是所有人的责任。没有“我的代码”、“你的代码”或“他/她的代码”,只有“我们的代码”。
• 只有水平上的差距,没有级别上的差异。两人结对,尽管可能大家的级别资历不同,但不管在分析、设计或编码上,双方都拥有平等的决策权利。
(四)版本控制
我的代码库:http://git.shiyanlou.com/shiyanloucs20135104
1、好处
• 版本控制提供项目级的 undo(撤销) 功能: 没有什么事情是终结版本, 任何错误必须很容易回滚。 假设你在使用世界上最复杂的文字处理系统。 它具备了所有的能想到的功能,就是没有支持 DELETE(删除) 键。想象你打字的时候得多么的谨慎和缓慢吧, 特别是一篇超大的文档的快临近末尾的时候, 一个不小心就要重头再来(试想你选中所有的文字, 不小心按了 DELETE 键, 因为没有撤销功能,只好重新录入)。编辑文字和版本控制相同,任何时候都需要回滚,无论是一个小时, 一天, 还是一周, 这让你的团队工作自由快速的工作, 而且对于修正错误也非常自信。
• 版本控制允许多人在同一代码上工作, 只要遵守一定的控制原则就行。 再也不会发生诸如一个人覆盖了另一个人编辑的代码,导致那个人的修改无效这样的情况。
• 版本控制系统保存了过去所作的修改的历史记录。如果你遭遇到一些惊讶的代码,通过版本控制系统可以很容易找出是谁干的, 修改了什么, 修改的时间, 如果幸运的话,还能找出原因。
• 版本控制系统还支持在主线上开发的同时发布多个软件版本。在软件发布的时候也不需要整个团队的停止工作,不需要冻结代码。
• 版本控制也是项目级的时间机器,你可以选择任何一个时间, 精确地查看项目在当时的情况。 这对研究非常有用, 也是重现以前某个有问题的发布版本的基础。
2、举例:Hello World.java
进入Code目录,进入shiyanlou_cs212,创建HelloWorld目录,创建并编辑HelloWorld.java文件,然后将其存放到代码库中
(五)重构:
重构(Refactor),就是在不改变软件外部行为的基础上,改变软件内部的结构,使其更加易于阅读、易于维护和易于变更.
先进行重命名操作
再进行类中元素的封装操作
之后进行ToString方法的封装
一个完整的重构流程包括:
1. 从版本控制系统代码库中Check out code
2. 读懂代码(包括测试代码)
3. 发现bad smell
4. Refactoring
5. 运行所有的Unit Tests
6. 往代码库中Check in code
(六)实践项目
此次试验中,我们结对的游戏项目是坦克大战
1.代码库地址:http://git.shiyanlou.com/shiyanloucs20135104
2.游戏界面如下:
3.部分源代码如下:
子弹类:
import java.awt.Color; import java.awt.Graphics; import java.awt.Rectangle; import java.util.List; /** * 这个类用于生成子弹,及已控制子弹。 * */ public class Bullet {
private int x, y; //标记子弹的初始位置。 private static final int WIDTH = 10; //子弹的宽度。 private static final int HEIGHT = 10; //子弹的高度。 private Direction dir; //子弹的方向,与对应的坦克保持一致。 private static final int BSEEPD = 15; //子弹飞行的速度。 private TankClient tc; //持有对主程序的引用。 private boolean ilive; //子弹是否存活,活着为true,死了为false。 private boolean good; //子弹是否为自己的坦克所发射,是为true,不是为false。
/** * 子弹的构造函数,生成一颗按参数要求的子弹 */ public Bullet(int x, int y, boolean good, Direction dir, TankClient tc) { this.x = x + 10; this.y = y + 10; this.good = good; this.dir = dir; this.ilive = true; this.tc = tc; } /** * 重写draw方法,当子弹状态为true时画一颗子弹,否则不画。 */ public void draw(Graphics g) { if(!ilive) { //即子弹已失效,则移除子弹 tc.bullets.remove(this); return; } Color c = g.getColor(); g.setColor(Color.red); //将子弹颜色写成红色 g.fillOval(x, y, 10, 10); //进行颜色填充 g.setColor(c); move(); //移动子弹 } /** * 根据子弹的方向和速度进行移动。 */ public void move() { //根据方向修改子弹的位置 switch(dir) { case L: x -= BSEEPD; break; case U: y -= BSEEPD; break; case R: x += BSEEPD; break; case D: y += BSEEPD; break; case STOP: break; } if(x < 0 || y < 0 || x > TankClient.getGAME_WIDTH() || y > TankClient.getGAME_HEIGHT()) { ilive = false; } } /** * 获得当前子弹对象的所占区域。 */ public Rectangle getRect() { return new Rectangle(x, y, WIDTH, HEIGHT); } /** * 判断子弹是否击中墙。 */ public boolean hitWall(Wall w) { if(this.ilive&&this.getRect().intersects(w.getRect())) { this.setIlive(false); return true; } return false; } /** * 判断是否击中所有墙中的任何一面墙,是返回true,否则返回false。 */ public boolean hitWalls(List<Wall> walls) { for(int i=0; i<walls.size(); i++) { Wall w = walls.get(i); if(this.hitWall(w)) { return true; } } return false; } /** * 判断是否击中坦克。 */ public boolean hitTank(Tank t) { if(this.ilive&&this.getRect().intersects(t.getRect())&&t.isIlive()&&this.good!=t.isGood()) { //通过判断子弹的区域和坦克的区域是否重合判断是否击中 if(t.isGood()) { //如果坦克是自己坦克 t.setLife(t.getLife()-25); //减25生命值 if(t.getLife()<=0) { Explode e = new Explode(x, y, tc); //产生爆炸实例,并在主窗口显示 tc.explodes.add(e); t.setIlive(false); //将坦克消灭 } } else { Explode e = new Explode(x, y, tc); tc.explodes.add(e); t.setIlive(false); } ilive = false; return true; } return false; } /** * 判断是否击中坦克链表中的任何一辆,击中返回true,否则返回false。 */ public boolean hitTanks(List<Tank> tanks) { for(int i=0; i<tanks.size(); i++) { Tank t = tanks.get(i); //用t遍历所有坦克 if(this.hitTank(t)) { return true; } } return false; }
/** * 获取ilive的值。 */ public boolean isIlive() { return ilive; }
/** * 设置ilive的值。 */ public void setIlive(boolean ilive) { this.ilive = ilive; } }
坦克类:
import java.awt.Color; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.event.KeyEvent; import java.util.List; import java.util.Random;
/** * 生成和控制坦克。 * @author carefree2005 * */ public class Tank {
private int x, y; //坦克的初始位置。 private static final int WIDTH = 30; //坦克的宽度。 private static final int HEIGHT = 30; //坦克的高度。 private static final int BSEEPD = 5; //敌人坦克的移动速度。 private static final int GSEEPD = 30; //我军坦克的移动速度。 private int oldX = 0; //用于记忆坦克上一步的x坐标。 private int oldY = 0; //用于记忆坦克上一步的y坐标。 private Direction dir = Direction.STOP; //坦克的行驶方向。 private Random r = new Random(); //随机数。 private int step = r.nextInt(80) + 3; // private boolean good = true; //坦克是否为我军坦克,是为true,否则为false。 private boolean ilive; //坦克是否还活着,是为true,否则为false。 private int life = 100; //坦克的生命值。 private BloodBar bb = new BloodBar(); //用来显示坦克生命值的血条。 TankClient tc = null; //持有对主程序的引用。
/** * 新生成一辆坦克。 */ public Tank(int x, int y, boolean good) { this.x = x; this.y = y; this.oldX = x; this.oldY = y; this.good = good; if(good) { dir = Direction.U; } else { dir = Direction.D; } this.ilive = true; } /** * Tank的另一个构造方法,生成一辆坦克。 */ public Tank(int x, int y, boolean good, TankClient tc) { this(x, y, good); this.tc = tc; } /** * 根据坦克的方向和位置控制坦克的移动。 */ public void move() { this.oldX = x; this.oldY = y; switch(dir) { case L: if((x - Tank.WIDTH)>0) { if(!good) { x -= BSEEPD; break; } else { x -= GSEEPD; break; } } else { Direction[] dirs = Direction.values(); int rn = r.nextInt(dirs.length-1); dir = dirs[rn]; } case U: if((y - Tank.HEIGHT)>0) { if(!good) { y -= BSEEPD; break; } else { y -= GSEEPD; break; } } else { Direction[] dirs = Direction.values(); int rn = r.nextInt(dirs.length-1); dir = dirs[rn]; } case R: if((x + Tank.WIDTH)<TankClient.getGAME_WIDTH()) { if(!good) { x += BSEEPD; break; } else { x += GSEEPD; break; } } else { Direction[] dirs = Direction.values(); int rn = r.nextInt(dirs.length-1); dir = dirs[rn]; } case D: if((y + Tank.HEIGHT)<TankClient.getGAME_HEIGHT()) { if(!good) { y += BSEEPD; break; } else { y += GSEEPD; break; } } else { Direction[] dirs = Direction.values(); int rn = r.nextInt(dirs.length-1); dir = dirs[rn]; } case STOP: break; } if(!good) { Direction[] dirs = Direction.values(); if(step == 0) { step = r.nextInt(80) + 3; int rn = r.nextInt(dirs.length-1); dir = dirs[rn]; } step--; if(r.nextInt(50)>47) { this.fire(dir); } } } /** * 画一辆坦克。 */ public void draw(Graphics g) { if(!ilive) { if(!good) { tc.tanks.remove(this); return; } } Color c = g.getColor(); if(good) { g.setColor(Color.RED); } else { g.setColor(Color.BLUE); } g.fillRect(x, y, WIDTH, HEIGHT); g.setColor(Color.BLACK); switch(dir) { case L: g.fillRect(x-Tank.WIDTH/3, y+Tank.WIDTH/3, Tank.WIDTH/3, Tank.HEIGHT/3); break; case U: g.fillRect(x+Tank.WIDTH/3, y-Tank.WIDTH/3, Tank.WIDTH/3, Tank.HEIGHT/3); break; case R: g.fillRect(x+Tank.WIDTH, y+Tank.WIDTH/3, Tank.WIDTH/3, Tank.HEIGHT/3); break; case D: g.fillRect(x+Tank.WIDTH/3, y+Tank.WIDTH, Tank.WIDTH/3, Tank.HEIGHT/3); break; } g.setColor(c); if(!good) { move(); } if(good) { bb.draw(g); } }
/** * 监听键盘的按下按键动作。 */ public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); switch(key) { case KeyEvent.VK_D: dir = Direction.R; move(); break; case KeyEvent.VK_S: dir = Direction.D; move(); break; case KeyEvent.VK_A: dir = Direction.L; move(); break; case KeyEvent.VK_W: dir = Direction.U; move(); break; case KeyEvent.VK_N: superFire(); break; case KeyEvent.VK_F1: if(tc.getTankNum() == 20) tc.newGame(); break; case KeyEvent.VK_1: this.setIlive(true); this.setLife(100); break; default : break; } } /** * 监听键盘的松开按键动作。 */ public void keyReleased(KeyEvent e) { int key = e.getKeyCode(); if(KeyEvent.VK_J == key) { fire(dir); } } /** * 控制坦克开火。 */ public Bullet fire(Direction dir) { if(!ilive) { return null; } Bullet b = new Bullet(x, y, good, dir, tc); tc.bullets.add(b); return b; } /** * 朝多个方向开火。 */ private void superFire() { Direction[] dirs = Direction.values(); for(int i=0; i<4; i++) { fire(dirs[i]); } } /** * 获取坦克的所占区域。 */ public Rectangle getRect() { return new Rectangle(x, y, Tank.WIDTH, Tank.HEIGHT); } /** * 判断坦克是否吃到血块。 */ public boolean eatBlood(Blood b) { if(this.ilive && b.isIlive() && this.getRect().intersects(b.getRect())) { this.setLife(100); b.setIlive(false); return true; } return false; } /** * 坦克退回上一个位置。 */ public void stay() { x = this.oldX; y = this.oldY; } /** * 坦克之间是否碰撞,是的话返回true,否则返回false。 */ public boolean collideTank(Tank t) { if(this.ilive&&this.getRect().intersects(t.getRect())&&t.ilive) { this.stay(); Direction[] dirs = Direction.values(); int rn = r.nextInt(dirs.length-1); dir = dirs[rn]; return true; } return false; } /** * 判断这辆坦克是否与其他坦克碰撞。 */ public boolean collideTanks(List<Tank> tanks) { for(int i=0 ;i<tanks.size(); i++) { Tank t = tanks.get(i); if(this != t) { if(this.collideTank(t)) { return true; } } } return false; } /** * 判断这辆坦克是否与墙碰撞。 * @param w 一个墙的实例。 * @return 碰撞了返回返回true,否则返回false。 */ public boolean collideWall(Wall w) { if(this.ilive&&this.getRect().intersects(w.getRect())) { this.stay(); Direction[] dirs = Direction.values(); int rn = r.nextInt(dirs.length-1); dir = dirs[rn]; return true; } return false; } /** * 判断这辆坦克是否与任何一面墙相碰撞。 */ public boolean collideWalls(List<Wall> walls) { if(!good) { for(int i=0; i<walls.size(); i++) { Wall w = walls.get(i); if(this.collideWall(w)) { return true; } } return false; } return false; }
/** * 获取ilive的值。 */ public boolean isIlive() { return ilive; }
/** * 设置ilive的值。 */ public void setIlive(boolean ilive) { this.ilive = ilive; }
/** * 获取good的值。 */ public boolean isGood() { return good; }
/** * 设置good的值。 */ public void setGood(boolean good) { this.good = good; }
/** * 获取life的值。 */ public int getLife() { return life; }
/** * 设置life的值。 */ public void setLife(int life) { this.life = life; } /** * 生成一个表示生命值的血条。 * @author carefree2005 * */ public class BloodBar { /** * 画一个血条。 */ public void draw(Graphics g) { Color c = g.getColor(); g.setColor(Color.MAGENTA); g.drawRect(x, y-25, Tank.WIDTH, 8); int w = Tank.WIDTH*life/100; g.setColor(Color.MAGENTA); g.fillRect(x, y-25, w, 8); g.setColor(c); } } }
点击运行类:
import java.awt.Color; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Random; //import java.util.concurrent.*;
/** * 坦克大战的主程序,创建游戏窗口。 */ //建立一个窗口 public class TankClient extends Frame { private static final long serialVersionUID = 1L; //序列号。 private static final int GAME_WIDTH = 600; //主窗口的宽度。 private static final int GAME_HEIGHT = 600; //主窗口的高度。 private int tankNum = 0; //最初敌军坦克的数量。 private int score = 0; //最初得分 private Random r = new Random(); //随机数。 //private boolean flag = true; Image offScreenImage = null; //整个窗口图像的镜像。 Tank myT = new Tank(285, 570, true, this); //一个自己坦克的实例。 List<Tank> tanks = new LinkedList<Tank>(); //存储敌人坦克的链表。 List<Bullet> bullets = new ArrayList<Bullet>(); //存储子弹的链表。 List<Explode> explodes = new ArrayList<Explode>(); //存储爆炸的链表。 List<Wall> walls = new ArrayList<Wall>(); //存储墙的链表。 List<Blood> bloods = new ArrayList<Blood>(); //存储血块的链表。
/** * 重写父类的paint方法,画出所有窗口中的事物(non-Javadoc)。 * @see java.awt.Container#paint(java.awt.Graphics) */ public void paint(Graphics g) { score = (tankNum-tanks.size())*10; g.drawString("还剩下坦克:" + (20-tankNum+tanks.size()), 495, 50); //在图的这些位置显示当前剩余坦克数量和得分 g.drawString("你的得分为:" + score, 495, 60); if((20-tankNum+tanks.size())==0) { g.drawString("恭喜你过关了!", 280, 290); } //bullets是存储子弹的链表,b是每次从中提取的元素,是Bullet类的一个实例 for(int i=0; i<bullets.size(); i++) { Bullet b = bullets.get(i); b.hitTanks(tanks); //判断子弹是否击中敌坦克 b.hitTank(myT); //判断子弹是否击中己方坦克 b.hitWalls(walls); //判断子弹是否击中墙 b.draw(g); //根据返回值重画图 } for(int i=0; i<explodes.size(); i++) { Explode e = explodes.get(i); e.draw(g); } for(int i = 0; i<tanks.size(); i++) { Tank t = tanks.get(i); t.collideWalls(walls); //判断坦克是否撞墙及撞坦克 t.collideTanks(tanks); t.collideTank(myT); t.draw(g); } for(int i=0; i<walls.size(); i++) { Wall w = walls.get(i); w.draw(g); } for(int i=0; i<bloods.size(); i++) { Blood b = bloods.get(i); myT.eatBlood(b); b.draw(g); } if(myT.isIlive()) { myT.draw(g); } } /** * 重写父类的update方法,生成镜像图片,实现双缓冲(non-Javadoc) * @see java.awt.Container#update(java.awt.Graphics) */ public void update(Graphics g) { if(offScreenImage == null) { offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT); } Graphics gOffScreen = offScreenImage.getGraphics(); Color c = gOffScreen.getColor(); gOffScreen.setColor(Color.lightGray); gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT); gOffScreen.setColor(c); paint(gOffScreen); g.drawImage(offScreenImage, 0, 0, null); }
/** * 生成主窗口,并启动监听线程。 */ public void launchFrame() { //生成边框的墙 for(int i=0; i<5; i++) { walls.add(new Wall(220+30*i, 200)); } for(int i=0; i<5; i++) { walls.add(new Wall(120+30*i, 400)); } for(int i=0; i<5; i++) { walls.add(new Wall(400, 300+30*i)); } this.setSize(GAME_WIDTH, GAME_HEIGHT); this.setLocation(150, 100); this.setBackground(Color.lightGray); this.setTitle("坦克大战5130vs5104版"); this.setVisible(true); this.setResizable(false); //设置为不可调大小 Thread t1 = new Thread(new RePaintThread()); Thread t2 = new Thread(new NewTankThread()); t1.start(); t2.start(); if(this.getTankNum()==20&&this.tanks.size()==0) { t1.interrupt(); t2.interrupt(); } } public void Monitor() { this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { setVisible(false); System.exit(0); } }); this.addKeyListener(new KeyMonitor()); }
/** * 内部类,实现窗口画面的更新重画。 */ private class RePaintThread implements Runnable {
TankClient tc = new TankClient(); public void run() { while((20-tankNum+tanks.size()) != 0) { repaint(); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } repaint(); try { this.finalize(); } catch (Throwable e) { e.printStackTrace(); } } } /** * 生成一辆敌军坦克。 */ public void newTank() { tanks.add(new Tank(5+280*(tankNum%3), 30, false, this)); } /** * 生成一个血块。 */ public void newBlood() { bloods.add(new Blood()); } /** * 内部类,用于不断的生成敌军坦克。 */ private class NewTankThread implements Runnable {
TankClient tc = new TankClient(); public void run() { while(tankNum<20) { if(r.nextInt(50) > 46) { newBlood(); } //即图中至少有4辆坦克同时跑 if(tanks.size()<4) { newTank(); tankNum++; } try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } try { this.finalize(); } catch (Throwable e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * 内部类,监听键盘。 */ private class KeyMonitor extends KeyAdapter { public void keyReleased(KeyEvent e) { myT.keyReleased(e); }
public void keyPressed(KeyEvent e) { myT.keyPressed(e); } } /** * main方法,启动游戏。 */ public static void main(String[] args) { TankClient tc = new TankClient(); tc.Monitor(); tc.launchFrame(); }
/** * 获取主窗口的宽度。 */ public static int getGAME_WIDTH() { return GAME_WIDTH; } /** * 获取主窗口的高度。 */ public static int getGAME_HEIGHT() { return GAME_HEIGHT; }
/** * 获取敌军坦克的数量。 */ public int getTankNum() { return tankNum; }
/** * 设置敌军坦克的数量。 */ public void setTankNum(int tankNum) { this.tankNum = tankNum; }
public int getScore() { return score; }
public void setScore(int score) { this.score = score; }
public void newGame() { this.setTankNum(0); this.setScore(0); myT = null; myT = new Tank(285, 570, true, this); this.launchFrame(); } }
其余类的代码保留
游戏的5个重构:
游戏的TDD如下:
程序单元描述
游戏程序目前主要分为一下几个单元:
1. 游戏主面板类:包括所有游戏元素在窗口的绘制显示,实现双缓冲,事件监听处理等,面板重绘,各个对象的实例化操作。
2. 坦克类:游戏中所有坦克对象
属性
(1)坦克的好坏
(2)生命值
(3)移动速度
(4)坦克高度和宽度
(5)坦克起始坐标
(6)坦克的起始方向
以及一些方法
(1)坦克的初始化
(2)绘制坦克
(3)控制坦克移动
(4)确定坦克的方向
(5)坦克开火
(6)碰撞检测
(7)坦克撞墙检测
(8)坦克发射超级炮弹
还包括一个内部类
(1)血条类
3. 子弹类:描述坦克打出的子弹的属性等 属性
(1)子弹的速度
(2)子弹宽高
(3)子弹生存情况
(4)子弹的“好坏”
(5)子弹起始坐标及方向
一些方法
(1)子弹的初始化方法
(2)子弹的绘制
(3)子弹的移动
(4)击中坦克的检测
(5)击中墙的检测
4. 爆炸类:描述子弹击中坦克产生的爆炸
属性
(1)爆炸产生的坐标
(2)爆炸的存在情况
一些方法
(1)初始化
(2)绘制爆炸
5. 墙类:描述游戏场景中阻挡坦克的墙
属性
(1)墙的坐标
(2)主面板的引用
一些方法
(1)初始化
(2)绘制墙
(3)检测碰撞
利用junit对目标类Tank.java生成TankTest.java测试类 自动生成测试代码,修改部分代码进行测试,如下图:
总结与体会
PSP时间:
步骤 |
耗时 |
百分比 |
需求分析 |
40 |
11% |
设计 |
50 |
14% |
代码实现 |
180 |
50% |
测试 |
60 |
17% |
分析总结 |
30 |
8% |
体会:通过结对项目,我认识到了合作的重要性,紧密的合作能够提高我们的能力。代码测试过程中出现很多错误,但经过互相的合作和探讨,加以改进,便成功运行
这次坦克大战的代码虽然借鉴了一些别人的,但是我们通过对GUI的把握,成功向程序中添加了很多控件。而且我通过一个循环结构,开发出了一个“大招”,那就是只要一按“k”,就会向四个方向同时发射子弹,这样获胜几率会增大许多~一般人我不告诉他
而且本实验通过面积的重合来判断是否击中和撞墙,通过链表存储子弹和坦克,并通过随机数的产生让每个敌方坦克自己运行和发弹,让我们隐约窥到很多游戏的开发模式。
通过本次实验,学会了如何使用github来管理代码。如果是开源的项目,通过网站托管方式进行统一管理,当然是非常棒的,并且有很多功能,如果不能开源,仅搭建内部Git服务器进行配置管理,团队管理部分的功能就需要自己开发,相比其他配置工具,在这方面没有太大的优势,当然在各大开源网站上已经有大量这方面的实现共享出来。
同时通过结对项目,我认识到了合作的重要性,紧密的合作能够提高我们的能力。代码测试过程中出现很多错误,但经过互相的合作和探讨,加以改进,便成功运行
结对伙伴:20135130王川东
博客地址 http://www.cnblogs.com/dvew/