基于java的坦克大战实例

来源:互联网 发布:东华软件邮箱 编辑:程序博客网 时间:2024/05/22 17:45

原创性声明

此博文的出处 为 http://blog.csdn.net/zhujunxxxxx/article/details/40460931如果进行转载请注明出处。本文作者原创,邮箱zhujunxxxxx@163.com,如有问题请联系作者

前言

很久没碰java了,今天突然找出了大二时期写的一个坦克大战游戏的源码,然后运行了一下,竟然依然如此的亲切,突然想到了以前大二的时候的点点时光。好了不废话了,先给出一张图。

这个就是整个游戏的界面了,界面全是用的java画的,不是很好看。如果想要好看的得做成图片版本的

原理

游戏的基本原理就是,一开始创建N个敌人的实例,多线程在界面中移动(移动使用的是重写paint函数来实现的),然后随机移动和发射子弹。
还有就是使用了简单的碰撞检测方法来实现子弹击中别人和碰到障碍物
双缓冲技术,消除频闪

坦克类

坦克类是这个游戏的主要对象,可以分为三种坦克 一种是自己的坦克,一种是普通坦克,而另一种是BOSS坦克(血多 伤害大),不过都是一个类来实现的
import java.awt.*;import java.awt.event.*;import java.util.*;public class Tank {public static final int XSPEED = 5;public static final int YSPEED = 5;public static final int WIDTH = 30;public static final int HEIGHT = 30;TankClient tc;private int 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.D;//敌方坦克初始方向,向下private int step = r.nextInt(12) + 3;private boolean isSuper=false;public boolean isSuper(){return isSuper;}public void setSuper(boolean isSuper){this.isSuper = isSuper;}private static Random r = new Random();//随机数产生器private boolean good;//区分好坏的变量private boolean boss;public boolean isBoss(){return boss;}public boolean isGood(){return good;}public void setGood(boolean good){this.good = good;}private BloodBar bb=new BloodBar();//血条//坦克生命值的变量private int life=100;public int getLife(){return life;}public void setLife(int life){this.life = life;}//坦克生死的变量private boolean live=true;public boolean isLive(){return live;}public void setLive(boolean live){this.live = live;}//坦克的构造方法public Tank(int x, int y,boolean good) {this.x = x;this.y = y;this.oldX = x;this.oldY = y;this.good=good;}public Tank(int x, int y,boolean good,Direction dir, TankClient tc) {this(x, y,good);this.dir=dir;this.tc = tc;}public Tank(int x, int y,boolean good,boolean boss, Direction dir, TankClient tc) {this(x, y,good);this.dir=dir;this.boss=boss;this.tc = tc;}//画坦克的方法public void draw(Graphics g) {if(!live) return;//Tank死了就不在重画了Color c = g.getColor();if(good)//通过好坏设置颜色{g.setColor(Color.CYAN);}else if(boss){g.setColor(Color.RED);}else {g.setColor(Color.BLUE);}g.fillOval(x, y, WIDTH, HEIGHT);g.setColor(c);if(good) bb.draw(g);//我方坦克有血条if(boss) bb.draw(g);//boss有血条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();//重画时,进行移动}//坦克的移动方法,在重画的时候,通过方向定位来进行移动void move() {this.oldX = x;this.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 < 30) y = 30;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&&!boss) {Direction[] dirs = Direction.values();if(step == 0) {step = r.nextInt(12) + 3;int rn = r.nextInt(dirs.length);dir = dirs[rn];}step --;if(r.nextInt(40) > 38) this.fire();}//boss坦克的攻击方法if(!good&&boss){Direction[] dirs = Direction.values();if(step == 0) {step = r.nextInt(12) + 3;int rn = r.nextInt(dirs.length);dir = dirs[rn];}step --;if(r.nextInt(40) > 20)this.fire();}}//按下键时进行设置键位标志,用于方向定位判断public void keyPressed(KeyEvent e) {int key = e.getKeyCode();switch(key) {case KeyEvent.VK_CAPS_LOCK:this.setSuper(!this.isSuper());case KeyEvent.VK_F2 :if(!this.live) {this.live = true;this.life = 100;}case KeyEvent.VK_CONTROL:fire();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;}locateDirection();//每次按键后就判断方向}//Tank的方向定位,通过按键后设置的标志进行判断void locateDirection() {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 keyReleased(KeyEvent e) {int key = e.getKeyCode();switch(key) {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_A :superFire();break;}locateDirection();}//发出一发子弹的方法,向missiles里面添加子弹public 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;if(good&&this.isSuper())//判断是不是超级炮弹{m = new Missile(x, y,good, ptDir,this.tc,true);}else {m = new Missile(x, y,good, ptDir,this.tc);}tc.missiles.add(m);return m;}//SuperFire向每个方向发子弹的方法public 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);return m;}private void superFire(){Direction[] dirs = Direction.values();for(int i=0;i<8;i++){//tc.missiles.add(fire(dirs[i]));fire(dirs[i]);}}//判断相撞的方法所需的public Rectangle getRect() {return new Rectangle(x, y, WIDTH, HEIGHT);}//与坦克和墙相撞时停下来的方法private void stay() {x = oldX;y = oldY;}//判断坦克是否与墙相撞的方法public boolean collidesWithWall(Wall w) {if(this.live && this.getRect().intersects(w.getRect())) {this.stay();return true;}return false;}//判断坦克是否与坦克相撞的方法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;}//血条的内部类private class BloodBar {public void draw(Graphics g) {Color c = g.getColor();g.setColor(Color.orange);g.drawRect(x, y-10, WIDTH, 10);int w = WIDTH * life/100 ;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())) {this.life = 100;b.setLive(false);return true;}return false;}}

坦克类内部有很多的操作方法,还有一个血条。

子弹类

子弹类就是分为敌方子弹和我方子弹,具有移动速度等属性,具有碰撞检测等功能。
import java.awt.*;import java.util.List;public class Missile {public static final int XSPEED = 10;//子弹速度的变量public static final int YSPEED = 10;public static  int WIDTH = 10;//子弹大小public static  int HEIGHT = 10;int x, y;private TankClient tc;Tank.Direction dir;//方向变量private boolean isSuper;private boolean live = true;//子弹的生死变量public boolean isLive() {return live;}private boolean good;//坦克的好坏之分变量public boolean isGood(){return good;}//Missile的两个构造方法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.tc = tc;this.good=good;}public Missile(int x, int y,boolean good, Tank.Direction dir, TankClient tc,boolean isSuper) {this(x, y, dir);this.tc = tc;this.isSuper=isSuper;this.good=good;}//画子弹的方法public void draw(Graphics g) {if(!live) {tc.missiles.remove(this);return;}Color c = g.getColor();if(good&&isSuper)//是超级炮弹就变大{g.setColor(Color.YELLOW);g.fillOval(x-5, y-5, 20, 20);}else if(good&&!isSuper) {g.setColor(Color.YELLOW);g.fillOval(x, y, WIDTH, HEIGHT);}else {g.setColor(Color.RED);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;case STOP:break;}//子弹越界了就要死if(x < 0 || y < 0 || x > TankClient.GAME_WIDTH || y > TankClient.GAME_HEIGHT) {live = false;tc.missiles.remove(this);}}public Rectangle getRect() {return new Rectangle(x, y, WIDTH, HEIGHT);}//子弹与子弹相撞的方法,且子弹分好坏public boolean hitMissile(Missile m) {if(this.live && this.getRect().intersects(m.getRect()) && m.isLive() && this.good !=m.isGood() ){this.live=false;m.live=false;return true;}return false;}public boolean hitMissiles(List<Missile> missiles) {for(int i=0; i<missiles.size(); i++) {if(hitMissile(missiles.get(i))) {//调用了hitMissile方法missiles.remove(missiles.get(i));return true;}}return false;}public boolean hitTank(Tank t) {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 if(t.isBoss())//BOSS被击中{if(this.isSuper)//被超级炮弹击中{t.setLife(t.getLife()-33);if(t.getLife()<=0)t.setLive(false);}else {t.setLife(t.getLife()-5);if(t.getLife()<=0)t.setLive(false);}}else//坏人直接死t.setLive(false);//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))) {//调用了hitTank方法tanks.remove(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;}}

主程序类

主类主要是实现显示界面的功能,把在内存中的对象画出来,实现动画的效果。
import java.awt.*;import java.awt.event.*;import java.util.List;import java.util.ArrayList;import org.omg.CORBA.PUBLIC_MEMBER;public class TankClient extends Frame{public static  int GAME_WIDTH = 800;public static int GAME_HEIGHT = 600;Tank myTank = new Tank(200, 300,true,Tank.Direction.STOP, this);Tank BossTank = new Tank(60, 50,false,true,Tank.Direction.D,this);List<Missile> missiles=new ArrayList<Missile>();//装子弹的容器List<Explode> explodes = new ArrayList<Explode>();//装爆炸的容器List<Tank> tanks = new ArrayList<Tank>();//装敌方坦克的容器Blood b = new Blood();//实例化一个血块//实例化两个墙Wall w1=new Wall(300, 200, 20,150, this);Wall w2=new Wall(200, 100, 20,150, this);//双缓冲技术,消除频闪Image offScreenImage = null;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.gray);gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);gOffScreen.setColor(c);paint(gOffScreen);g.drawImage(offScreenImage, 0, 0, null);}public void paint(Graphics g)//会自动调用这个方法{g.drawString("tanks    count:" + tanks.size(), 10, 35);g.drawString("explodes count:" + explodes.size(), 10, 50);g.drawString("missiles count:" + missiles.size(), 10, 65);g.drawString("Tank     life:" + myTank.getLife(), 10, 80);if(tanks.size() <= 0) {for(int i=0; i<5; i++) {tanks.add(new Tank(50 + 40*(i+1), 50, false, Tank.Direction.D, this));}}//把容器里面的子弹都画出来for(int i=0;i<missiles.size();i++){Missile m=missiles.get(i);m.hitTanks(tanks);//添加子弹打坦克的方法m.hitTank(myTank);m.hitTank(BossTank);m.hitWall(w1);m.hitWall(w2);m.hitMissiles(missiles);//调用子弹撞子弹方法m.draw(g);}//把容器里面的爆炸全部画出来for(int i=0; i<explodes.size(); i++) {Explode e = explodes.get(i);e.draw(g);//e.ExplodehitTanks(tanks);//e.ExplodehitTank(BossTank);}//把容器里面的坦克全部画出来for(int i=0; i<tanks.size(); i++) {Tank t = tanks.get(i);t.collidesWithWall(w1);t.collidesWithWall(w2);t.collidesWithTanks(tanks);t.draw(g);}b.draw(g);//血块画出来BossTank.draw(g);BossTank.collidesWithWall(w1);BossTank.collidesWithWall(w2);BossTank.collidesWithTanks(tanks);//我方坦克画出来并添加方法myTank.draw(g);myTank.eat(b);myTank.collidesWithWall(w1);myTank.collidesWithWall(w2);//画出墙来w1.draw(g);w2.draw(g);}//初始化游戏窗口的方法public void lauchFrame(){//初始化坦克,添加进容器for(int i=0;i<10;i++){tanks.add(new Tank(50+40*(i+1),50,false,Tank.Direction.D,this));}this.setLocation(300, 100);this.setSize(GAME_WIDTH,GAME_HEIGHT);this.setTitle("zz TankWar");this.addWindowListener(new WindowAdapter(){public void windowClosing(WindowEvent e){System.exit(0);}});this.setBackground(Color.GREEN);this.setResizable(false);this.addKeyListener(new KeyMonitor());this.setVisible(true);new Thread(new PaintThread()).start();}private class PaintThread implements Runnable{public void run(){while(true){repaint();try{Thread.sleep(50);}catch(InterruptedException e){e.printStackTrace();}}}}private class KeyMonitor extends KeyAdapter {//键盘监听public void keyReleased(KeyEvent e){myTank.keyReleased(e);}public void keyPressed(KeyEvent e) {myTank.keyPressed(e);}}public static void main(String[] args) {TankClient t=new TankClient();t.lauchFrame();}}


总结

其实坦克大战这种游戏很简单,只要懂一些基本的数据结构和碰撞检测,还有重绘的方法就能够打造属于自己的坦克大战,给自己童年一个华丽的蜕变。

2 1
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 2岁多宝宝不长肉怎么办 2个月宝宝尿裤子怎么办 四岁宝宝脾气大怎么办 两周宝宝换奶粉怎么办 两周半宝宝不喝奶粉怎么办 2岁半宝宝太调皮怎么办 2岁宝宝晚上睡觉晚怎么办 三周岁半宝宝入园后不合群怎么办 数学懒于思考的孩子怎么办 2岁宝宝爱哭不讲道理怎么办 孩子不讲道理一直哭怎么办 白天不烧晚上烧怎么办 两岁宝宝出虚汗怎么办 两岁宝宝出水痘怎么办 两岁宝宝爱看手机怎么办 两岁宝宝太好动怎么办 五岁宝宝不会数数怎么办 四岁宝宝算数不好怎么办 两个月宝宝体内有火怎么办 两个月宝宝有火怎么办 2岁宝宝起眼屎怎么办 一周岁的宝宝皮肤过敏怎么办 两岁宝宝脾气倔不听话怎么办 22个月宝宝打人怎么办 两岁宝宝会打人怎么办 3岁哭闹倔强不止怎么办 我儿子二十三岁不爱说话怎么办 2岁宝宝不听话脾气大怎么办 2岁宝宝调皮不听话怎么办 2岁的宝宝不听话怎么办 2岁宝宝总是不听话怎么办 2岁宝宝淘气不听话怎么办 两岁宝宝不听大人的话怎么办? 来月经奶量减少怎么办 月经来了奶少怎么办 来例假奶水少了怎么办 两岁宝宝吐口水怎么办 3岁宝宝不愿自己吃饭怎么办 婆家的人很烦人怎么办 三十了还没结婚怎么办 两岁宝贝断奶粉怎么办