1.线程-应用于坦克战争04版5991.1增加功能599-601
java线程的基本知识,现在让我们实际使用它。添加以下功能:当玩家按下j键时,发射子弹.
当用户按下J键时,我们的坦克会发射子弹.
1.1.1思路1.发射子弹时,相当于启动线程
2.Hero有子弹对象。当我们按下J时,我们将启动发射行为(线程),使子弹不断移动,形成射击效果
3.MyPanel需要不断重绘子弹才能产生这种效果.
4.当子弹移动到面板的边界时,应销毁(销毁启动子弹的线程)
com中的代码.stulzl.Tankgame04.包中测试Tankgame04package com.stulzl.tankgame04;import javax.swing.*;//主方法 572public class Tankgame04 extends JFrame { ///定义我们的MyPanel MyPanel mp = null; public static void main(String[] args) { Tankgame04 tankgame0 = new Tankgame04();//调用显示结果 } /////构造设置窗口信息 public Tankgame04(){ mp = new MyPanel();///初始化面板 ///将mp放入Thread 并启动 Thread thread = new Thread(mp); thread.start();///启动MyPanel线程 this.add(mp);///将面板添加到窗口 this.setSize(1000,750);//设置窗口大小 this.addKeyListener(mp);///让JFrame监控mp this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭程序 this.setVisible(true);//显示窗口 }}
父类Tankpackage com.stulzl.tankgame04;public class Tank {//父类坦克 private int x;///坦克的横坐标 private int y;///坦克纵坐标 private int direct;//在坦克方向0上 1右 2下 3左 577 private int speed = 1;//控制坦克速度 ///在坦克上右下左移动 577 public void moveUp(){//向上移动 y-=speed; } public void moveRight(){//向右移动 x+=speed; } public void moveDown(){//向下移动 y+=speed; } public void moveLeft(){//左移动 x-=speed; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public int getDirect() { return direct; } public void setDirect(int direct) { this.direct = direct; } public Tank(int x, int y) { this.x = x; this.y = y; } 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; }}
我们的坦克Heropackage com.stulzl.tankgame04;public class Hero extends Tank {///我们的坦克,设置坦克坐标,打开Shot射击线程 //定义Shot对象,表示射击(线程) Shot shot = null; public Hero(int x, int y) { super(x, y); } //射击方法 600 public void shortEnemyTank(){ //创建Shot对象,根据Hero坦克的位置 方向 switch(getDirect()){ case 0://向上 shot = new Shot(getX()+20,getY(),0);///因为射击Shot线程有参构造器,所以要传值 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; } ///启动Shot线程,因为Shot实现了Runnable接口,所以可以直接调用 Thread thread = new Thread(shot); thread.start();//启动 }}
Shot射击线程package com.stulzl.tankgame04;//发射子弹线程 600public class Shot implements Runnable{ int x;//子弹x坐标 int y;//子弹y坐标 int direct=0;//子弹的方向 int speed = 2;///子弹速度 boolean isLive = true;//子弹是否存活 //构造器,接收传来的值 public Shot(int x, int y, int direct) { this.x = x; this.y = y; this.direct = direct; } @Override public void run() {//射击行为 while(true){ //休息50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } 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; } } }}
敌人坦克Enemytankpackage com.stulzl.tankgame04;public class EnemyTank extends Tank{//敌人的坦克 public EnemyTank(int x, int y) { super(x, y); }}
MyPanel画板package com.stulzl.tankgame04;import javax.swing.*;import java.awt.*;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.util.Vector;//坦克绘图区//为响应键盘事件,Keylistener// 不断重绘子弹需要重绘子弹 MyPanel Runnnablele实现 ,作为线程使用 601public class MyPanel extends JPanel implements KeyListener,Runnable{ //定义我们的坦克 Hero hero = null; //定义敌人坦克,放入Vector中 578 Vector enemyTanks = new Vector<>(); int enemyTanksSize = 3;///敌人坦克数量 ////构造器设置坦克信息 public MyPanel(){ hero = new Hero(100,100);///创建并初始化自己的坦克坐标 hero.setSpeed(2);///改变坦克速度 ///初始化敌人坦克 for (int i = 0; i <enemyTanksSize; i++) { //创建并设置敌人坦克坐标,每隔100像素放一辆坦克 EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0); ////改变敌人坦克的方向 enemyTank.setDirect(2); ///舔敌人坦克到Vector enemyTanks.add(enemyTank); } } ///绘图方法 @Override public void paint(Graphics g) { super.paint(g); g.fillRect(0,0,1000,750);//填矩形,默认黑色,这是坦克的游戏区 //画出自己的坦克,我们把方法封装成一种方法,可以调用 573 drawTank(hero.getX(), hero.getY(), g,hero.getDirect(),1); ///画hero射击的子弹 if(hero.shot!=null && hero.shot.isLive){///如果子弹存在并存活,就画出来。如果子弹移动到边界,就不会画出来。 g.draw3DRect(hero.shot.x,hero.shot.y,1,1,false);//子弹大小 长1 高1 //this.repaint();//重绘 } ///画敌人坦克,Vector集合遍历Vector for (int i = 0; i <enemyTanks.size(); i++) { //取出敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); ///画敌人坦克 drawTank(enemyTank.getX(),enemyTank.getY(),g,enemyTank.getDirect(),0); } } /** * @param x 坦克的左上角 x 坐标 * @param y 坦克的左上角 y 坐标 * @param g 画笔 * @param direct 坦克方向(上下左右) * @param type 坦克类型 */ ////写作方法画坦克 public void drawTank(int x,int y,Graphics g,int direct,int type){ //不同类型的基础,设置不同的颜色 switch(type){ case 0:///敌人的坦克 g.setColor(Color.cyan); break; case 1:///我们的坦克 g.setColor(Color.yellow); break; } //按坦克方向,绘制相应方向的坦克 577 //direct 表示方向(0: 向上 1 向右 2 向下 3 向左 ) switch(direct){ case 0://表示坦克向上 g.fill3DRect(x,y,10,60,false);//左轮////画3D矩形看起来更像 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);//左轮////画3D矩形看起来更像 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,10,60,false);//左轮////画3D矩形看起来更像 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; case 3://向左 g.fill3DRect(x,y,60,10,false);//左轮////画3D矩形看起来更像 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; default: System.out.println(“暂时没有其他操作”); } } //三种方法 //有字符输出,触发 public void keyTyped(KeyEvent e) { } //按键盘,触发 ///处理按下wdsa键的情况 577 @Override public void keyPressed(KeyEvent e) { if(e.getKeyCode()==KeyEvent.VK_W){//方向上 //改变坦克的方向 hero.setDirect(0); hero.moveUp(); }else if(e.getKeyCode()==KeyEvent.VK_D){//方向右 hero.setDirect(1); hero.moveRight(); }else if(e.getKeyCode()==KeyEvent.VK_S){//方向下 hero.setDirect(2); hero.moveDown(); }else if(e.getKeyCode()==KeyEvent.VK_A)左方向{///方向 hero.setDirect(3); hero.moveLeft(); } //如果用户按J键,就发射子弹 if(e.getKeyCode()==KeyEvent.VK_J){ hero.shortEnemyTank();//调用射击方法 } this.repaint();//面板重绘 } ///松开键盘,触发 @Override public void keyReleased(KeyEvent e) { } @Override public void run() { while(true){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } this.repaint(); } }}
2.坦克战争05版602-6062.1增加功能1.让敌人的坦克发射子弹(可以有多颗子弹)
2.当我们的坦克击中敌人的坦克时,敌人的坦克就会消失,如果爆炸效果更好.
3.让敌人的坦克自由上下左右移动
4.控制我们的坦克和敌人的坦克在规定范围内移动分析--->解决
2.1.2思路com中的代码.stulzl.Tankgame05.包中测试Tankgame05package com.stulzl.tankgame05;import javax.swing.*;//主方法 572public class Tankgame05 extends JFrame { ///定义我们的MyPanel MyPanel mp = null; public static void main(String[] args) { Tankgame05 tankgame0 = new Tankgame05();//调用显示结果 } /////构造设置窗口信息 public Tankgame05(){ mp = new MyPanel();///初始化面板 ///将mp放入Thread 并启动 Thread thread = new Thread(mp); thread.start();///启动MyPanel线程 this.add(mp);///将面板添加到窗口 this.setSize(1000,750);//设置窗口大小 this.addKeyListener(mp);///让JFrame监控mp this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭程序 this.setVisible(true);//显示窗口 }}
父类Tankpackage com.stulzl.tankgame05;public class Tank {//父类坦克 private int x;///坦克的横坐标 private int y;///坦克纵坐标 private int direct;//在坦克方向0上 1右 2下 3左 577 private int speed = 1;//控制坦克速度 ///在坦克上右下左移动 577 public void moveUp(){//向上移动 y-=speed; } public void moveRight(){//向右移动 x+=speed; } public void moveDown(){//向下移动 y+=speed; } public void moveLeft(){//左移动 x-=speed; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public int getDirect() { return direct; } public void setDirect(int direct) { this.direct = direct; } public Tank(int x, int y) { this.x = x; this.y = y; } 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; }}
我们的坦克Heropackage com.stulzl.tankgame05;public class Hero extends Tank {///我们的坦克,设置坦克坐标,打开Shot射击线程 //定义Shot对象,表示射击(线程) Shot shot = null; public Hero(int x, int y) { super(x, y); } //射击方法 600 public void shortEnemyTank(){ //创建Shot对象,根据Hero坦克的位置 方向 switch(getDirect()){ case 0://向上 shot = new Shot(getX()+20,getY(),0);///因为射击Shot线程有参构造器,所以要传值 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; } ///启动Shot线程,因为Shot实现了Runnable接口,所以可以直接调用 Thread thread = new Thread(shot); thread.start();//启动 }}
敌人的坦克EnemyTankpackage com.stulzl.tankgame05;import java.util.Vector;public class EnemyTank extends Tank implements Runnable{//敌人的坦克 //在敌人坦克类,使用Vector 保存多个Shot Vector shots = new Vector<>(); boolean isLive = true;///敌人坦克存活 public EnemyTank(int x, int y) { super(x, y); } @Override public void run() { while(true){ //继续按照坦克的方向移动 605 switch(getDirect()){ case 0://向上 ///保持坦克朝一个方向走30步 for(int i=0;i<30;i++){ ///控制坦克移动范围不得超过边界,只能在边界内移动 606 if(getY()>0) { moveUp(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 1://向右 for(int i=0;i<30;i++){ if(getX()+60<1000){ moveRight(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 2://向下 for(int i=0;i<30;i++){ if(getY()+60<750){ moveDown(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 3://向左 for(int i=0;i<30;i++){ if(getX()>0){ moveLeft(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; } ///然后判断随机改变坦克方向0-3 setDirect((int)(Math.random()*4));//Math.random() [0-1) //Math.random()*4) [0-4) //结束线程 if(isLive==false){ break; } } }}
Shot射击线程package com.stulzl.tankgame05;//发射子弹线程 600public class Shot implements Runnable{ int x;//子弹x坐标 int y;//子弹y坐标 int direct=0;//子弹的方向 int speed = 2;///子弹速度 boolean isLive = true;//子弹是否存活 //构造器,接收传来的值 public Shot(int x, int y, int direct) { this.x = x; this.y = y; this.direct = direct; } @Override public void run() {//射击行为 while(true){ //休息50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } 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)){//子弹越界窗口的范围 isLive = false;///子弹死了 break; } } }}
炸弹Bombpackage com.stulzl.tankgame05;//炸弹 604public class Bomb { int x,y;///炸弹坐标 int life = 9;//炸弹生命周期,用来控制后面炸弹爆炸瞬间的大图还是小图? boolean isLive = true;//是否存活 public Bomb(int x, int y) { this.x = x; this.y = y; } //减少生命值 public void lifeDown(){ if(life>0){ life--; }else{ isLive = false; } }}
MyPanel画板package com.stulzl.tankgame05;import javax.swing.*;import java.awt.*;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.util.Vector;//坦克绘图区//为响应键盘事件,Keylistener// 不断重绘子弹需要重绘子弹 MyPanel Runnnablele实现 ,作为线程使用 601public class MyPanel extends JPanel implements KeyListener,Runnable{ //定义我们的坦克 Hero hero = null; //定义敌人坦克,放入Vector中 578 Vector enemyTanks = new Vector<>(); int enemyTanksSize = 3;///敌人坦克数量 //定义Vector,用于储存炸弹 Vector bombs = new Vector<>(); //定义三张炸弹图片来显示爆炸效果 //说明子弹击中坦克时,将Bomb对象添加到bombs中 Image image1 = null; Image image2 = null; Image image3 = null; ////构造器设置坦克信息 public MyPanel(){ ///我们的坦克设置 hero = new Hero(100,100);///创建并初始化自己的坦克坐标 hero.setSpeed(2);///改变坦克速度 //遍历 敌人坦克的初始化 for (int i = 0; i <enemyTanksSize; i++) { //创建并设置敌人坦克坐标,每隔100像素放一辆坦克 EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);//(100 * (i + 1)), 0敌人坦克坐标 ////改变敌人坦克的方向 enemyTank.setDirect(2); ////随机启动敌人坦克 Thread thread1 = new Thread(enemyTank); thread1.start(); ///给enemyTank 加一颗子弹 602 Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect()); ///添加enemytankVector 成员 enemyTank.shots.add(shot); //启动 shot 对象 Thread thread = new Thread(shot); thread.start(); ///将敌人坦克添加到Vector中 enemyTanks.add(enemyTank); } ///初始化图片对象 image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif")); image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif")); image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif")); } ///绘图方法 @Override public void paint(Graphics g) { super.paint(g); g.fillRect(0,0,1000,750);//填矩形,默认黑色,这是坦克的游戏区 //画出自己的坦克,我们把方法封装成一种方法,可以调用 573 drawTank(hero.getX(), hero.getY(), g,hero.getDirect(),1); ///画hero射击的子弹 if(hero.shot!=null && hero.shot.isLive==true){///如果子弹存在并存活,就画出来。如果子弹移动到边界,就不会画出来。 g.draw3DRect(hero.shot.x,hero.shot.y,1,1,false);//子弹大小 长1 高1 //this.repaint();//重绘 } //如果bombs集合中有对象,就画出 604 for (int i = 0; i < bombs.size(); i++) { //取出炸弹 Bomb bomb = bombs.get(i); ///根据当前bomb对象的life值判断相应的图片 if(bomb.life>6){ g.drawImage(image1,bomb.x, bomb.y, 60,60,this);///画大图片 }else if(bomb.life>3){ g.drawImage(image2,bomb.x, bomb.y, 60,60,this);///画中图 }else{ g.drawImage(image3,bomb.x, bomb.y, 60,60,this);///画小图片 } ///减少炸弹的生命值 bomb.lifeDown(); 如果bomblife为0,从bombs集合中删除 if(bomb.life==0){ bombs.remove(bomb); } } ///画敌人坦克,Vector集合遍历Vector for (int i = 0; i <enemyTanks.size(); i++) { ///从Vector中 取出敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); if(enemyTank.isLive) {//目前敌人坦克还活着,我们才画 ///画敌人坦克 drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0); //画出 enemyTank 所有子弹 602 for (int j = 0; j < enemyTank.shots.size(); j++) { //取出子弹 Shot shot = enemyTank.shots.get(j); //绘制 if (shot.isLive == true) { ///子弹存活 g.draw3DRect(shot.x, shot.y, 1, 1, false);//子弹大小 长1 高1 } else {//子弹不存活 //从Vector 移除 enemyTank.shots.remove(shot); } } } } } /** * @param x 坦克的左上角 x 坐标 * @param y 坦克的左上角 y 坐标 * @param g 画笔 * @param direct 坦克方向(上下左右) * @param type 坦克类型 */ ////写作方法画坦克 public void drawTank(int x,int y,Graphics g,int direct,int type){ //不同类型的基础,设置不同的颜色 switch(type){ case 0:///敌人的坦克 g.setColor(Color.cyan); break; case 1:///我们的坦克 g.setColor(Color.yellow); break; } //按坦克方向,绘制相应方向的坦克 577 //direct 表示方向(0: 向上 1 向右 2 向下 3 向左 ) switch(direct){ case 0://表示坦克向上 g.fill3DRect(x,y,10,60,false);//左轮////画3D矩形看起来更像 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);//左轮////画3D矩形看起来更像 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,10,60,false);//左轮////画3D矩形看起来更像 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; case 3://向左 g.fill3DRect(x,y,60,10,false);//左轮////画3D矩形看起来更像 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; default: System.out.println(“暂时没有其他操作”); } } ///写一个方法来判断我们的子弹是否击中了敌人坦克 603 public void hitTank(Shot s,EnemyTank enemyTank){ ///判断子弹s是否击中坦克 switch(enemyTank.getDirect()){ case 0://向上 case 2://向下//如果子弹在坦克区,说明击中坦克 if(s.x>enemyTank.getX() && s.x<enemyTank.getX()+40 && s.y>enemyTank.getY() && s.y<enemyTank.getY()+60){ s.isLive = false; enemyTank.isLive = false; //当我们的子弹击中敌人坦克时,将enemytank从Vector中删除 enemyTanks.remove(enemyTank); ///创建Bomb对象,加入bombs集合 Bomb bomb = new Bomb(enemyTank.getX(),enemyTank.getY()); bombs.add(bomb);////将炸弹添加到金bombsVector集合中 } break; case 1: case 3: if(s.x>enemyTank.getX() && s.x<enemyTank.getX()+60 && s.y>enemyTank.getY() && s.y<enemyTank.getY()+40){ s.isLive = false; enemyTank.isLive = false; //当我们的子弹击中敌人坦克时,将enemytank从Vector中删除 605 enemyTanks.remove(enemyTank); ///创建Bomb对象,加入bombs集合 Bomb bomb = new Bomb(enemyTank.getX(),enemyTank.getY()); bombs.add(bomb);////将炸弹添加到金bombsVector集合中 } break; } } //三种方法 //有字符输出,触发 public void keyTyped(KeyEvent e) { } //按键盘,触发 ///处理按下wdsa键的情况 577 @Override public void keyPressed(KeyEvent e) { if(e.getKeyCode()==KeyEvent.VK_W){//方向上 //改变坦克的方向 hero.setDirect(0); if(hero.getY()>0){///判断我们的坦克,在边界范围内移动 hero.moveUp(); } }else if(e.getKeyCode()==KeyEvent.VK_D){//方向右 hero.setDirect(1); if(hero.getX()+60<1000){ hero.moveRight(); } }else if(e.getKeyCode()==KeyEvent.VK_S){//方向下 hero.setDirect(2); if(hero.getY()+60<750){ hero.moveDown(); } }else if(e.getKeyCode()==KeyEvent.VK_A)左方向{///方向 hero.setDirect(3); if(hero.getX()>0){ hero.moveLeft(); } } //如果用户按J键,就发射子弹 if(e.getKeyCode()==KeyEvent.VK_J){ hero.shortEnemyTank();//调用射击方法 } this.repaint();//面板重绘 } ///松开键盘,触发 @Override public void keyReleased(KeyEvent e) { } @Override public void run() { while(true){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } ///判断是否击中敌人坦克 603 if(hero.shot!=null && hero.shot.isLive){//当我的子弹射出(即不是空的)并且 存活 //遍历敌人所有坦克,看看哪辆敌人坦克被击中 for (int i = 0; i < enemyTanks.size(); i++) { EnemyTank enemyTank = enemyTanks.get(i);///取出一辆敌人坦克 hitTank(hero.shot,enemyTank); } } this.repaint(); } }}
3.坦克战争06版607-6093.1增加功能1.我们坦克只有在发射子弹消亡后才能发射新子弹.=>扩展(发多颗子弹怎么办?
控制在我们的面板上,最多只有5个)
2.在敌人坦克发射的子弹消亡后,可以重新发射子弹.
3.当敌人的坦克击中我们的坦克时,我们的坦克消失了,爆炸了.
课后练习:让敌人坦克最多发射3颗(在面板上),我们的坦克最多发射3颗.
并且能产生正常的爆炸效果.
3.1.1思路:com中的代码.stulzl.tankgame06.包中测试Tankgame06package com.stulzl.tankgame06;import javax.swing.*;//主方法 572public class Tankgame06 extends JFrame { ///定义我们的MyPanel MyPanel mp = null; public static void main(String[] args) { Tankgame06 tankgame0 = new Tankgame06();//调用显示结果 } /////构造设置窗口信息 public Tankgame06(){ mp = new MyPanel();///初始化面板 ///将mp放入Thread 并启动 Thread thread = new Thread(mp); thread.start();///启动MyPanel线程 this.add(mp);///将面板添加到窗口 this.setSize(1000,750);//设置窗口大小 this.addKeyListener(mp);///让JFrame监控mp this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭程序 this.setVisible(true);//显示窗口 }}
父类Tankpackage com.stulzl.tankgame06;public class Tank {//父类坦克 private int x;///坦克的横坐标 private int y;///坦克纵坐标 private int direct;//在坦克方向0上 1右 2下 3左 577 private int speed = 1;//控制坦克速度 boolean isLive = true;////我们坦克生存标志 ///在坦克上右下左移动 577 public void moveUp(){//向上移动 y-=speed; } public void moveRight(){//向右移动 x+=speed; } public void moveDown(){//向下移动 y+=speed; } public void moveLeft(){//左移动 x-=speed; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public int getDirect() { return direct; } public void setDirect(int direct) { this.direct = direct; } public Tank(int x, int y) { this.x = x; this.y = y; } 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; }}
我们的坦克Heropackage com.stulzl.tankgame06;import java.util.Vector;public class Hero extends Tank {///我们的坦克,设置坦克坐标,打开Shot射击线程 ///我们的坦克生存标志islive直接写在父类中,方便 ///定义Shot对象,表示射击(线程) Shot shot = null; ///可以发射多颗子弹 607 Vector shots = new Vector<>(); //构造器 public Hero(int x, int y) { super(x, y); } //射击方法 600 public void shortEnemyTank(){ ///最多发射5颗子弹 607// if(shots.size()==5){///// return;// } //创建Shot对象,根据Hero坦克的位置 方向 switch(getDirect()){ case 0://向上 shot = new Shot(getX()+20,getY(),0);///因为射击Shot线程有参构造器,所以要传值 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; } ////将新创建的shot放入shots的Vector集合中 shots.add(shot); 由于Shot实现了Runnable接口,///启动Shot线程所以可以直接调用 Thread thread = new Thread(shot); thread.start();//启动 }}
敌人的坦克EnemyTankpackage com.stulzl.tankgame06;import java.util.Vector;public class EnemyTank extends Tank implements Runnable{//敌人的坦克 //在敌人坦克类,使用Vector 保存多个Shot子弹 Vector shots = new Vector<>(); boolean isLive = true;///敌人坦克存活 public EnemyTank(int x, int y) { super(x, y); } @Override public void run() { while(true){ 我们在这里判断,如果shotss, size() = 在shots集合中创建子弹,并启动 608 if(isLive==true && shots.size()==0){//敌人坦克存活,敌人子弹集合Vector为空 Shot s = null;//临时子弹变量 ///判断坦克方向,创建相应的子弹 switch(getDirect()){ case 0://向上 s = new Shot(getX()+20,getY(),0); break; case 1://向右 s = new Shot(getX()+60,getY()+20,1); break; case 2://向下 s = new Shot(getX()+20,getY()+60,2); break; case 3://向左 s = new Shot(getX(),getY()+20,3); break; } shots.add(s);////将子弹添加到shotsVector集合中 Thread thread = new Thread(s); thread.start();//启动子弹线程 } //继续按照坦克的方向移动 605 switch(getDirect()){ case 0://向上 ///保持坦克朝一个方向走30步 for(int i=0;i<30;i++){ ///控制坦克移动范围不得超过边界,只能在边界内移动 606 if(getY()>0) { moveUp(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 1://向右 for(int i=0;i<30;i++){ if(getX()+60<1000){ moveRight(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 2://向下 for(int i=0;i<30;i++){ if(getY()+60<750){ moveDown(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 3://向左 for(int i=0;i<30;i++){ if(getX()>0){ moveLeft(); } //休眠50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; } ///然后判断随机改变坦克方向0-3 setDirect((int)(Math.random()*4));//Math.random() [0-1) //Math.random()*4) [0-4) //结束线程 if(isLive==false){ break; } } }}
Shot射击线程package com.stulzl.tankgame06;//发射子弹线程 600public class Shot implements Runnable{ int x;//子弹x坐标 int y;//子弹y坐标 int direct=0;//子弹的方向 int speed = 2;///子弹速度 boolean isLive = true;//子弹是否存活 //构造器,接收传来的值 public Shot(int x, int y, int direct) { this.x = x; this.y = y; this.direct = direct; } @Override public void run() {//射击行为 while(true){ //休息50毫秒 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } 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)){//子弹越界窗口的范围 isLive = false;///子弹死了 break; } } }}
炸弹Bombpackage com.stulzl.tankgame06;//炸弹 604public class Bomb { int x,y;///炸弹坐标 int life = 9;//炸弹生命周期,用来控制后面炸弹爆炸瞬间的大图还是小图? boolean isLive = true;//是否存活 public Bomb(int x, int y) { this.x = x; this.y = y; } //减少生命值 public void lifeDown(){ if(life>0){ life--; }else{ isLive = false; } }}
MyPanel画板package com.stulzl.tankgame06;import javax.swing.*;import java.awt.*;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.util.Vector;//坦克绘图区//为响应键盘事件,Keylistener// 不断重绘子弹需要重绘子弹 MyPanel Runnnablele实现 ,作为线程使用 601public class MyPanel extends JPanel implements KeyListener,Runnable{ //定义我们的坦克 Hero hero = null; //定义敌人坦克,放入Vector中 578 Vector enemyTanks = new Vector<>(); int enemyTanksSize = 3;///敌人坦克数量 //定义Vector,用于储存炸弹 Vector bombs = new Vector<>(); //定义三张炸弹图片来显示爆炸效果 //说明子弹击中坦克时,将Bomb对象添加到bombs中 Image image1 = null; Image image2 = null; Image image3 = null; ////构造器设置坦克信息 public MyPanel(){ ///我们的坦克设置 hero = new Hero(500, 100);///创建并初始化自己的坦克坐标 hero.setSpeed(2);///改变坦克速度 //遍历 敌人坦克的初始化 for (int i = 0; i <enemyTanksSize; i++) { //创建并设置敌人坦克坐标,每隔100像素放一辆坦克 EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);//(100 * (i + 1)), 0敌人坦克坐标 ////改变敌人坦克的方向 enemyTank.setDirect(2); ////随机启动敌人坦克 Thread thread1 = new Thread(enemyTank); thread1.start(); ///给enemyTank 添加子弹shotot 602 Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect()); ///添加enemytankVector 成员 enemyTank.shots.add(shot); //启动 shot 子弹对象 Thread thread = new Thread(shot); thread.start(); ///将敌人坦克添加到Vector中 enemyTanks.add(enemyTank); } ///初始化图片对象 image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif")); image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif")); image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif")); } ///绘图方法 @Override public void paint(Graphics g) { super.paint(g); g.fillRect(0,0,1000,750);//填矩形,默认黑色,这是坦克的游戏区 //画出自己的坦克,我们把方法包装成一种方法,调用即可 573 if(hero !=null && hero.isLive==true) {///我们的坦克不是空的,而是存活的 drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1); } ///画hero射击子弹// if(hero.shot!=null && hero.shot.isLive==true){///如果子弹存在并存活,则绘制。如果子弹移动到边界,则不绘制// g.draw3DRect(hero.shot.x,hero.shot.y,1,1,false);//子弹大小 长1 高1// //this.repaint();////////重绘/// } ////画hero子弹集Vector 607 ////遍历绘制 for (int i = 0; i < hero.shots.size(); i++) { Shot shot = hero.shots.get(i);//取出一颗子弹 //如果子弹存在并存活,就画出来。如果子弹移动到边界,它将不会被绘制。 if(shot!=null && shot.isLive==true) { g.draw3DRect(shot.x,shot.y, 1, 1, false);//子弹大小 长1 高1 }else{///如果子弹是空的或已经被销毁,从shots的Vector集合中删除 hero.shots.remove(shot); } } //如果bombs集合中有对象,就画出 604 for (int i = 0; i < bombs.size(); i++) { //取出炸弹 Bomb bomb = bombs.get(i); ///根据当前bomb对象的life值判断相应的图片 if(bomb.life>6){ g.drawImage(image1,bomb.x, bomb.y, 60,60,this);///画大图片 }else if(bomb.life>3){ g.drawImage(image2,bomb.x, bomb.y, 60,60,this);///画中图 }else{ g.drawImage(image3,bomb.x, bomb.y, 60,60,this);///画小图片 } ///减少炸弹的生命值 bomb.lifeDown(); 如果bomblife为0,从bombs集合中删除 if(bomb.life==0){ bombs.remove(bomb); } } ///画敌人坦克,Vector集合遍历Vector for (int i = 0; i <enemyTanks.size(); i++) { ///从Vector中 取出敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); if(enemyTank.isLive) {//目前敌人坦克还活着,我们才画 ///画敌人坦克 drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0); //画出 enemyTank 所有子弹 602 for (int j = 0; j < enemyTank.shots.size(); j++) { //取出敌人子弹 Shot shot = enemyTank.shots.get(j); //绘制 if (shot.isLive == true) { ///子弹存活 g.draw3DRect(shot.x, shot.y, 1, 1, false);//子弹大小 长1 高1 } else {///敌人子弹不存活 //从Vector 移除 enemyTank.shots.remove(shot); } } } } } /** * @param x 坦克的左上角 x 坐标 * @param y 坦克的左上角 y 坐标 * @param g 画笔 * @param direct 坦克方向(上下左右) * @param type 坦克类型 */ ////写作方法画坦克 public void drawTank(int x,int y,Graphics g,int direct,int type){ //不同类型的基础,设置不同的颜色 switch(type){ case 0:///敌人的坦克 g.setColor(Color.cyan); break; case 1:///我们的坦克 g.setColor(Color.yellow); break; } //按坦克方向,绘制相应方向的坦克 577 //direct 表示方向(0: 向上 1 向右 2 向下 3 向左 ) switch(direct){ case 0://表示坦克向上 g.fill3DRect(x,y,10,60,false);//左轮////画3D矩形看起来更像 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);//左轮////画3D矩形看起来更像 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,10,60,false);//左轮////画3D矩形看起来更像 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; case 3://向左 g.fill3DRect(x,y,60,10,false);//左轮////画3D矩形看起来更像 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; default: System.out.println(“暂时没有其他操作”); } } ///写一个判断子弹击中坦克的方法 方法 603 public void hitTank(Shot s, Tank tank){//Tank 这里直接用父类Tank接收tank涉及多态,更方便 ///判断子弹s是否击中坦克 switch(tank.getDirect()){ case 0://向上 case 2://向下//如果子弹在坦克区,说明击中坦克 if(s.x>tank.getX() && s.x<tank.getX()+40 && s.y>tank.getY() && s.y<tank.getY()+60){ s.isLive = false; tank.isLive = false; //当我们的子弹击中敌人坦克时,将enemytank从Vector中删除 enemyTanks.remove(tank); ///创建bomb对象,加入bombs集合 Bomb bomb = new Bomb(tank.getX(),tank.getY()); bombs.add(bomb);///将炸弹添加到bombs的Vector集合中 } break; case 1: case 3: if(s.x>tank.getX() && s.x<tank.getX()+60 && s.y>tank.getY() && s.y<tank.getY()+40){ s.isLive = false; tank.isLive = false; //当我们的子弹击中敌人坦克时,将enemytank从Vector中删除 605 enemyTanks.remove(tank); ///创建Bomb对象,加入bombs集合 Bomb bomb = new Bomb(tank.getX(),tank.getY()); bombs.add(bomb);////将炸弹添加到金bombsVector集合中 } break; } } //三种方法 //有字符输出,触发 public void keyTyped(KeyEvent e) { } //按键盘,触发 ///处理按下wdsa键的情况 577 @Override public void keyPressed(KeyEvent e) { if(e.getKeyCode()==KeyEvent.VK_W){//方向上 //改变坦克的方向 hero.setDirect(0); if(hero.getY()>0){///判断我们的坦克,在边界范围内移动 606 hero.moveUp(); } }else if(e.getKeyCode()==KeyEvent.VK_D){//方向右 hero.setDirect(1); if(hero.getX()+60<1000){ hero.moveRight(); } }else if(e.getKeyCode()==KeyEvent.VK_S){//方向下 hero.setDirect(2); if(hero.getY()+60<750){ hero.moveDown(); } }else if(e.getKeyCode()==KeyEvent.VK_A)左方向{///方向 hero.setDirect(3); if(hero.getX()>0){ hero.moveLeft(); } } //如果用户按J键,就发射子弹 if(e.getKeyCode()==KeyEvent.VK_J){ //如果子弹是空的或者子弹被摧毁了,子弹就会被发射 ,一颗子弹只能发射一次 607// if(hero.shot==null || hero.shot.isLive==false){// hero.shortEnemyTank();///调用射击方法/// } ///发射多颗子弹 if(hero.shots.size()<5){///最多发射5颗子弹 607 hero.shortEnemyTank();//调用射击方法 } } this.repaint();//面板重绘 } ///松开键盘,触发 @Override public void keyReleased(KeyEvent e) { } //当我们的坦克能够连续发射子弹时,判断每颗子弹是否击中敌人坦克 607 public void hitEnemyTank(){ for (int j = 0; j < hero.shots.size(); j++) {//遍历子弹集合 Shot shot = hero.shots.get(j);///取出集合子弹 if(shot!=null && shot.isLive){//当我们的子弹射出(即不是空的)并且 存活 //遍历敌人所有坦克的集合,看看哪辆敌人坦克被击中 for (int i = 0; i < enemyTanks.size(); i++) { EnemyTank enemyTank = enemyTanks.get(i);///取出一辆敌人坦克 hitTank(shot,enemyTank);///调用判断是否击中 } } } } ///编写方法判断敌人坦克子弹是否击中我们坦克子弹 609 public void hitHero(){ ///遍历所有敌人坦克enemytanks的Vector集合 for (int i = 0; i < enemyTanks.size(); i++) { ///取出一辆敌人坦克 EnemyTank enemyTank = enemyTanks.get(i); ///遍历敌人坦克所有子弹shots集合Vectortor for(int j=0;j<enemyTank.shots.size();j++){ //取出子弹 Shot shot = enemyTank.shots.get(j); ////判断敌人坦克子弹shot是否击中我们的hero坦克 if(hero.isLive && shot.isLive){///我们的坦克存活,敌人子弹也存活 hitTank(shot,hero); } } } } @Override public void run() { while(true){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } ///判断是否击中敌人坦克 603 //这种方法只能判断当前射出的子弹击中敌人,单颗子弹//// if(hero.shot!=null && hero.shot.isLive){//当我的子弹射出(即不是空的)并且 存活// //遍历敌人所有坦克,看看哪个敌人坦克被击中/// for (int i = 0; i < enemyTanks.size(); i++) {// EnemyTank enemyTank = enemyTanks.get(i);////取出敌人坦克///// hitTank(hero.shot,enemyTank);// }// } ///调用多颗子弹击中敌人坦克 hitEnemyTank(); ////调用敌人坦克击中我们的Hero坦克方法 hitHero(); this.repaint(); } }}