新手请见谅!不足之处,多多指教!谢谢。
坦克大战总结
0.1版本
实现功能:
设计出一个框架出来
0.2版本
实现功能:
1、添加关闭比窗口事件处理。
this.addWindowListener(new WindowAdapter ()
{
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
2、不允许窗口的大小改动。
this.setResizable(false);
掌握:
1、匿名类的用法
2、匿名类的应用场合(类短小,不涉及将来的扩张,不涉及重要的业务逻辑)
3、通过eclipse 重写父类方法
右键-----source----overWrite.
0.3版本
功能: 画出代表坦克的实心圆。
掌握: 如何重写paint 方法。
public void paint(Graphics g) {
Color c = g.getColor();
g.setColor(Color.red);
g.fillOval(50,50, 30, 30);
g.setColor(c);
}
注意:不要改变原来的前景色。.
0.4版本
功能:
让坦克动起来
步骤:
1、将位置改变为变量。
int x = 50,y = 50; g.fillOval(x, y, 30, 30);
2、启动线程不断重画。
private class PaintThread implements Runnable
{
public void run() {
while(true)
{
repaint();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3、为什么使用线程重画而不是没按下一个键进行一次重画。
线程重画更均匀,更能控制重画的速度。
按键重画不能解决子弹自动飞行的问题。
0.41版本
功能:使用双缓冲消除闪烁现象。
原因:刷新重画频率太快,paint 方法还完成。
解决办法: 将所有东西画在虚拟图片上一次性现实出来。
因为调用paint方法之前会调用updata()方法,现在在update()方法中将重画的图先画在一张图上
public void update(Graphics g) {
if(offgreenImage == null)//offgreenImage 在类中定义了。
{
offgreenImage = this.createImage(800, 600);
}
Graphics offScreen = offgreenImage.getGraphics();//得到图片的画笔
Color c = offScreen.getColor();
offScreen.setColor(Color.GREEN);
offScreen.fillRect(0, 0, 800, 600);
paint(offScreen);
g.drawImage(offgreenImage, 0, 0, null);
}
0.5版本
功能: 代码重构
将以后可能需要多处改变的变量定义为常量
本例中是:Frame 的宽度和高度。常量名一般大写。
注意:常量一般是 public static final 的。
0.6版本
功能: 让坦克听从指挥
1、添加键盘监听器类 KeyMonitor
private class KeyMonitor extends KeyAdapter {
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
switch (key) {
case KeyEvent.VK_LEFT:
x -= 5; break;
case KeyEvent.VK_RIGHT:
x += 5; break;
case KeyEvent.VK_UP:
y -= 5; break;
case KeyEvent.VK_DOWN:
y += 5; break;
}
}
}
2、TankWar 添加键盘监听器类
在luanchFrame中添加this.addKeyListener(new KeyMonitor());
3、针对不同的键改变坦克的位置,与重画线程结合产生不同方向的运动。
注意:
Switch case 语句中break 的运用。
版本0.7
功能: 将坦克单独包装成类。
步骤:
1、建立Tank类
2、为Tank类添加成员变量 x,y
3、添加draw方法,是Tank类自己控制自己的画法
4、添加Tank类处理按键的方法
5、根据Tank类修改TankWar类。
版本0.8
功能:让坦克朝8个方向行走
步骤:
1、添加记录按键状态的布尔量
private boolean bL = false,bU = false,bR = false,bD = false;
2、添加代表方向的量(使用枚举)
enum Direction {L,LU,U,RU,R,RD,D,LD,STOP};
Direction dir = Direction.STOP;
3、根据状态确定坦克的方向
void locaDirection()
{
if(bL && !bU && !bR && !bD) dir = Direction.L;
else if(bL && bU && !bR && !bD) dir = Direction.LU;
else if(!bL && bU && !bR && !bD) dir = Direction.U;
else if(!bL && bU && bR && !bD) dir = Direction.RU;
else if(!bL && !bU && bR && !bD) dir = Direction.R;
else if(!bL && !bU && bR && bD) dir = Direction.RD;
else if(!bL && !bU && !bR && bD) dir = Direction.D;
else if(bL && !bU && !bR && bD) dir = Direction.L;
else if(!bL && !bU && !bR && !bD) dir = Direction.STOP;
}
4、根据方向进行下一步的移动(move方法)
5、在pressed()方法中调用locaDurection()方法。
public void pressed(KeyEvent e)
{
int key = e.getKeyCode();
switch (key) {
case KeyEvent.VK_LEFT:
bL = true;break;
case KeyEvent.VK_RIGHT:
bR = true;break;
case KeyEvent.VK_UP:
bU = true;break;
case KeyEvent.VK_DOWN:
bD = true;break;
}
locaDirection();
}
版本0.9
功能:修正0.8版本
问题:0.8版本中按下一个键后键就永远变成了true了
解决: 处理按键抬起的消息
1、在TankWar中的KeyMoniter中增加按键抬起消息响应。
public void keyReleased(KeyEvent e) {
myTank.released(e);}
2、在Tank中增加代码
public void released(KeyEvent e) {
int key = e.getKeyCode();
switch (key) {
case KeyEvent.VK_LEFT:
bL = false;break;
case KeyEvent.VK_RIGHT:
bR = false;break;
case KeyEvent.VK_UP:
bU = false;break;
case KeyEvent.VK_DOWN:
bD = false;break;
}
locaDirection();
}
版本1.0
功能:添加子弹类
步骤:
1、添加Missile 类
2、添加x,y,dir等属性及常量
3、添加构造方法,draw方法
4、更具不同方向,进行不同运动
版本1.1
功能:根据主坦克的方向和位置,打出子弹
步骤:
1、增加对CTRL键的按键处理
2、根据“坦克打出依法子弹”这句话确定Tank中的fire,其返回值是Missile
3、根据Tank方向和位置设定子弹的方向和位置并画出来,然后返回。
版本1.2
功能:为了解决坦克停下也能打出炮弹的问题-----画出炮筒
步骤
1、Tank 类增加新的属性 ptDir
2、每次move后根据Tank新的方向确定炮筒的方向
3、将炮筒用直线的形式画出来(在draw方法中中画出来)
版本1.3
功能:打出多发炮弹
步骤:
1、使用容器装炮弹
List<Missile> missiles = new ArrayList<Missile>();
2、每当抬起Ctrl键就往容器中添加新的炮弹
case KeyEvent.VK_CONTROL:
fire();
break;
3、逐一画出每一发炮弹
在paint中使用
for(int i = 0;i<missiles.size();i++)
{
Missile m = missiles.get(i);
m.draw(g);
4、}
追:泛型的使用
将TankWar1的示例传递到tank中
版本1.4
功能:解决炮弹不消亡的问题
解决坦克出界的问题
if(x<0) x = 0;
if(y<30) y = 30;
if(x+Tank.WIDTH > TankWar1.GAME_WIDTH) x = TankWar1.GAME_WIDTH - Tank.WIDTH;
if(y + Tank.HEIGHT > TankWar1.GAME_HEIGHT) y = TankWar1.GAME_HEIGHT - Tank.HEIGHT;
步骤:
1、加入控制炮弹生死的量live (Missile类中加入)
2、当炮弹已经死去就不需要对其进行重画
3、当炮弹飞出边界就死亡
if(x < 0 || y < 0 || x > TankWar1.GAME_WIDTH || y > TankWar1.GAME_HEIGHT )
{
live = false;
this.tc.missiles.remove(this);
}
4、但炮弹死亡就从容器中擦出
5、在Missile类中添加新的构造函数 加入对TankWar1的引用
public Missile(int x,int y,Tank.Direction dir,TankWar1 tc)
{
this(x,y,dir);
this.tc = tc;
}
6、当炮弹new 出来时在fire()方法中
Missile m = new Missile(x,y,ptDir,this.tc);
版本1.41
功能:Debug的使用
1、一步一步跟踪代码
2、为代码设置断点
3、Step into 和 step over 的概念
4、学会在运行过程中观测变量的值。
版本1.5
功能:画出一量敌人的坦克
步骤:
1、加入区别敌我的量
2、根据敌我的不同设置不同的颜色
3、更新Tank的构造函数,加入good
4、TankWar1中画出敌人的坦克并画出。
版本1.6
功能:将敌人坦克击毙
分析:一颗子弹击中敌人坦克
步骤:
1、Missile中加入hitTank(Tank)方法,返回布尔类型
public boolean hitTank(Tank t)
{
if(this.getRect().intersects(t.getRect()) && t.isLive())
{
t.setLive(false);
this.live = false;
return true;
}
return false;
}
2、碰撞检测的辅助类Rectangle
public Rectangle getRect()
{
return new Rectangle(x,y,WIDTH,HEIGHT);
}
3、为Tank和Missile都加入getRect方法
4、当击中敌人坦克时,坦克被打死,子弹也死去
即把坦克和子弹的live 都设置为false
5、增加控制Tank生死的量live
6、如果死去就不画了
7、修改当子弹出界后把子弹的live设置为false,并不把它擦出
8、当画出子弹时先判断一下子弹是否还活着
if(!live)
{this.tc.missiles.remove(this);
return;}
版本1.7
功能: 加入爆炸
步骤:
1、添加爆炸类
2、用不同直径的圆模拟爆炸
3、加入live 判断爆炸的生死,死了就不要画了
4、加入位置属性
5、加入draw方法
爆炸应该位于集合类中
1、TankWar1加入集合
List<Explode> explodes = new ArrayList<Explode>();
2、将集合中的爆炸逐一画出(如果死去就去除)(模仿子弹画法)
for(int i=0;i<explodes.size();i++)
{
Explode e = explodes.get(i);
e.draw(g);
}
击毙一辆坦克时产生爆炸(在hitTank中添加)
Explode e = new Explode(x,y,tc);
tc.explodes.add(e);
版本1.8
功能:添加多辆坦克
步骤:
1、用容器来装敌人的Tank
List<Tank> tanks = new ArrayList<Tank>();
2、向容器中装入多辆敌人的Tank
for(int i = 0;i<tanks.size();i++)
{
Tank t = tanks.get(i);
t.draw(g);
}
3、画出敌人的坦克
4、运行,不能打掉
5、添加hitTanks方法,打一系列的坦克
public boolean hittanks(List<Tank> tanks)
{
for(int i = 0;i<tanks.size();i++){
if(hitTank(tanks.get(i)))
{
return true;}}
return false;}
6、TankWar中每一发子弹都可以打tanks
7、每次击毙一辆坦克要从容器中去除
if(!live)
{if(!good)
{
tc.tanks.remove(this);
}
return;}
版本1.9
功能:让敌军的坦克更加智能
步骤:
1、让敌军的坦克动起来
构造函数中可以指定方向
New 敌军坦克的时候指定敌军坦克的方向
2、让敌军的坦克向随机的方向移动
坦克静态的,添加随机数产生器 java.util.Random
private static Random r = new Random();
Move完成后,如果是敌军坦克,随机设定一个数来指定坦克的下一个方向
将enum转化为相应的整形数组
Direction[] dirs = Direction.values();
Direction.values()
3、让敌军坦克向随机方向移动随机的步骤
添加变量,记录随机步骤
if(step == 0)
{step = r.nextInt(12)+4;
int rn = r.nextInt(dirs.length);
dir = dirs[rn];
}
当变量等于0时,改变方向,否则只是随机步骤递减
4、让敌军坦克发射炮弹
本军炮弹不打本军炮弹
炮弹根据好坏添加变量good,根据好坏画出不同的颜色
修改炮弹的构造方法
修改坦克的fire()方法
如果坦克死了的话就不可以打出子弹了
if(!live)
return null;
修改hitTank方法,好不能打好,坏不能打坏
this.good!=t.isGood()
5、敌军炮火不可以太猛烈
用随机数解决
if(r.nextInt(20)>16)
{
this.fire();
}
版本2.0
功能:添加两堵墙
步骤:
1、建立Wall类,建立Wall对象,画出来
2、让每一颗子弹打击每一堵墙
hitWall()方法。
public boolean hitWall(Obstancle ob)
{
if(live && this.getRect().intersects(ob.getRect()))
{
live = false;
return true;
}
return false;}
注意:子弹速度不可以太快,否则容易穿过墙
3、让坦克不能穿过墙
记录上一次的位置oldX,oldY
修改构造函数
每次move之前记录上一次位置
添加stay()方法
public void stay()
{
x = oldX;
y = oldY;
}
记录移动前的位置
当撞到时回到移动前的位置
当撞到的时候stay
版本2.1
功能:坦克不能互相穿越
步骤:当坦克撞到坦克时stay 在Tank类中增加jittanks()方法
public boolean hitTanks(java.util.List<Tank> tanks)
{
for(int i = 0;i<tanks.size();i++)
{
Tank t = tanks.get(i);
if(this!=t)
{
if(this.live && t.isLive() && this.getRect().intersects(t.getRect()))
{
this.stay();
t.stay();
return true;
}
}}
return false;
}
版本2.2
功能:增加超级炮弹
代码:
增加一个fire(Direction dir)方法。
按方向来打出炮弹
public void superfire()
{
Direction [] dirs = Direction.values();
for(int i = 0;i<8;i++)
{
fire(dirs[i]);
}
}
版本2.3
功能:给我方坦克增加生命值
给坦克增加life 变量 初始值为100
在hitTank中改变代码
if(t.isGood())
{
t.setLife(t.getLife()-20);
if(t.getLife()<=0)
t.setLive(false);
}
else
t.setLive(false);
版本2.4
功能:图形化表示主坦克的生命值
步骤:
根据不同的life值进行不同的显示
给坦克类增加一个内部类
private class bloodBar
{
public void draw(Graphics g)
{
Color c = g.getColor();
g.setColor(Color.lightGray);
g.drawRect(x, y-10,WIDTH, HEIGHT);
int w = WIDTH * life/100;
g.fillRect(x, y-10, w, 10);
g.setColor(c);
}
}
版本2.5
功能:添加血块
添加Blood类
添加必要的方法
让blood对象固定轨迹运动,并在一定时间后消失
版本2.6
敌人死光了重新加入
我军死掉了按F12开始