用java做坦克大战--主要类和基本功能实现

本文详细阐述了坦克大战游戏的总体功能设计,包括客户端主类TankClient的调度功能、坦克类Tank的操作与碰撞检测、子弹类Missile的飞行与碰撞、敌人坦克的创建与AI控制、以及爆炸类Explode的生成与销毁。文章还介绍了游戏窗口创建、声音和图片的加载、以及动态效果的实现,提供了一个完整的游戏开发流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.坦克大战总体功能设计

1.1 TankClient

TankClient类是客户端的主类,它承担整个系统的所有功能的调度工作,是游戏运行的界面。它有三个线程,主线程负责对窗口和键盘的监听;PaintThread线程负责对界面的重画;ControlThread负责对游戏中的事件响应。主要方法和属性有:

  public static final int GAME_WINDOW_WIDTH=   ; 窗口宽度

  public static final int GAME_WINDOW_HEIGHT =   ; 窗口高度

       public static final int GAME_WIDTH =   ; 游戏界面宽度

  public static final int GAME_HEIGHT =   ; 游戏界面高度

  public static final int ENEMYSUM =   ; 每关总敌人数

       public static final int ENEMYCONTROL =  ; 场上存活的最多地敌人数

       public static int enemyLeave = ENEMYSUM ; 每关剩下的地人数

  public static int enemyLive =  ; 场上存活的敌人数

  public static boolean enemy =f/t    ; 添加敌人的标志

       public static boolean win = f/t    ; 胜利的标志

       public static boolean loadMap = f/t  ; 加载地图的标志

       public static boolean choose = f/t  ; 单机是否开始的标志

       public static boolean single = f/t  ; 单机单人的标志

       public static boolean gameOver = f/t ; 失败的标志

       public static boolean paint =  f/t ; 重新绘画的标志

       public static boolean restart =f/t  ; 重开的标志

       public static boolean controlThread = f/t  ; ControlThread线程的标志

       public static boolean paintThread = f/t  ; PaintThread 线程的标志

       public static boolean bomb = f/t  ; 爆炸道具的标志

       public static boolean timer = f/t  ; 时间冻结的标志

       public static boolean pass = f/t ; 通过全关的标志

  public static boolean net = f/t  ; 联网模式的标志

  List<Missile> missiles = new ArrayList<Missile>(); 保存子弹的集合

       List<Tank> tanks = new ArrayList<Tank>(); 保存坦克的集合

       List<Explode> explodes = new ArrayList<Explode>(); 保存爆炸的集合

  List<MapElement> maps= new ArrayList<MapElement>();保存地图元素的集合

  public void luanchFrame();初始化窗口

  public void initMenu();添加菜单栏  

  public void paint(Graphics g);绘画

  public void update(Graphics g);缓冲

  void addEnemy();添加敌人坦克

  void addProp();刷出道具

  public void init();初始化游戏参数

  Class PaintThread extends Runnable 绘画线程

  Class ControlThread extends Runnable 游戏控制线程

  Class KeyMonit extends KeyAdapter 键盘监听类

  Class ConnectDialog extends Dialog 网络连接的窗口

  Class LeafletDialog extends Dialog 帮助说明窗口

1.2tank

能够控制坦克的移动和射击,坦克能和别的物体碰撞。因为游戏中是以图片来区分敌我的,单机模式下坦克只有四个方向,而网络模式下坦克有八个方向,所以需要多个坦克类来实现,Tank类为所有坦克类的父接口,其中定义了一些基本的属性和方法,所有坦克类都需实现它。主要方法和属性有:

  public static final int XSPEED =  ; X方向的速度

       public static final int YSPEED =  ; Y方向的速度

       public static final int WIDTH =  ;坦克宽度

       public static final int HEIGHT =  ;坦克高度

       void draw(Graphics g);绘画自己

       void move();移动

       void back();回退到上一步

       void keyPressed(KeyEvent e);对键盘按下事件响应

       void keyReleased(KeyEvent e);对键盘弹起事件响应

       void fire();开火

       void load();加载自己的图片

  boolean collidesWithMapElement(MapElement me);检测自己是否与地图上某个元素碰撞

  boolean collidesWithMapElements(List<MapElement> list); 检测自己是否与地图上一些元素碰撞

  boolean collidesWithTank(Tank t); 检测自己是否与坦克碰撞

  boolean collidesWithTanks(List<Tank> list); 检测自己是否与一群坦克碰撞

       Rectangle getRect();得到自己的外切矩形

  Map<String,Image> imgs 保存坦克的图片。

1.3子弹类

坦克射击后能产生一个飞行的子弹,在和其他物体碰撞后,能做出不同的效果,如:与坦克相撞,则消失;与草地碰撞,穿过去等。由于单机模式下坦克只有四个方向,而网络模式下坦克有八个方向,相应的子弹也是不同的,所以有两个子弹类,还有个接口Missile,他其中定义了一些属性和方法,主要方法和属性有:

    public static final int XSPEED =  ; X方向的速度

       public static final int YSPEED =  ; Y方向的速度

       public static final int WIDTH =  ;  子弹宽度

       public static final int HEIGHT =  ; 子弹高度

  void draw(Graphics g); 绘画自己

       void move(); 移动

  void load();加载自己的图片

  Rectangle getRect();的到自己的外切矩形

  boolean hitTank(Tank t); 检测自己是否与坦克碰撞

  boolean hitTanks(List<Tank> list); 检测自己是否与一群坦克碰撞

       Rectangle getRect();得到自己的外切矩形

  boolean hitMapElement(MapElement me); 检测自己是否与地图上某个元素碰撞

  boolean hitMapElements(List<MapElement> list); 检测自己是否与地图上一些元素碰撞

  Map<String,Image> imgs 保存子弹的图片。

1.4爆炸类

在子弹击中敌方坦克后,能产生一个爆炸,爆炸的效果是通过在一个地方按顺序绘画出一组不同打下的图片得到。Explode类的主要属性和方法有:

  private int x , y; 爆炸发生的像素坐标

  private int step = 0; 爆炸发生到第几步

  public void draw(Graphics g)绘画自己

  Image[] imgs 保存爆炸的图片。

 

1.5加载声音和图片类

加载指定的图片和声音,并封装方便调用。加载声音和图片的类分别是Sound和LoadImages,其中Sound类是通过sun.audio包完成的,Sound的主要属性有:

   private static AudioDataStream ads; 音频数据

       private static AudioStream as; 音频流

       private static FileInputStream fis; 文件输入流

       public static final int ADD = 1; 坦克出生

       public static final int FIRE = 2; 开火

       public static final int HIT = 3; 打到坦克

       public static final int START = 4; 游戏开始

图片的加载,通过Toolkit类来现实加载。

2实现

2.1窗口创建

主类TankClient继承Frame类,产生一个固定大小的窗口,并为其添加了windowClosing事件,在退出时有个确认对话框进行确认。还使用了Toolkit类得到屏幕分辨率,来使窗口居中[7]。游戏模式及帮助,是在菜单栏中选择。

TankClient
  1 Toolkit tk = Toolkit.getDefaultToolkit();
  2 
  3               int windowX = tk.getScreenSize().width;
  4 
  5               int windowY = tk.getScreenSize().height;
  6 
  7               if (GAME_WINDOW_WIDTH > windowX || GAME_WINDOW_HEIGHT > windowY){
  8 
  9                      this.setLocation(0, 0);
 10 
 11               } else {
 12 
 13                      this.setLocation((windowX - GAME_WINDOW_WIDTH) / 2,
 14 
 15                                    (windowY - GAME_WINDOW_HEIGHT) / 2);
 16 
 17               }
 18 
 19               this.setSize(GAME_WINDOW_WIDTH, GAME_WINDOW_HEIGHT);
 20 
 21               this.setTitle("坦克大战");
 22 
 23               this.addWindowListener(new WindowAdapter() {
 24 
 25               public void windowClosing(WindowEvent e) {
 26 
 27               if (javax.swing.JOptionPane.showConfirmDialog(null,"退出","",javax.swing.JOptionPane.YES_NO_OPTION) <= 0) {
 28 
 29                                    paintThread = false;
 30 
 31                                    controlThread = false;
 32 
 33                                    System.exit(0);
 34 
 35                             } else {
 36 
 37                                    return;
 38 
 39                             }
 40 
 41                      }
 42 
 43               });
 44 
 45 this.setResizable(false);
 46 
 47 菜单栏的创建,并为每个MenuItem添加了ActionListener,来控制程序的流程。
 48 
 49 MenuBar mb = new MenuBar();
 50 
 51               Menu m1 = new Menu("File");
 52 
 53               Menu m3 = new Menu("Help");
 54 
 55               MenuItem mi1Single = new MenuItem("单机");
 56 
 57               MenuItem mi3Leaflet = new MenuItem("说明");
 58 
 59               m1.add(mi1Single);
 60 
 61 m3.add(mi3Leaflet);
 62 
 63               mb.add(m1);
 64 
 65               mb.add(m3);
 66 
 67               mi1Single.addActionListener(new ActionListener() {
 68 
 69                      public void actionPerformed(ActionEvent e) {
 70 
 71                             init();
 72 
 73                             level = 1;
 74 
 75                             pass = false;
 76 
 77                             paint = true;
 78 
 79                             enemy = true;
 80 
 81                             if (!gameOver) {
 82 
 83                                    if (!choose) {
 84 
 85                                           if (paintT == null) {
 86 
 87                                                  paintT = new PaintThread();
 88 
 89                                                  new Thread(paintT).start();
 90 
 91                                           }
 92 
 93                                           if (controlT == null) {
 94 
 95                                                  controlT = new ControlThread();
 96 
 97                                                  new Thread(controlT).start();
 98 
 99                                           }
100 
101                                    } else {
102 
103                                           tank1p = null;
104 
105                                           if (!single)
106 
107                                                  tank2p = null;
108 
109                                           choose = false;
110 
111                                           init();
112 
113                                    }
114 
115                             } else {
116 
117                                    tank1p = null;
118 
119                                    if (!single)
120 
121                                           tank2p = null;
122 
123                                    single = true;
124 
125                                    choose = false;
126 
127                                    gameOver = false;
128 
129                                    init();
130 
131                             }
132 
133                      }
134 
135               });
136 
137               mi1Net.addActionListener(new ActionListener() {
138 
139                      public void actionPerformed(ActionEvent e) {
140 
141                             conn.setVisible(true);
142 
143                      }
144 
145               });
146 
147               mi3Leaflet.addActionListener(new ActionListener() {
148 
149                      public void actionPerformed(ActionEvent e) {
150 
151                             leaf.setVisible(true);
152 
153                      }
154 
155               });
156 
157        this.setMenuBar(mb);

 

2.2坦克的创建与移动

整个程序的动态效果是通过不停的重画来实现的,在TankClient类中有个线程PaintThread调用update方法,再自动调用paint方法,每100毫秒重画一次,paint方法中实现所有物体的绘画。玩家坦克的创建在TankClient类中。

Tank
 1 public void run() {
 2 
 3                      while (paintThread) {
 4 
 5                             repaint();
 6 
 7                             if(choose && enemy){
 8 
 9                                    if(++enemyTimes > 60){
10 
11                                           enemyTimes = 0;
12 
13                                           addEnemy();
14 
15                                    }
16 
17                             }     
18 
19                             try {
20 
21                                    Thread.sleep(100);
22 
23                             } catch (InterruptedException e) {
24 
25                                    e.printStackTrace();
26 
27                             }
28 
29                      }
30 
31               }

 

坦克的绘画,就是在制定位置上画上一张图片。图片存放在Map<String, Image> imgs = new HashMap<String, Image>() 的Map对象中,获取方法稍后介绍。坦克的属性中有两个DirectionAlong的成员,他们来控制是画那张图片。

Tankdraw
 1 switch (ptDir) {
 2 
 3               case L:
 4 
 5                      g.drawImage(imgs.get("L"), x, y, null);
 6 
 7                      break;
 8 
 9               case U:
10 
11                      g.drawImage(imgs.get("U"), x, y, null);
12 
13                      break;
14 
15               case R:
16 
17                      g.drawImage(imgs.get("R"), x, y, null);
18 
19                      break;
20 
21               case D:
22 
23                      g.drawImage(imgs.get("D"), x, y, null);
24 
25                      break;
26 
27               }

 

坦克的移动是响应了键盘相应的按键,在move()方法中根据方向来改变坦克的X,Y属性来实现的,move()方法中还有防止坦克出界的控制语句。键盘的监听是再TankClient类中添加的,调用坦克各自的具体的实现。

keyPressed
  1 public void keyPressed(KeyEvent e) {
  2 
  3               int key = e.getKeyCode();
  4 
  5               switch (key) {
  6 
  7               case KeyEvent.VK_A:
  8 
  9                      dir = DirectionAlong.L;
 10 
 11                      break;
 12 
 13               case KeyEvent.VK_W:
 14 
 15                      dir = DirectionAlong.U;
 16 
 17                      break;
 18 
 19               case KeyEvent.VK_D:
 20 
 21                      dir = DirectionAlong.R;
 22 
 23                      break;
 24 
 25               case KeyEvent.VK_S:
 26 
 27                      dir = DirectionAlong.D;
 28 
 29                      break;
 30 
 31               }
 32 
 33        }
 34 
 35        public void keyReleased(KeyEvent e) {
 36 
 37               int key = e.getKeyCode();
 38 
 39               switch (key) {
 40 
 41               case KeyEvent.VK_J:
 42 
 43                      fire();
 44 
 45                      break;
 46 
 47               case KeyEvent.VK_A:
 48 
 49               case KeyEvent.VK_W:
 50 
 51               case KeyEvent.VK_D:
 52 
 53               case KeyEvent.VK_S:
 54 
 55                      dir = DirectionAlong.STOP;
 56 
 57                      break;
 58 
 59               }
 60 
 61        }
 62 
 63 public void move() {
 64 
 65               this.oldX = x;
 66 
 67               this.oldY = y;
 68 
 69               switch (dir) {
 70 
 71               case L:
 72 
 73                      x -= XSPEED;
 74 
 75                      break;
 76 
 77               case U:
 78 
 79                      y -= YSPEED;
 80 
 81                      break;
 82 
 83               case R:
 84 
 85                      x += XSPEED;
 86 
 87                      break;
 88 
 89               case D:
 90 
 91                      y += YSPEED;
 92 
 93                      break;
 94 
 95               case STOP:
 96 
 97                      break;
 98 
 99               }
100 
101               if (dir != DirectionAlong.STOP) {
102 
103                      ptDir = dir;
104 
105               }
106 
107               if (x < 0)
108 
109                      x = 0;
110 
111               if (y < 50)
112 
113                      y = 50;
114 
115               if (x + TANK_ALONG_WIDTH > TankClient.GAME_WIDTH)
116 
117                      x = TankClient.GAME_WIDTH - TANK_ALONG_WIDTH;
118 
119               if (y + TANK_ALONG_HEIGHT > TankClient.GAME_HEIGHT)
120 
121                      y = TankClient.GAME_HEIGHT - TANK_ALONG_HEIGHT;
122 
123        }

 

为了消除画面的闪烁现象,使用了缓冲技术,就是创建一个虚拟的图片,在内存中把所有需要画的画完,再一次性现实到屏幕上。

ScreenImage
 1 if (offScreenImage == null) {
 2 
 3                   offScreenImage = this.createImage(GAME_WINDOW_WIDTH,
 4 
 5                                 GAME_WINDOW_HEIGHT);
 6 
 7            }
 8 
 9            Graphics gOffScreen = offScreenImage.getGraphics();
10 
11            Color c = gOffScreen.getColor();
12 
13            gOffScreen.setColor(Color.GREEN);
14 
15            gOffScreen.fillRect(0, 0, GAME_WINDOW_WIDTH, GAME_WINDOW_HEIGHT);
16 
17            gOffScreen.setColor(c);
18 
19            paint(gOffScreen);
20 
21            g.drawImage(offScreenImage, 0, 0, null);

2.3子弹的创建与飞行

子弹的实现与坦克相类似,只是子弹发出后不会改变方向,只需要不段前进就可以了,相对坦克容易些。子弹类中有x,y,dir的成员属性,draw方法等封装了起来。实现许多子弹在屏幕上飞行的效果,就是为每个子弹创建一个实例,在TankClient类中有个数组来管理,子弹类中也有出界的处理,出界后会把子弹的live属性设为false;从而这在missiles数组中除去。   

missiles
 1 List<Missile> missiles = new ArrayList<Missile>();
 2 
 3        for (int i = 0; i < missiles.size(); i++) {
 4 
 5                                           Missile m = missiles.get(i);
 6 
 7                                           m.hitTanks(tanks);
 8 
 9                                           m.hitMapElements(maps);
10 
11                                           m.hitTank(tank1p);
12 
13                                           if (!single)
14 
15                                                  m.hitTank(tank2p);
16 
17                                           if (!m.isLive()) {
18 
19                                                  missiles.remove(m);
20 
21                                           } else
22 
23                                                  m.draw(g);
24 
25        }

子弹的创建是在坦克类fire()方法中。

missiles add
 1   if (!live)
 2 
 3                      return;
 4 
 5               int x = this.x + Tank.TANK_ALONG_WIDTH / 2 - Missile.WIDTH / 2;
 6 
 7               int y = this.y + Tank.TANK_ALONG_HEIGHT / 2 - Missile.HEIGHT / 2;
 8 
 9               MissileAlong m = new MissileAlong(x, y, ptDir, good, tc, id);
10 
11               tc.missiles.add(m);

2.4敌人坦克的创建与AI

敌人的坦克与玩家的坦克一样有draw()方法,move()方法,fire()方法等,只是他们不是通过响应键盘事件来控制,而是简单的AI控制,AI的控制是通过随机数来实现的,给它一个随机的方法和随机开火。区分敌我的标志是good属性,good为true是己方,false为敌人。

DirectionAlong
 1 DirectionAlong[] dires = DirectionAlong.values();
 2 
 3               if (step == 0) {
 4 
 5                      step = r.nextInt(13) + 3;
 6 
 7                      int in = r.nextInt(dires.length);
 8 
 9                      dir = dires[in];
10 
11               }
12 
13               step--;
14 
15               if (r.nextInt(100) > 95)
16 
17                    
18 
19   this.fire();

敌人坦克创建在PaintThread线程中,在能创建新的敌人坦克的情况下,每隔秒创建一个,具体的创建方法是addEnemy();

敌人坦克的数量是很多的,所以在Tankclient中有个数组来管理。

tanks
 1 List<Tank> tanks = new ArrayList<Tank>();
 2 
 3 for (int i = 0; i < tanks.size(); i++) {
 4 
 5                                           Tank t = tanks.get(i);
 6 
 7                                           t.collidesWithMapElements(maps);
 8 
 9                                           t.collidesWithTanks(tanks);
10 
11                                           t.collidesWithTank(tank1p);
12 
13                                           if (!single)
14 
15                                                  t.collidesWithTank(tank2p);
16 
17                                           if (!t.isLive()) {
18 
19                                                  tanks.remove(t);
20 
21                                                  enemyLive--;
22 
23                                           } else
24 
25                                                  t.draw(g);
26 
27                                    }

2.5将敌人坦克击毙

子弹类中加入hitTank(Tank t)的方法来击毙敌人坦克,碰撞检测的辅助类Rectangle,在坦克类和子弹类中都加入getRect()方法,得到其外切矩形。当击中敌人坦克时,敌人坦克死亡,子弹也死亡。由于有很多坦克所以还加了方法 hitTanks(List<Tank> tanks) 来检测某子弹与一群坦克的碰撞。

  

hitTank
 1        public Rectangle getRect() {
 2 
 3               return new Rectangle(x, y, WIDTH, HEIGHT);
 4 
 5        }
 6 
 7        public boolean hitTank(Tank t) {
 8 
 9               if (this.live && t.isLive() && this.good != t.isGood()
10 
11                             && this.getRect().intersects(t.getRect())) {
12 
13                      this.live = false;
14 
15                      if(!t.isPower()){
16 
17                             t.setLive(false);}
18 
19                      Sound.sounds(Sound.HIT);
20 
21                      return true;
22 
23               }
24 
25               return false;
26 
27        }
28 
29        public boolean hitTanks(List<Tank> tanks) {
30 
31               for(int i = 0; i<tanks.size(); i++){
32 
33                      if(hitTank(tanks.get(i))){
34 
35                             return true;
36 
37                      }
38 
39               }
40 
41               return false;
42 
43        }

 

 2.6击毙坦克时产生爆炸

爆炸的效果是在一个点上按顺序画一组图片产生的,主要方法为draw()方法。有个成员属性step来记录爆炸发生到第几步,画完后会把live属性设为false。应为会有许多爆炸在同事出现在屏幕上,所以在TankClient中有个数组管理。

draw Graphics
 1 public void draw(Graphics g){
 2 
 3               if(!live) return;  
 4 
 5               if(!init){
 6 
 7                      for (int i = 0; i < imgs.length; i++) {
 8 
 9                             g.drawImage(imgs[i], -10, -10, null);
10 
11                      }
12 
13                      init = true;
14 
15               }     
16 
17               if(step >= imgs.length) {
18 
19                      step = 0;
20 
21                      live = false;
22 
23                      return;
24 
25               }     
26 
27               g.drawImage(imgs[step],x,y,null);
28 
29               step++;
30 
31        }

 

转载于:https://www.cnblogs.com/lefan/archive/2012/12/27/2836377.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值