课件资源放在文末
1.线程-应用到坦克大战
1.1 坦克大战 0.3
【坦克类:包括坦克的基本属性,以及坦克的移动方法】
package com.hspedu.tankgame03;/*** @author 韩顺平* @version 1.0*/
public class Tank {private int x;//坦克的横坐标private int y;//坦克的纵坐标private int direct = 0;//坦克方向 0 上1 右 2下 3左private int speed = 1;public int getSpeed() {return speed;}public void setSpeed(int speed) {this.speed = speed;}//上右下左移动方法public void moveUp() {y -= speed;}public void moveRight() {x += speed;}public void moveDown() {y += speed;}public void moveLeft() {x -= 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;}
}
【我方坦克类】继承了坦克类,在这个版本中增加了发射子弹的方法【其实就是创建一个子弹对象,并且每一个子弹对象就是一个线程】
package com.hspedu.tankgame03;/*** @author 韩顺平* @version 1.0* 自己的坦克*/
public class Hero extends Tank {//定义一个Shot对象Shot shot =null;public Hero(int x, int y) {super(x, y);}//射击public void shotEnemyTank(){//创建 Shou 对象,根据当前Hero对象的位置和方向来创建switch (getDirect()){//得到hero方向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();}
}
【敌方坦克类】
package com.hspedu.tankgame03;/*** @author 韩顺平* @version 1.0* 敌人的坦克*/
public class EnemyTank extends Tank {public EnemyTank(int x, int y) {super(x, y);}
}
【子弹类】包括子弹的基本属性 ,以及子弹进程什么时候结束及其运动过程的变化
package com.hspedu.tankgame03;/*** @author 林然* @version 1.0*/
public class Shot implements Runnable{//设计子弹int x;//子弹x坐标int y;//子弹y坐标int direction =0;//子弹方向int speed = 2;//子弹宿舍boolean isLive = true;//是否存活//构造器public Shot(int x, int y, int direction) {this.x = x;this.y = y;this.direction = direction;}@Overridepublic void run() {while (true){try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}//根据方向来改变switch (direction){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;}}}
}
【面板类:主要在之前的基础上新增了绘制子弹的功能以及将面板也作为一个线程,实现Runnable,每隔一段时间进行面板重绘】
package com.hspedu.tankgame03;import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;/*** @author 韩顺平* @version 1.0* 坦克大战的绘图区域*///为了监听 键盘事件, 实现KeyListener//为了让Panel 不停地重绘子弹,需要将MyPanelRunnable,当做一个线程使用
public class MyPanel extends JPanel implements KeyListener,Runnable {//定义我的坦克Hero hero = null;//定义敌人坦克,放入到VectorVector<EnemyTank> enemyTanks = new Vector<>();int enemyTankSize = 3;public MyPanel() {hero = new Hero(100, 100);//初始化自己坦克//初始化敌人坦克for (int i = 0; i < enemyTankSize; i++) {//创建一个敌人的坦克EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);//设置方向enemyTank.setDirect(2);//加入enemyTanks.add(enemyTank);}}@Overridepublic void paint(Graphics g) {super.paint(g);g.fillRect(0, 0, 1000, 750);//填充矩形,默认黑色//画出自己坦克-封装方法drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1);//画出hero射击的子弹if(hero.shot!=null&&hero.shot.isLive!=false){g.draw3DRect(hero.shot.x,hero.shot.y,5,5,false);}//画出敌人的坦克, 遍历Vectorfor (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;}//根据坦克方向,来绘制对应形状坦克//direct 表示方向(0: 向上 1 向右 2 向下 3 向左 )//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, 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;case 3: //表示向左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;default:System.out.println("暂时没有处理");}}@Overridepublic void keyTyped(KeyEvent e) {}//处理wdsa 键按下的情况@Overridepublic void keyPressed(KeyEvent e) {System.out.println(e.getKeyCode());if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键//改变坦克的方向hero.setDirect(0);////修改坦克的坐标 y -= 1hero.moveUp();} else if (e.getKeyCode() == KeyEvent.VK_D) {//D键, 向右hero.setDirect(1);hero.moveRight();} else if (e.getKeyCode() == KeyEvent.VK_S) {//S键hero.setDirect(2);hero.moveDown();} else if (e.getKeyCode() == KeyEvent.VK_A) {//A键hero.setDirect(3);hero.moveLeft();}if(e.getKeyCode()==KeyEvent.VK_J){hero.shotEnemyTank();}//让面板重绘this.repaint();}@Overridepublic void keyReleased(KeyEvent e) {}@Overridepublic void run() {while (true){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}this.repaint();}}
}
【主方法类:将面板作为一个线程启动】
package com.hspedu.tankgame03;import javax.swing.*;/*** @author 韩顺平* @version 1.0*/
public class HspTankGame03 extends JFrame {//定义MyPanelMyPanel mp = null;public static void main(String[] args) {HspTankGame03 hspTankGame01 = new HspTankGame03();}public HspTankGame03() {mp = new MyPanel();Thread thread =new Thread(mp);thread.start();this.add(mp);//把面板(就是游戏的绘图区域)this.setSize(1000, 750);this.addKeyListener(mp);//让JFrame 监听mp的键盘事件this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setVisible(true);}
}
1.2 坦克大战 0.4 版
在本次项目中大量在使用线程,我们也要知道即使线程结束了,但是其对象也是依然存在的,对于不同的逻辑操作我们可以在对应的类中写出对应的方法并不断重绘,对于需要反复判断的我们也要在Mypanel的run方法中不断进行调用
package com.hspedu.tankgame04;import javax.swing.*;/*** @author 林然* @version 1.0*/
public class HspTankGame04 extends JFrame {//定义MyPanelMyPanel mp = null;public static void main(String[] args) {HspTankGame04 hspTankGame01 = new HspTankGame04();}public HspTankGame04() {mp = new MyPanel();Thread thread =new Thread(mp);thread.start();this.add(mp);//把面板(就是游戏的绘图区域)this.setSize(1000, 750);this.addKeyListener(mp);//让JFrame 监听mp的键盘事件this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setVisible(true);}
}
package com.hspedu.tankgame04;import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;/*** @author linran* @version 1.0* 坦克大战的绘图区域*///为了监听 键盘事件, 实现KeyListener//为了让Panel 不停地重绘子弹,需要将MyPanelRunnable,当做一个线程使用
public class MyPanel extends JPanel implements KeyListener,Runnable {//定义我的坦克Hero hero = null;//定义敌人坦克,放入到VectorVector<EnemyTank> enemyTanks = new Vector<>();int enemyTankSize = 3;//定义一个Vector用于存放炸弹//当子弹击中坦克时,就加入Vector<Bomb> bombs =new Vector<>();//定义三张图片用于显示爆炸效果Image image1 = null;Image image2 =null;Image image3 =null;public MyPanel() {hero = new Hero(100, 100);//初始化自己坦克//初始化敌人坦克for (int i = 0; i < enemyTankSize; i++) {//创建一个敌人的坦克EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);//设置方向enemyTank.setDirect(2);//启动坦克线程,让他动起来new Thread(enemyTank).start();//加入一颗子弹Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect());//加入enemyTank的Vector成员进行管理enemyTank.shots.add(shot);//启动shot对象new Thread(shot).start();//加入enemyTanks.add(enemyTank);}//初始化炸弹图片image1=Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/bomb_1.gif"));image2=Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/bomb_2.gif"));image3=Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/bomb_3.gif"));}@Overridepublic void paint(Graphics g) {super.paint(g);g.fillRect(0, 0, 1000, 750);//填充矩形,默认黑色//画出自己坦克-封装方法if(hero.isLife){drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1);}//画出hero射击的子弹for (int i=0;i<hero.shots.size();i++){Shot shot = hero.shots.get(i);if(shot!=null&&shot.isLive!=false){g.draw3DRect(shot.x,shot.y,5,5,false);}else {//从vector移除hero.shots.remove(shot);}}for(int i=0;i<bombs.size();i++){Bomb bomb = bombs.get(i);//根据当前对象的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();if(bomb.life==0){bombs.remove(i);//如果生命为0就删除}}//画出敌人的坦克, 遍历Vectorfor (int i = 0; i < enemyTanks.size(); i++) {//取出坦克EnemyTank enemyTank = enemyTanks.get(i);if(enemyTank.isLive){//当子弹存活才画出坦克drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0);//画出enemyTank的所有子弹for (int j=0;j<enemyTank.shots.size();j++){//取出子弹Shot shot = enemyTank.shots.get(j);if(shot.isLive!=false){g.draw3DRect(shot.x,shot.y,5,5,false);}else {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;}//根据坦克方向,来绘制对应形状坦克//direct 表示方向(0: 向上 1 向右 2 向下 3 向左 )//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, 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;case 3: //表示向左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;default:System.out.println("暂时没有处理");}}//判断我方子弹是否击中敌人坦克public void hittank(Vector<Shot> shots,EnemyTank enemyTank){//判断s击中坦克for (int i=0;i<shots.size();i++){Shot s = shots.get(i);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;//创建Bomb对象加入集合中Bomb bomb =new Bomb(enemyTank.getX(),enemyTank.getY());bombs.add(bomb);enemyTanks.remove(enemyTank);}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;//创建Bomb对象加入集合中Bomb bomb =new Bomb(enemyTank.getX(),enemyTank.getY());bombs.add(bomb);enemyTanks.remove(enemyTank);}break;}}}//判断我方子弹是否击中敌人坦克public void hitHero(Vector<Shot> shots,Hero hero){//判断s击中坦克for (int i=0;i<shots.size();i++){Shot s = shots.get(i);if(s.isLive&&hero.isLife){switch (hero.getDirect()){case 0://上case 2://下if(s.x>hero.getX()&&s.x<hero.getX()+40&&s.y>hero.getY()&&s.y<hero.getY()+60){ s.isLive=false;//创建Bomb对象加入集合中Bomb bomb =new Bomb(hero.getX(),hero.getY());bombs.add(bomb);hero.isLife=false;}break;case 1: //左右case 3:if(s.x>hero.getX()&&s.x<hero.getX()+60&&s.y>hero.getY()&&s.y<hero.getY()+40){ s.isLive=false;hero.isLife=false;//创建Bomb对象加入集合中Bomb bomb =new Bomb(hero.getX(),hero.getY());bombs.add(bomb);}break;}}}}@Overridepublic void keyTyped(KeyEvent e) {}//处理wdsa 键按下的情况@Overridepublic void keyPressed(KeyEvent e) {System.out.println(e.getKeyCode());if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键//改变坦克的方向hero.setDirect(0);////修改坦克的坐标 y -= 1hero.moveUp();} else if (e.getKeyCode() == KeyEvent.VK_D) {//D键, 向右hero.setDirect(1);hero.moveRight();} else if (e.getKeyCode() == KeyEvent.VK_S) {//S键hero.setDirect(2);hero.moveDown();} else if (e.getKeyCode() == KeyEvent.VK_A) {//A键hero.setDirect(3);hero.moveLeft();}if(e.getKeyCode()==KeyEvent.VK_J){//if(!(hero.shot!=null&&hero.shot.isLive)) {发射一颗子弹if(hero.shots.size()<5)hero.shotEnemyTank();//发射多颗子弹,在面板最多有5g颗子弹//}}//让面板重绘this.repaint();}@Overridepublic void keyReleased(KeyEvent e) {}@Overridepublic void run() {while (true){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//判断是否击中坦克if(hero.shots.size()>0){//当前我的子弹存活//遍历敌人所有坦克for (int i = 0; i < enemyTanks.size(); i++) {EnemyTank enemyTank =enemyTanks.get(i);hittank(hero.shots,enemyTank);}}//判断是否击中我方坦克for (int i=0;i<enemyTanks.size();i++){hitHero(enemyTanks.get(i).shots,hero);}this.repaint();}}
}
package com.hspedu.tankgame04;/*** @author 林然* @version 1.0*/
public class Tank {private int x;//坦克的横坐标private int y;//坦克的纵坐标private int direct = 0;//坦克方向 0 上1 右 2下 3左private int speed = 5;public int getSpeed() {return speed;}public void setSpeed(int speed) {this.speed = speed;}//上右下左移动方法public void moveUp() {if(y>0)//判断边界条件,个人感觉在这里设置的话我方和敌人坦克都可以直接控制{y -= speed;}}public void moveRight() {if(x<1000&&(x+80)<1000){x += speed;}}public void moveDown() {if(y<750&&(y+100)<750){y += speed;}}public void moveLeft() {if(x>0){x -= 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;}
}
package com.hspedu.tankgame04;import java.util.Vector;/*** @author 林然* @version 1.0* 敌人的坦克*/
public class EnemyTank extends Tank implements Runnable{Vector <Shot> shots=new Vector<>();boolean isLive =true;public EnemyTank(int x, int y) {super(x, y);}//射击public void shotHeroTank(){//创建 Shou 对象,根据当前EnemyTank对象的位置和方向来创建Shot shot =null;switch (getDirect()){//得到EnemyTank方向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;}//加入子弹集合shots.add(shot);//启动子弹线程new Thread(shot).start();}@Overridepublic void run() {int count=0;while (isLive){if(shots.size()<5){shotHeroTank();}count++;//根据坦克方向来继续移动switch (getDirect()){case 0://向上moveUp();break;case 1://向右moveRight();break;case 2://向下moveDown();break;case 3://向左moveLeft();break;}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//然后随机的改变坦克方向[0-4).连续走10步后换方向if(count%10==0){setDirect((int)(Math.random()*4));count=0;}//写并发程序一定要考虑线程什么时候结束}}
}
package com.hspedu.tankgame04;import java.util.Vector;/*** @author linran* @version 1.0* 自己的坦克*/
public class Hero extends Tank {//定义一个Shot对象Shot shot =null;boolean isLife =true;//可以发送多颗子弹Vector<Shot> shots =new Vector<>();public Hero(int x, int y) {super(x, y);}//射击public void shotEnemyTank(){//创建 Shou 对象,根据当前Hero对象的位置和方向来创建switch (getDirect()){//得到hero方向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;}//加入子弹集合shots.add(shot);//启动子弹线程new Thread(shot).start();}
}
package com.hspedu.tankgame04;/*** @author 林然* @version 1.0*/
public class Shot implements Runnable{//设计子弹int x;//子弹x坐标int y;//子弹y坐标int direction =0;//子弹方向int speed = 2;//子弹宿舍boolean isLive = true;//是否存活//构造器public Shot(int x, int y, int direction) {this.x = x;this.y = y;this.direction = direction;}@Overridepublic void run() {while (true){try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}//根据方向来改变switch (direction){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;}}}
}
package com.hspedu.tankgame04;/*** @author 林然* @version 1.0* 炸弹*/
public class Bomb {int x,y;//炸弹坐标int life = 9;//炸弹的生命周期boolean isLife =true;//是否还存活public Bomb(int x, int y) {this.x = x;this.y = y;}//减少生命值public void lifeDown(){if(life>0){life--;}else{isLife=false;}}
}
2.课件资源
链接:https://pan.baidu.com/s/1anOddH3cE47HuAUHyAGiVg?pwd=msmz
提取码:msmz