1、java绘图机制
2、多线程基础
@SuppressWarnings("all") public class Tank { private int x; //坦克横坐标 private int y; //坦克纵坐标 private int direct;//表示方向(0-上, 1-右, 2-下, 3-左) private int type; Bullet bullet = null; //定义一个子弹对象 private int bulletNum = 1; //坦克的子弹数量-->给子类去设置 private boolean isTankAlive = true; public boolean isTankAlive() { return isTankAlive; } public void setTankAlive(boolean tankAlive) { isTankAlive = tankAlive; } public int getBulletNum() { return bulletNum; } public void setBulletNum(int bulletNum) { this.bulletNum = bulletNum; } //再给个速度 private int tank_speed = 1; public int getTank_speed() { return tank_speed; } public void setTank_speed(int tank_speed) { this.tank_speed = tank_speed; } //在父类中添加根据坦克方向创建子弹的方法 //子弹的初始坐标就是坦克炮筒的终点坐标 public Bullet makeBullet (){ switch (getDirect()) { case 0 : //上 bullet = new Bullet(getX() + 20, getY(), 0); break; case 1 : //右 bullet = new Bullet(getX() + 60, getY() + 20, 1); break; case 2 : //下 bullet = new Bullet(getX() + 20, getY() + 60, 2); break; case 3 : //左 bullet = new Bullet(getX(), getY() + 20, 3); break; } return bullet; } //上右下左移动方法 public void moveUp() { y -= tank_speed; } public void moveRight() { x += tank_speed; }public void moveDown() { y += tank_speed; }public void moveLeft() { x -= tank_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; } }
@SuppressWarnings("all") //把子弹做成线程 class Bullet implements Runnable { //给子弹设置它自身的坐标 //并根据坦克的方向进行初始化 private int x;//子弹横坐标 private int y;//子弹纵坐标 private int direct = 0; //子弹方向 private int speed = 5; //子弹速度 private boolean isBulletAlive = true; //子弹是否还存活 public int getX() { return x; } public int getY() { return y; } public int getDirect() { return direct; } public int getSpeed() { return speed; } public void setBulletAlive(boolean live) { isBulletAlive = live; } public boolean getBulletAlive() { return isBulletAlive; } public Bullet(int x, int y, int direct) { this.x = x; this.y = y; this.direct = direct; } @Override public void run() //射击 { while (true) { //休眠->以便看到效果 try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } //根据方向改变x, y坐标 //代码简单就不用封装为方法了 switch (direct) { case 0 : //上 y -= speed; break; case 1 : //右 x += speed; break; case 2 : //下 y += speed; break; case 3 : //左 x -= speed; break; } //测试, 输出x,y 坐标 //System.out.println("子弹 x= " + x + " y= " + y); //如果 //1.子弹移动到面板边界,则销毁(即退出循环->结束线程) //2.子弹击中敌方坦克, 也销毁 //注意条件的判断方式->先列出正确的条件 再取反 if (!(x >= 0 && x <= 1000 && y >= 0 && y <= 750 && isBulletAlive == true)) { isBulletAlive = false; System.out.println("子弹已销毁.."); break; } } } }
public class Bomb { int x, y;//炸弹坐标 int life = 18; //炸弹生命周期 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; } } }
@SuppressWarnings("all") //自己的坦克 public class Hero extends Tank { //定义一个Vector, 用来存放我方子弹 Vector<Bullet> bullet_hero = new Vector<Bullet>(); public Hero(int x, int y) { super(x, y); } //把创建一个Bullet对象和启动Bullet线程 封装到shotEnemy方法 public void shotEnemy() { //如果我方坦克发射的子弹大于5颗, 就不再创建新的子弹了 setBulletNum(5); if (bullet_hero.size() > getBulletNum()) { return; } //根据当前Hero对象的位置和方向, 创建一个Bullet对象 //-->调用父类方法 bullet = makeBullet(); bullet_hero.add(bullet); //把我方子弹加入集合 new Thread(bullet).start();//启动Bullet线程 } }
Enemy类(敌方坦克,同样继承Tank类, 做成线程)
@SuppressWarnings("all") public class Enemy extends Tank implements Runnable { //定义一个Vector, 用来存放敌方子弹 private Vector<Bullet> bullet_enemy = new Vector<Bullet>(); public Vector<Bullet> getBullet_enemy() { return bullet_enemy; } public Enemy(int x, int y) { super(x, y); } @Override public void run() { while (isTankAlive()) { setBulletNum(2);//如果敌方坦克的子弹为2颗 if(bullet_enemy.size() <= getBulletNum()){ //判断敌方坦克的方向来创建子弹对象-->调用父类方法 bullet = makeBullet(); bullet_enemy.add(bullet); //把敌方子弹加入集合 new Thread(bullet).start();//启动该Bullet线程 } //生成一个1~3的随机整数(即不往上打),表示敌方坦克的方向 int direct_random = (int) (1 + Math.random() * 3); System.out.println("随机方向:" + direct_random); System.out.println("敌方坐标(" + getX() + "," + getY() + ")"); setDirect(direct_random); System.out.println("实际方向: " + getDirect()); //设置敌方坦克的随机速度 int speed_random = (int)(Math.random() * 3 + 1); setTank_speed(speed_random); switch (getDirect()) { case 0 : //上 //让坦克保持一个方向走30步 for (int i = 0; i < 30; i++) { if(getY() > 0) { //控制敌方坦克移动范围 moveUp(); } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 1 : //右 for (int i = 0; i < 30; i++) { if(getX() + 60 < 1000) { //控制敌方坦克移动范围 moveRight(); } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 2 : //下 for (int i = 0; i < 30; i++) { if(getY() + 60 < 750) { //控制敌方坦克移动范围 moveDown(); } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 3 : //左 for (int i = 0; i < 30; i++) { if(getX() > 0) { //控制敌方坦克移动范围 moveLeft(); } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; } } } }
@SuppressWarnings("all") //坦克大战的绘图区域 //1.为了监听键盘事件,实现KeyListener //2.为了看到子弹前进的效果->MyPanel要一直repaint->把MyPanel也做成一个线程 public class MyPanel extends JPanel implements KeyListener,Runnable { //定义我的坦克 Hero hero = null; //定义敌方坦克,放入到vector集合 Vector<Enemy> enemyTanks = new Vector<Enemy>(); int enemyTankNum = 3; //定义一个Vector, 用于存放炸弹 //当敌人击中坦克时, 就加入一个Bomb对象到bombs Vector<Bomb> bombs = new Vector<Bomb>(); //定义三张炸弹图片, 用于`显示爆炸效果 Image image1 = null; Image image2 = null; Image image3 = null; public MyPanel() { //初始化自己的坦克 hero = new Hero(100, 200); hero.setTank_speed(10);//设置我方坦克速度 //初始化敌方的坦克 for(int i = 0; i < enemyTankNum; i++) { Enemy enemy = new Enemy(200 * (i + 1), 0); enemy.setDirect(2);//设置敌方坦克方向朝下 //在此处创建敌方坦克的一颗子弹, 并加入到bullet_enemy 集合 Bullet b = new Bullet(enemy.getX() + 20, enemy.getY() + 60, enemy.getDirect()); enemy.getBullet_enemy().add(b);//加入到bullet_enemy 集合 new Thread(b).start(); //启动敌方坦克子弹 enemyTanks.add(enemy); new Thread(enemy).start(); //启动敌方坦克 } //初始化图片对象 image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif")); image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif")); image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif")); } @Override public void paint(Graphics g) { super.paint(g); //填充矩形,默认是黑色 g.fillRect(0, 0, 1000, 750); //画出我方坦克-->封装到方法 if(hero.isTankAlive()) { drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1); } //遍历我方子弹集合, 画出我方坦克发射的子弹 for(int i = 0; i < hero.bullet_hero.size(); i++){ Bullet bullet = hero.bullet_hero.get(i); if (bullet != null && bullet.getBulletAlive() == true) { g.setColor(Color.yellow); g.fillOval(bullet.getX(), bullet.getY(), 5, 5); } else { //当我方子弹不存活时就将它移除 hero.bullet_hero.remove(bullet); } } //如果bombs集合中有对象, 就画出 for(int i = 0; i < bombs.size(); i++) { Bomb bomb = bombs.get(i); //取出炸弹对象 //根据当前bomb的life值去画出对应的图片 if (bomb.life > 12) { g.drawImage(image1, bomb.x, bomb.y, 60, 60, this); } else if (bomb.life > 6) { g.drawImage(image2, bomb.x, bomb.y, 60, 60, this); } else{ g.drawImage(image3, bomb.x, bomb.y, 60, 60, this); } //让炸弹的生命值减少 bomb.lifeDown(); //配合出现动态效果 //如果life为0, 就从bombs集合中删除 if (bomb.life == 0) { bombs.remove(bomb); } } //画出敌方坦克, 遍历Vecror for(int i = 0; i < enemyTanks.size(); i++) { //取出坦克 Enemy enemy = enemyTanks.get(i); //调用drawTank方法 //如果当前敌方坦克还存活,才去画 if (enemy.isTankAlive() == true) { drawTank(enemy.getX(), enemy.getY(), g, enemy.getDirect(), 0); //画出敌方坦克的子弹 for (int j = 0; j < enemy.getBullet_enemy().size(); j++) { Bullet b = enemy.getBullet_enemy().get(j);//取出敌方子弹 if (b.getBulletAlive() == true) { g.setColor(Color.cyan); g.fillOval(b.getX(), b.getY(), 5, 5); } else { enemy.getBullet_enemy().remove(b);//移除敌方子弹 } } } else{ //当敌方坦克不存活了, 就移除它 enemyTanks.remove(enemy); } } } //画出坦克-->封装到方法 /** * * @param x 坦克的左上角横坐标 * @param 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("其他情况暂时没有..."); } } @Override public void keyTyped(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { //处理wdsa键按下的情况 if(e.getKeyCode() == KeyEvent.VK_W)//按下w键 { //向上 hero.setDirect(0); if(hero.getY() > 0) { //控制我方坦克移动范围 hero.moveUp(); } } else if(e.getKeyCode() == KeyEvent.VK_D)//按下d键 { //向右 hero.setDirect(1); if(hero.getX() + 60 < 1000) {//控制我方坦克移动范围 hero.moveRight(); } } else if(e.getKeyCode() == KeyEvent.VK_S)//按下s键 { //向下 hero.setDirect(2); if(hero.getY() + 60 < 750) {//控制我方坦克移动范围 hero.moveDown(); } } else if(e.getKeyCode() == KeyEvent.VK_A)//按下a键 { //向左 hero.setDirect(3); if(hero.getX() > 0) { //控制我方坦克移动范围 hero.moveLeft(); } } //如果用户按下J键, 发射子弹 if(e.getKeyCode() == KeyEvent.VK_J) { System.out.println("用户按下J键, 发射子弹..."); hero.shotEnemy(); } this.repaint();//面板重绘 } @Override public void keyReleased(KeyEvent e) { } //敌方坦克被击中 //什么时候判断坦克是否被击中呢?? -->面板重绘时 //alt + shift + R -->批量更改变量名 public void hitted(Bullet b, Tank t) { switch (t.getDirect()) { case 0: //上 case 2: //下 if (b.getX() >= t.getX() && b.getX() <= t.getX() + 40 && b.getY() >= t.getY() && b.getY() <= t.getY() +60) { b.setBulletAlive(false); t.setTankAlive(false); //创建一个Bomb对象, 加入到bombs集合中 Bomb bomb = new Bomb(t.getX(), t.getY()); bombs.add(bomb); } break; case 1: //右 if (b.getX() >= t.getX() && b.getX() <= t.getX() + 60 && b.getY() >= t.getY() && b.getY() <= t.getY() + 40) { b.setBulletAlive(false); t.setTankAlive(false); //创建一个Bomb对象, 加入到bombs集合中 Bomb bomb = new Bomb(t.getX(), t.getY()); bombs.add(bomb); } case 3: //左 break; } } //判断敌方坦克是否被击中 public void hitEnemy(){ //如果我方发射的子弹还存活->不为空且没有碰到边界 //注意: 这里如果用 逻辑与-> & 就会报空指针异常 //遍历我方子弹集合 bullet_hero, //并调用hitted方法,判断敌方坦克是否被击中 for (int j = 0; j < hero.bullet_hero.size(); j++) { Bullet bullet = hero.bullet_hero.get(j); if (bullet != null && bullet.getBulletAlive()) { //遍历敌方坦克 for (int i = 0; i < enemyTanks.size(); i++) { Enemy e = enemyTanks.get(i); //调用hitted方法,判断坦克是否被击中 hitted(bullet, e); } } } } //判断我方坦克是否被击中 public void hitHero(){ //遍历敌方子弹集合 bullet_enemy, //并调用hitted方法,判断我方坦克是否被击中 for (int i = 0; i < enemyTanks.size(); i++) { Enemy enemy = enemyTanks.get(i); for(int j = 0; j < enemy.getBullet_enemy().size(); j++){ Bullet bullet = enemy.getBullet_enemy().get(j); if (bullet != null && bullet.getBulletAlive()) { hitted(bullet, hero); } } } } //使MyPanel不断重绘->这样才能显示子弹的行进 @Override public void run() { while (true) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //判断坦克是否被击中 hitEnemy(); hitHero(); this.repaint(); } } }
public class TankGame04 extends JFrame { //定义MyPanel MyPanel mp = null; public static void main(String[] args) { TankGame04 tankGame01 = new TankGame04(); } //构造器 public TankGame04() { mp = new MyPanel(); this.add(mp); this.setSize(1050, 900); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); this.addKeyListener(mp);//监听面板发生的键盘事件 //启功MyPanel mp 线程 Thread thread_mp = new Thread(mp); thread_mp.start(); } }