以JLable特性实现坦克大战(1)

以AWT特性简单写出坦克大战(1)

在学习java图形界面的时候我发现JLabel的特性可以完美避开图片闪烁问题,由此我以JLabel(javax.swing.JLabel)为核心完成了坦克大战的实现,并且拥有良好的扩展性,但由于自身能力有限,经过调试后任然有少量Bug和一些不完美的地方。

首先我回答使用JLabel的优越性,它支持添加图片,不会闪烁。下面看演示代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Example extends JFrame {
     static int a=350;
    static  int b=350;
    public static void main(String[] args) {
        // TODO Auto-generated method stub


         Example car=new Example(); //初始化顶层容器:窗体         


         JPanel jp=new JPanel(); //初始化面板
         car.add(jp);
         jp.setLayout(null);         

         JLabel testjl=new JLabel(new ImageIcon("enemy2D.gif"));//添加一张放在项目根目录的图片         
         testjl.setBounds(350, 350, 100, 100);
         jp.add(testjl);

         car.setResizable(false);//打开窗体      
         car.setBounds(400, 20, 800, 800);
         car.setTitle("Battle City Left:a    Right:d    Up:w    Down:s");
         car.setVisible(true);
         car.setDefaultCloseOperation(car.DISPOSE_ON_CLOSE);
         //控制方向             
         car.addKeyListener(new KeyListener() {

            @Override
            public void keyPressed(KeyEvent arg0) {
                // TODO Auto-generated method stub
                if(arg0.getKeyChar()=='w'||arg0.getKeyChar()=='W')
                {
                    b-=5;
                    testjl.setBounds(a, b, 60, 60);
                }

                if(arg0.getKeyChar()=='d'||arg0.getKeyChar()=='D')
                {
                    a+=5;
                    testjl.setBounds(a, b, 60, 60);
                }
                if(arg0.getKeyChar()=='s'||arg0.getKeyChar()=='S')
                {
                    b+=5;
                    testjl.setBounds(a, b, 60, 60);
                }
                if(arg0.getKeyChar()=='a'||arg0.getKeyChar()=='A')                                          
                {
                    a-=5;
                    testjl.setBounds(a, b, 60, 60);
                }
            }

            @Override
            public void keyReleased(KeyEvent arg0) {
                // TODO Auto-generated method stub

            }

            @Override
            public void b

            }            
         });    
    }
}

如代码显示,图片没有闪烁。


下是坦克大战的逻辑结构:
这里写图片描述

总体思路

程序启动后,四个线程和主线程一起运行。主类和敌方坦克控制类用于调用坦克类中的行走方法或开炮方法;这两个方法仅仅修改坦克对象的位置数据或新建一个炮弹线程(每颗炮弹我都用一个线程来处理)。画图类的工作只是遍历所有坦克对象并根据对象的数据(比如位置坐标)在面板上绘图。结束类用于判断游戏结束条件(所有敌方坦克的生命为0,或则P1生命为0)

详细设计

1.我们先构建Tank类:Tank.java
Tank类主要作用:

  1. 定义坦克属性:坦克图片,生命,位置,速度,朝向 炮弹图片,图片直径
  2. 实现坦克行走功能:walk()
    该函数用于坦克的移动,每次调用都会改变坦克对象的位置数据。
  3. 实现坦克开炮功能:fire()
    该函数用于坦克开炮,每次调用都会新建一个炮弹线程,这个线程就在有限空间内负责炮弹所有的能力。
    4.下面是详细代码
import javax.swing.*;   
import java.lang.Math;
public class Tank extends Main {
    //坦克类
    //属性:坦克图片,生命,位置,速度,朝向 炮弹图片,图片直径
    JLabel image;//坦克图片
    ImageIcon imiclib[]=new ImageIcon[4];//四个方向的坦克图标
    JLabel shellPicture=new JLabel(new ImageIcon("tankmissile.gif"));//炮弹图片
    boolean life=true;//生命
    int locationX;//x坐标
    int locationY;//y坐标
    int speed;//速度
    int direction;//方向
    double circled=2*25*Math.sqrt(2);//图片直径
    boolean fire=true;//能否开炮
    //构造方法
    public Tank(ImageIcon imiclib[],int x,int y,int speed,int direction)
    {
        this.image=new JLabel(imiclib[0]);
        this.imiclib=imiclib;
        this.locationX=x;
        this.locationY=y;
        this.speed=speed;
        this.direction=direction;

    }
    public Tank() {}; 
    //行走
    void walk(int direction,Tank aimtank[])
    {
        boolean walk2=true;
        boolean walk=true;      
        //1.图片情况(根据不同的方向装载图片)
        if(this.direction!=direction)
        {           
            this.direction=direction;
            this.image.setIcon(this.imiclib[this.direction]);
        }

        //2.边界碰撞用walk判断
        switch(this.direction)
        {
           case 0:if(this.locationY<=0) walk=false; break;
           case 1:if(this.locationX>=736) walk=false; break;
           case 2:if(this.locationY>=704) walk=false; break;
           case 3:if(this.locationX<=0) walk=false; break;
        }
        //3.行进方向坦克碰撞 用walk2判断
        for(int i=0;i<aimtank.length;i++)
        {
            if(this.equals(aimtank[i]))
                continue;           
            //方法二,把坦克看成一个圆,判断相切;
            //*********************
            //1.未相撞到相撞的判断
            //2.相撞到离开的判断
            //*********************
            int tX=this.locationX+30;//获取圆心
            int tY=this.locationY+30;
            int atX=aimtank[i].locationX+30;
            int atY=aimtank[i].locationY+30;
            double distance=Math.sqrt((Math.pow(tX-atX, 2)+Math.pow(tY-atY, 2)));//两坦克中心的距离 
            if(this.circled>=distance)//图片直径大于两坦克中心距离时,判断为相切
            {
                walk2=false;
                /*
                *下面要考虑怎么离开相切状态。相切后如果向着使distance减小的方向,那么任然会处在相切的状态
                *所以只能向着使distance增大方向移动,直到它大于图片直径。
                *所以我用一个switch语句表达这个过程,有点像提前预测坦克行走方向,如果向着某个方向能使distance
                *增大,那么这个方向就走出相撞状态的方向。
                */              
                switch(this.direction)//进行预测,往哪个方向能够离开
                {
                  case 0:
                  {
                      //System.out.println("方向0");
                      tY-=10;
                      distance=Math.sqrt((Math.pow(tX-atX, 2)+Math.pow(tY-atY, 2)));//预测后的距离                      
                        if(this.circled<=distance)
                        {
                            walk2=true;                         
                        }
                      break;
                  }
                  case 1:
                  {
                      tX+=10;
                      distance=Math.sqrt((Math.pow(tX-atX, 2)+Math.pow(tY-atY, 2)));
                      if(this.circled<=distance)
                            walk2=true;
                      break;
                  }
                  case 2:
                  {
                      tY+=10;
                      distance=Math.sqrt((Math.pow(tX-atX, 2)+Math.pow(tY-atY, 2)));
                      if(this.circled<=distance)
                            walk2=true;
                      break;
                  }
                  case 3:
                  {
                      tX-=10;
                      distance=Math.sqrt((Math.pow(tX-atX, 2)+Math.pow(tY-atY, 2)));
                      if(this.circled<=distance)
                            walk2=true;
                      break;
                  }
                }
            }
        }

        //3.确定方向,改变数据
        if(walk&&walk2)
        {
            switch(this.direction)
            {
            case 0:this.locationY-=8; break;
            case 1:this.locationX+=8; break;
            case 2:this.locationY+=8; break;
            case 3:this.locationX-=8; break;
            }           
        }
    }


    //开炮
    public void fire(JPanel Cenjp,Tank tank[],Tank tankmian)
    {

        switch(this.direction)
        {
        //根据不同方向实例化一个炮弹对象并创建线程
        //把炮弹加入到窗体中
        case 0: 
            {
                //根据不同方向实例化一个炮弹对象
                Shell shell=new Shell(shellPicture, locationX+22, locationY-20,direction);
                //把炮弹加入到窗体中
                Cenjp.add(shell.shell); 
                Thread th=new Thread(new ThreadShellMe(tank,shell,tankmian));               
                th.start();
                break;
            }
        case 1:
            {
                Shell shell=new Shell(shellPicture, locationX+70, locationY+23,direction);
                Cenjp.add(shell.shell); 
                Thread th=new Thread(new ThreadShellMe(tank,shell,tankmian));               
                th.start();
                break;
            }
        case 2:
            {
                Shell shell=new Shell(shellPicture, locationX+22, locationY+70,direction);
                Cenjp.add(shell.shell); 
                Thread th=new Thread(new ThreadShellMe(tank,shell,tankmian));               
                th.start();
                break;
            }
        case 3: 
            {
                Shell shell=new Shell(shellPicture, locationX-20,locationY+22,direction);
                Cenjp.add(shell.shell); 
                Thread th=new Thread(new ThreadShellMe(tank,shell,tankmian));               
                th.start();
                break;
            }

        }
    }

}

到此Tank.java完成。我们成功构建了坦克对象以及实现了坦克的两大能力:行走 开炮。
·····························未完待续,下一篇讲游戏开始动画类,主类,结束类等几个简单类的实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值