目录
1.逻辑分析:
- 当发射一颗子弹后就相当于启动一个线程
- Hero有子弹的对象,当按下j,我们就启动一个线程,让子弹不停的移动,形成射击的效果。
- 我们MyPanel需要不停的重绘,才能出现该效果。
- 当子弹移动到面板的边界时就销毁。
2. 代码
1.Shot类
包含子弹的多种属性和方法
public class Shot implements Runnable{
//子弹的坐标
private int x;
private int y;
private int direct=0;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getDirect() {
return direct;
}
public void setDirect(int direct) {
this.direct = direct;
}
public boolean isLive() {
return isLive;
}
public void setLive(boolean live) {
isLive = live;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
private boolean isLive=true;
public Shot(int x, int y, int direct) {
this.x = x;
this.y = y;
this.direct = direct;
}
private int speed=2;
@Override
public void run() {
while (true){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//根据方向改变
switch (direct){
case 0://上
y-=speed;
break;
case 1://右
x+=speed;
break;
case 2:
y+=speed;
break;
case 3:
x-=speed;
break;
}
System.out.println("x="+x+"y="+y);
if (!(x>=0&&x<=1000&&y>=0&&y<=750)) {
isLive=false;
break;
}
}
}
}
因为这个子弹相对于坦克其实是相对独立的,所以我们把他单独一个类,并且做个线程处理。在run方法里面我们根据坦克的方向绘制出子弹的运动方向和轨迹。
2.Hero类
public class Hero extends Tank {
public Hero(int x, int y) {
super(x, y);
}
//定义一个shot对象
Shot shot=null;
public void shotEnemyTank(){
//根据当前hero对象的位置和方向来创建Shot
switch (getDirect()){
case 0://向上
shot=new Shot(getX()+20,getY(),0);
break;
case 1://向右
shot=new Shot(getX()+60,getY()+20,1);
break;
case 2://向下
shot=new Shot(getX()+20,getY()+60,2);
break;
case 3://向下
shot=new Shot(getX(),getY()+20,3);
break;
}
new Thread(shot).start();
}
//启动shot线程
}
我们对于hero增加了一个方法,把坦克的参数传到子弹类里面去。并开启了shot的线程。
3.MyPanel类
public class MyPanel extends JPanel implements KeyListener,Runnable {
//定义我的坦克
Hero hero=null;
//定义敌人的坦克,放入到vector里面
Vector<EnemyTank> enemyTanks=new Vector<>();
int enemyTankSize=3;
public MyPanel(){
hero=new Hero(100,100);
hero.setSpeed(1);
//初始化敌人的tank
for (int e = 0; e < enemyTankSize; e++) {
EnemyTank enemyTank=new EnemyTank(100*(e+1),0);
enemyTank.setDirect(3);
enemyTanks.add(enemyTank);
}
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.fillRect(0,0,1000,750);//
//画出坦克-封装方法
draw_tank(hero.getX(),hero.getY(),g,hero.getDirect(),0);
//画出hero射击的子弹
if(hero.shot!=null&&hero.shot.isLive()==true)
{
g.fill3DRect(hero.shot.getX(),hero.shot.getY(),2,2,false);
}
//画出敌人的坦克
for (EnemyTank enemyTank : enemyTanks) {
draw_tank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 1);
}
}
//编写方法,画出坦克
/**
*
* @param x 坦克左上角x的坐标
* @param y 坦克的左上角y的坐标
* @param g 画笔
* @param direct 坦克方向
* @param type 坦克类型
*/
public void draw_tank(int x,int y,Graphics g,int direct,int type){
switch (type){
case 0:
g.setColor(Color.CYAN);
break;
case 1:
g.setColor(Color.orange);
break;
}
//根据tank的方向绘制
//根据坦克的方向绘制对应形象的坦克
switch (direct){
case 0://表示向上
g.fill3DRect( x, y,10,60,false);
g.fill3DRect( x+30, y,10,60,false);
g.fill3DRect( x+10, y+10,20,40,false);
g.fillOval(x+10,y+20,20,20);
g.drawLine(x+20,y+30,x+20,y);
break;
case 1://表示向右
g.fill3DRect( x, y,60,10,false);
g.fill3DRect( x, y+30,60,10,false);
g.fill3DRect( x+10, y+10,40,20,false);
g.fillOval(x+20,y+10,20,20);
g.drawLine(x+30,y+20,x+60,y+20);
break;
case 2://表示向左
g.fill3DRect( x, y,60,10,false);
g.fill3DRect( x, y+30,60,10,false);
g.fill3DRect( x+10, y+10,40,20,false);
g.fillOval(x+20,y+10,20,20);
g.drawLine(x+30,y+20,x,y+20);
break;
case 3://表示向上
g.fill3DRect( x, y,10,60,false);
g.fill3DRect( x+30, y,10,60,false);
g.fill3DRect( x+10, y+10,20,40,false);
g.fillOval(x+10,y+20,20,20);
g.drawLine(x+20,y+30,x+20,y+60);
break;
default:
System.out.println("暂时没有处理");
}
}
@Override
public void keyTyped(KeyEvent e) {
}
//处理wsad按下的键
@Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_W)
{
//改变坦克的方向
hero.setDirect(0);
hero.move_up();
} else if (e.getKeyCode()==KeyEvent.VK_D) {
hero.setDirect(1);
hero.move_right();
} else if (e.getKeyCode()==KeyEvent.VK_A) {
hero.setDirect(2);
hero.move_left();
} else if (e.getKeyCode()==KeyEvent.VK_S) {
hero.setDirect(3);
hero.move_down();
}
if(e.getKeyCode()==KeyEvent.VK_J) {
System.out.println("用户按下j");
hero.shotEnemyTank();
}
this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
this.repaint();
}
}
}
在画板类里面我们增加了一个监听按键,以及监听j的按键为了启动子弹的线程。并在画板上面建立一个线程为了绘制子弹。这里我们会说为什么又要用一个线程。这个线程是为了让画板在执行。因为如果不去为了画板开一个线程,那么子弹就会画了一次就结束了,后面线程虽然还在运行。但是在画板上不能体现。
4.HspTankGame03类
public class HspTankGame03 extends JFrame {
//定义MyPanel
MyPanel mp=null;
public static void main(String[] args) {
HspTankGame03 hspTankGame01=new HspTankGame03();
}
public HspTankGame03(){
mp=new MyPanel();
Thread thread = new Thread(mp);
thread.start();HspTankGame03
this.add(mp);
this.addKeyListener(mp);//增加监听事件
this.setSize(1000,750);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
}
在主进程里面加一个画板的进程开启就行了。结果如下:
子弹就显现出来了