Java坦克大战 (六) 之增加可玩性

来源:互联网 发布:档案整理软件 编辑:程序博客网 时间:2024/04/29 22:28

本文来自:小易博客专栏。转载请注明出处:http://blog.csdn.net/oldinaction

在此小易将坦克大战这个项目分为几个版本,以此对J2SE的知识进行回顾和总结,希望这样也能给刚学完J2SE的小伙伴们一点启示!


坦克大战V0.6实现功能:

1、超级炮弹
2、我方坦克有生命值
3、图像化我方坦克的生命值
4、添加血块,吃了可以涨生命值
5、敌方坦克死完后可以再有新的加入,我方坦克死亡可以复活


注意事项:

1、画生命值是fillRect()和drawRect()的不同

2、此处血块的移动时自己定义成固定的,也可以随机,每次移动的距离不能太大,否则会闪烁


坦克大战V0.6源代码:

TankClient类:

import java.awt.*;import java.awt.event.*;import java.util.List; //java.awt包中也有个List,所以此处要导包明确import java.util.ArrayList;/** * 主要的类 * @ClassName: TankClient * @Description: 坦克,子弹,爆炸,血块都在这里实例化 * @author oldinaction * @date 2014年9月2日 下午4:15:44 */public class TankClient extends Frame {public static final int GAME_WIDTH = 800;public static final int GAME_HEIGHT = 600;Tank myTank = new Tank(350, 520, true, Tank.Direction.STOP, this); //我方坦克Wall w1 = new Wall(100, 300, 20, 100, this), w2 = new Wall(300, 250, 150, 20, this);Blood b = new Blood(this);List<Missile> missiles = new ArrayList<Missile>(); //定义一个集合来装子弹List<Explode> explodes = new ArrayList<Explode>();List<Tank> tanks = new ArrayList<Tank>();Image offScreenImage = null; //定义一个屏幕后的虚拟图片@Overridepublic void paint(Graphics g) {if (tanks.size() <= 0) {for(int i = 0; i < 8; i++) {tanks.add(new Tank(50 + 40*(i+1), 50, false,Tank.Direction.D, this));}}g.drawString("missiles Count: " + missiles.size(), 10, 50); //用来记录missiles中子弹的个数g.drawString("explodes Count: " + explodes.size(), 10, 70);g.drawString("tanks Count: " + tanks.size(), 10, 90);g.drawString("myTank life: " + myTank.getLife(), 10, 110);for (int i = 0; i < missiles.size(); i++) { //遍历集合,把其中的子弹画出来Missile m = missiles.get(i);m.hitTanks(tanks);m.hitTank(myTank);m.hitWall(w1); //检测子弹是否撞墙m.hitWall(w2);m.drawMissile(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.collidesWithWall(w1); //检测敌方坦克是否撞墙t.collidesWithWall(w2);t.collidesWithTanks(tanks);t.drawTank(g);}myTank.drawTank(g);myTank.eat(b);w1.draw(g);w2.draw(g);b.draw(g);}//利用双缓冲消除圆圈移动时屏幕的闪动@Overridepublic void update(Graphics g) {if (offScreenImage == null) {offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT); //判断是为了避免每次重画时都给offScreenImage赋值}Graphics gOffScreen = offScreenImage.getGraphics(); //定义虚拟图片上的画笔gOffScreenColor c = gOffScreen.getColor();gOffScreen.setColor(Color.GREEN);gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT); //重画背景,如果没有这句则在屏幕上会保留圆圈的移动路径gOffScreen.setColor(c);paint(gOffScreen); //把圆圈画到虚拟图片上g.drawImage(offScreenImage, 0, 0, null); //再一次性把虚拟图片画到真实屏幕上,在真实屏幕上画则要用真实屏幕的画笔g}public void luanchFrame() {for(int i = 0; i < 10; i++) {tanks.add(new Tank(50 + 40*(i+1), 50, false,Tank.Direction.D, this));}this.setLocation(300, 50);this.setSize(GAME_WIDTH, GAME_HEIGHT);this.setTitle("坦克大战 - By:小易 - QQ:381740148");this.setResizable(false); //不允许改变窗口大小this.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {System.exit(0);}}); //添加关闭功能,此处使用匿名类比较合适this.setBackground(Color.GREEN);this.addKeyListener(new KeyMonitor());setVisible(true);new Thread(new PaintThread()).start(); //启动线程,实例化线程对象时不要忘了new Thread(Runnable对象);}public static void main(String[] args) {TankClient tc = new TankClient();tc.luanchFrame();}//PaintThread只为TankClient服务,所以写成内部类好些public class PaintThread implements Runnable { public void run() {while (true) {repaint(); //repaint()是TankClient或者他的父类的方法,内部类可以访问外部包装类的成员,这也是内部类的好处try {Thread.sleep(50); //每隔50毫秒重画一次} catch (InterruptedException e) {e.printStackTrace();}}}}public class KeyMonitor extends KeyAdapter {@Overridepublic void keyReleased(KeyEvent e) {myTank.keyReleased(e);}@Overridepublic void keyPressed(KeyEvent e) {myTank.keyPressed(e);}}}

Tank类:

import java.awt.*;import java.awt.event.*;import java.util.Random;public class Tank {public static final int XSPEED = 5; //定义常量X轴速度public static final int YSPEED = 5;public static final int WIDTH = 30;public static final int HEIGHT = 30;public static final int LIFE = 100;//定义一个随机数产生器,此时的Random类是java.util.Random,不同于Math中的private static Random r = new Random(); //随机数产生器只需要一个,所以定义成静态,防止每次new一个坦克是都会产生一个随机数产生器private BloodBar bb = new BloodBar();private boolean good; //定义变量说明是我方还是敌方坦克,为true表示我方坦克private boolean live = true; //定义变量说明是坦克是否存活private int life = LIFE; //设置坦克的生命值为100TankClient tc;private int x , y; //定义变量画圆圈(坦克)时四边形左上点的x、y左边private int oldX , oldY; //定义坦克上个位置的坐标private boolean bL = false, bU = false, bR = false, bD = false; //定义变量左上右下的按键是否被按下enum Direction {L,LU,U,RU,R,RD,D,LD,STOP}; //定义枚举类型,值为左、左上、上、右上、右、右下、下、左下、停止private Direction dir = Direction.STOP; //定义变量坦克的方向private Direction ptDir = Direction.U; //定义变量坦克炮筒的方向,起初向上private int step = r.nextInt(12) + 3; //定义坦克朝着一个方向移动几步public Tank(int x, int y, boolean good) {this.x = x;this.y = y;this.good = good;this.oldX = x;this.oldY =y;}public Tank(int x, int y, boolean good, Direction dir, TankClient tc) {this(x, y, good); //相当于调用上面的构造方法this.dir = dir;this.tc = tc;}public void drawTank(Graphics g) {if(!live) {if(!good) {tc.tanks.remove(this);}return; //如果坦克没有存活就直接返回,不用画坦克了}Color c = g.getColor(); //取得g(以后称为画笔)的颜色if(good) g.setColor(Color.RED);else g.setColor(Color.BLUE);g.fillOval(x, y, WIDTH, HEIGHT); //"画圆",利用填充一个四边形(四边形的内切圆),参数分别代表:四边形左上点的坐标X,Y,宽度,高度g.setColor(c); //用完画笔后把画笔默认的颜色(黑色)设置回去if (good) bb.draw(g);//根据炮筒的方向,画直线代表炮筒switch (ptDir) {case L:g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x, y+Tank.HEIGHT/2);break;case LU:g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x, y);break;case U:g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH/2, y);break;case RU:g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH, y);break;case R:g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH, y+Tank.HEIGHT/2);break;case RD:g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH, y+Tank.HEIGHT);break;case D:g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH/2, y+Tank.HEIGHT);break;case LD:g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x, y+Tank.HEIGHT);break;}move(); //每次按键都会重画,就会调用drawTank,在这里重画坦克的此时位置}public void keyPressed(KeyEvent e) {int key = e.getKeyCode(); //得到按键的虚拟码,再和下面的KeyEvent.VK_LEFT等虚拟码比较看是否是某按键switch (key) {case KeyEvent.VK_F2:if (!this.live) {this.live = true;this.life = LIFE;}break;case KeyEvent.VK_LEFT:bL = true;break;case KeyEvent.VK_UP:bU = true;break;case KeyEvent.VK_RIGHT:bR = true;break;case KeyEvent.VK_DOWN:bD = true;break;}locateDraction();if (dir != Direction.STOP) {ptDir = dir;}}public void keyReleased(KeyEvent e) {int key = e.getKeyCode();switch (key) {case KeyEvent.VK_SPACE: //按SPACE就发射子弹调用fire方法fire(); //只有松开SPACE才能发出子弹break;case KeyEvent.VK_LEFT:bL = false;break;case KeyEvent.VK_UP:bU = false;break;case KeyEvent.VK_RIGHT:bR = false;break;case KeyEvent.VK_DOWN:bD = false;break;case KeyEvent.VK_CONTROL: //按Ctrl就发射子弹调用fire方法superFire();break;}locateDraction();}//通过上右下的按键是否被按下判断坦克要运动的方向void locateDraction() {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.LD;else if(!bL && !bU && !bR && !bD) dir =Direction.STOP;}public void move() {oldX = x;oldY = y;switch (dir) {case L:x -= XSPEED;break;case LU:x -= XSPEED;y -= YSPEED;break;case U:y -= YSPEED;break;case RU:x += XSPEED;y -= YSPEED;break;case R:x += XSPEED;break;case RD:x += XSPEED;y += YSPEED;break;case D:y += YSPEED;break;case LD:x -= XSPEED;y += YSPEED;break;case STOP:break;}if(this.dir != Direction.STOP) {this.ptDir = this.dir;}//防止坦克出界if (x < 0) x = 0;if (y < 25) y = 25; //考虑了标题栏的高度if (x + Tank.WIDTH > TankClient.GAME_WIDTH) x = TankClient.GAME_WIDTH - Tank.WIDTH;if (y + Tank.HEIGHT > TankClient.GAME_HEIGHT) y = TankClient.GAME_HEIGHT - Tank.HEIGHT;if (!good) {Direction[] dirs = Direction.values(); //把枚举转换成数组if (step == 0) {int rn = r.nextInt(dirs.length);dir = dirs[rn]; //如果移动步数为0就改变方向step = r.nextInt(12) + 3;}step --;if(r.nextInt(40) > 37) this.fire();}}public void stay() {x = oldX;y = oldY;}//坦克开火,就new一个子弹出来private Missile fire() {if(!live) return null;int x = this.x + Tank.WIDTH/2 - Missile.WIDTH/2; //让子弹从坦克中心打出int y = this.y + Tank.HEIGHT/2 - Missile.HEIGHT/2;Missile m = new Missile(x, y,good, ptDir , this.tc);tc.missiles.add(m); //每new一个Missile对象就把他装到集合中return m; //返回的m,其他地方可调用可不调用}private Missile fire(Direction dir) {if(!live) return null;int x = this.x + Tank.WIDTH/2 - Missile.WIDTH/2; //让子弹从坦克中心打出int y = this.y + Tank.HEIGHT/2 - Missile.HEIGHT/2;Missile m = new Missile(x, y,good, dir , this.tc);tc.missiles.add(m); //每new一个Missile对象就把他装到集合中return m; //返回的m,其他地方可调用可不调用}public Rectangle getRect() {return new Rectangle(x, y, WIDTH, HEIGHT); //得到坦克的探测方块,Rectangle是java.awt包中专门用于游戏碰撞的类}public boolean isLive() {return live;}//Tank类的成员方法可生成对应的get和set方法,那么在其他类中就可以访问了public void setLive(boolean live) {this.live = live;}public boolean isGood() {return good;}//检测坦克是否撞墙public boolean collidesWithWall(Wall w) {if(this.live && this.getRect().intersects(w.getRect())) {stay(); //如果坦克撞到墙就让他回到上一个位置return true;}return false;}//检测坦克是否相撞,java.util.List<E>接口或者类的另一种写法,java.awt中也有List,所以要写明确public boolean collidesWithTanks(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;}//超级炮弹:朝8个方向各发一发炮弹private void superFire() {Direction[] dirs = Direction.values();for (int i = 0; i < 8; i++) { //dirs[8]是STOPfire(dirs[i]);}}public int getLife() {return life;}public void setLife(int life) {this.life = life;}public class BloodBar {public void draw(Graphics g) {Color c = g.getColor();g.setColor(Color.RED);g.drawRect(x, y - 10, WIDTH, 10);int w = WIDTH * life/LIFE ;g.fillRect(x, y - 10, w, 10);g.setColor(c);}}//吃血块public boolean eat(Blood b) {if(this.live && b.isLive() && this.getRect().intersects(b.getRect())) {b.setLive(false);life = LIFE;return true;}return false;}}


Missile类:

import java.awt.*;import java.util.List;public class Missile {public static final int XSPEED = 10;public static final int YSPEED = 10;public static final int WIDTH = 10;public static final int HEIGHT = 10;int x, y;Tank.Direction dir;private boolean good; //定义变量表示是否是我方子弹private boolean live = true; //定义一个判断子弹是否出界的变量private TankClient tc;public Missile(int x, int y, Tank.Direction dir) {this.x = x;this.y = y;this.dir = dir;}public Missile(int x, int y,boolean good, Tank.Direction dir, TankClient tc) {this(x, y, dir);this.good = good;this.tc = tc;}public void drawMissile(Graphics g) {if(!live) {tc.missiles.remove(this);return;}Color c = g.getColor();g.setColor(Color.BLACK);g.fillOval(x, y, WIDTH, HEIGHT);g.setColor(c);move();}private void move() {switch (dir) {case L:x -= XSPEED;break;case LU:x -= XSPEED;y -= YSPEED;break;case U:y -= YSPEED;break;case RU:x += XSPEED;y -= YSPEED;break;case R:x += XSPEED;break;case RD:x += XSPEED;y += YSPEED;break;case D:y += YSPEED;break;case LD:x -= XSPEED;y += YSPEED;break;}if (x < 0 || y < 0 || x > tc.GAME_WIDTH || y > tc.GAME_HEIGHT) {live = false;}}public boolean isLive() {return live;}public Rectangle getRect() {return new Rectangle(x, y, WIDTH, HEIGHT); //得到子弹的探测方块,Rectangle是java.awt包中专门用于游戏碰撞的类}//判断子弹是否打到了坦克public boolean hitTank(Tank t) {//intersects是Rectangle的一个方法;t.isLive()是为了判断坦克是否存活,如果没有则打掉这个坦克后,之后的子弹到达原来这个坦克的位置就会消失if (this.live && this.getRect().intersects(t.getRect()) && t.isLive() && this.good != t.isGood()) {if (t.isGood()) {t.setLife(t.getLife() - 20);if (t.getLife() <= 0) {t.setLive(false);}} else {t.setLive(false);}this.live = false;Explode e = new Explode(x, y, tc);tc.explodes.add(e);return true;}return false;}public boolean hitTanks(List<Tank> tanks) {for (int i = 0; i < tanks.size(); i++) {if (hitTank(tanks.get(i))) {return true;}}return false;}public boolean hitWall(Wall w) {if(this.live && this.getRect().intersects(w.getRect())) {this.live = false;return true;}return false;}}

Explode类:

import java.awt.*;//爆炸类(爆炸的英文:explode)public class Explode {int x, y;private TankClient tc;private boolean live = true;int[] diameter = {4, 10, 20, 36, 56, 24, 12, 6}; //定义画不同直径的圆表示爆炸(直径的英文:diameter)int step = 0; //定义变量代表画爆炸的圆画到第几个了public Explode(int x, int y, TankClient tc) {this.x = x;this.y = y;this.tc = tc;}public void draw(Graphics g) {if(!live) {tc.explodes.remove(this);return;}if (step == diameter.length) {live = false;step = 0;return;}Color c = g.getColor();g.setColor(Color.ORANGE);g.fillOval(x, y, diameter[step], diameter[step]);g.setColor(c);step ++ ;}}

Wall类:

import java.awt.*;public class Wall {private int x, y, w, h;private TankClient tc;public Wall(int x, int y, int w, int h, TankClient tc) {this.x = x;this.y = y;this.w = w;this.h = h;this.tc = tc;}public void draw(Graphics g) {g.fillRect(x, y, w, h);}public Rectangle getRect() {return new Rectangle(x, y, w, h);}}

Blood类:

import java.awt.*;public class Blood {private int x, y, w, h;private TankClient tc;private boolean live = true;private int step = 0;int[][] pos = {{400,400}, {420,400}, {440,400}, {440,420}, {440,440}, {420,440}, {400,440}, {400,420}};public Blood(TankClient tc) {this.x = pos[0][0];this.y = pos[0][1];this.w = h = 10;this.tc = tc;}public void draw(Graphics g) {if (!live) return;Color c = g.getColor();g.setColor(Color.MAGENTA);g.fillRect(x, y, w, h);g.setColor(c);move();}private void move() {step ++;if(step == pos.length) {step = 0;}x = pos[step][0];y = pos[step][1];}public Rectangle getRect() {return new Rectangle(x, y, w, h);}public boolean isLive() {return live;}public void setLive(boolean live) {this.live = live;}}




0 0
原创粉丝点击