Use collections, Swings, threads and other knowledge points in the Java language to write a tank war game.
(1) Draw the principle of enemy and us tanks:
There is a Boolean type variable good inside the tank class. Used to judge the camp of a tank. When creating a tank object, the value of good is passed in the Tank class construction method. When drawing tanks, judge the value of good and distinguish the color of enemy and us;
(2) Principle of tank movement:
The response event of the monitor keyboard key press is written in the tank class, the monitored up, down, left and right keys are recorded, and variables in eight directions of the tank movement are synthesized. Then, the corresponding changes to the values of the tank coordinates x and y in each direction can be made to achieve the movement of our tank. The enemy tanks automatically move, randomly circulating the direction of the enemy tank's movement through a random number, and randomly output the number of times each movement. The combination of two random values enables the movement of enemy tanks.
(3) Principle of tank launching bullets:
Through keyboard monitoring, after detecting the bullet-fire command, add a bullet class to the main class's bullet class collection. Transfer the direction of the gun barrel, the position of the tank, and the camp of the tank to the bullets. In the main paint drawing method, the bullet collection will be cycled. If there are bullets in the set, draw them out. This enables the launch of bullets.
(4) The collision principle of tanks, bullets and walls:
In the tank-type bullet-type wall class, getRect method to obtain its own range, and then perform corresponding collision detection every time the tank and bullet is drawn (there are methods for collision with the wall and the tank outside the tank in the tank, and in the bullet, there are methods for collision with the wall and the tank.). If the range of oneself and the object that should not be collided, it means that the two objects collide.
(5) The principle of tank blood circulation:
If the blood clots collide with our tank, the blood clots will die, and the blood clots will be fully recovered.
(6) The principle of tank resurrection:
After detecting the resurrection order of our tank by keyboard monitoring, if our tank is in a dead state, our tank inventory status will be changed to alive and the blood volume of our tank will be restored to full health.
Programming ideas:
The programming idea of Tank War opens a thread in the main class, and does not cycle at 50 milliseconds to draw the method (draw everything in the entire interface). The things drawn include enemy tanks (color distinction), bullets, walls, blood clots, and explosions. So in total, several categories were written: Tank tanks, Missile bullets, Wall walls, Blood clots, and TankClient main classes. In each class, there is a drawing method written to realize the drawing function of the attributes of this class. There is a keyboard listening event in the main class called the keyboard listening event of this Tank class. Through keyboard monitoring, it is determined that the corresponding movement of Tank is made, while the enemy Tank is randomly moving. And every time the refresh is refreshed, various collision methods are called to determine the situation of some objects that should not be collided with. The creation of each object, such as bullets, is to add a new bullet class to a bullet class collection after it is triggered. When drawing, determine the number in the set to draw. If the tank is out of bounds or killing the tank, it will be deleted in the set. Other categories are also similar, so I won't go into details.
Each step in the code has a corresponding explanation.
TankClient.java
import java.awt.Color;import java.awt.Font;import java.awt.Graphics;import java.awt.Image;import java.awt.event.KeyAdapter;import java.awt.event.KeyEvent;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import java.util.ArrayList;import java.util.List;import javax.swing.JFrame;public class TankClient extends JFrame{ /** * @param args */ Image OffScrennImage = null; //Double buffer memory image storage/*Game size*/ public static final int GAME_WIDTH = 800; //Width interface public static final int GAME_HEIGTH = 600; //High interface Tank myTank = new Tank(500,400,true,Color.red,Tank.Direction.STOP, this); //Our tank class List<Missile> missiles = new ArrayList<Missile>();//Collection of bullets List<Explode> exploit = new ArrayList<Explode>();//Explosion set List<Tank> tanks = new ArrayList<Tank>(); //Tank set Wall wall1 = new Wall(150,200,20,300,this); //Wall 1 Wall wall2 = new Wall(250,500,300,20,this); //Wall 2 Wall wall3 = new Wall(650,200,20,300,this); //Wall 2 Wall wall4 = new Wall(250,300,300,20,this); //Wall 2 Wall wb = new Wall(750,550,40,40,this); //Wall2 Blood b = new Blood(); //blood class public static void main(String[] args) { // TODO Auto-generated method stub TankClient tc=new TankClient(); tc.lauchFrame(); } private void lauchFrame() { // TODO Auto-generated method stub for (int i = 0; i < 10; i++){ tanks.add(new Tank(50+40*(i+1), 50, false,Color.blue,Tank.Direction.D, this)); } this.setLocation(100, 100); //Window initial coordinate point this.setSize(GAME_WIDTH, GAME_HEIGTH); //Window initial size this.setTitle("TankWar"); //Window name/*Window listening*/ this.addWindowListener(new WindowAdapter() { @Override /*Run after point exits the fork*/ public void windowClosing(WindowEvent e) { // TODO Auto-generated method stub System.exit(0); //Exit} }); this.addKeyListener(new KeyMoniton()); //Set the keyboard to listen this.setVisible(true); //Set the window to display this.setResizable(false); //Set the window to not change the size this.getContentPane().setBackground(Color.green); //Set the window foreground color to green new Thread(new PaintThread()).start(); //Start the PaintThread class run } @Override public void paint(Graphics g) { // TODO Auto-generated method stub //Graphics is the brush class super.paint(g); myTank.draw(g); wall1.draw(g); wall2.draw(g); wall3.draw(g); wall4.draw(g); wb.draw(g); b.draw(g); myTank.eatBlood(b); myTank.hitWall(wall1); myTank.hitWall(wall2); myTank.hitWall(wall3); myTank.hitWall(wall4); /*Cycle bullet collection*/ for (int i = 0; i < missiles.size(); i++){ Missile m = missiles.get(i); //Get the current bullet m.hitTanks(tanks); //Your own bullet kills the enemy tank m.hitWall(wall1); //Bullet and wall m.hitWall(wall2); m.hitWall(wall3); m.hitWall(wall4); m.hitTank(myTank); //Enemy bullet hits its own tank m.draw(g); //Draw bullet} for (int i = 0; i < explore.size(); i++){ exploit.get(i).draw(g); //Draw explosion} for (int i = 0; i < tanks.size(); i++){ Tank t = tanks.get(i); t.draw(g); //Draw enemy tanks t.hitTanks(tanks); t.hitWall(wall1); //Tanks and walls t.hitWall(wall2); t.hitWall(wall3); t.hitWall(wall4); } //g.setFont(new Font("宋体", Font.BOLD,20)); g.drawString("missiles count:"+missiles.size(), 10, 50); //Show g.drawString("explode count:"+explode.size(), 10, 80); //Show g.drawString("tanks count:"+tanks.size(), 10, 110); g.drawString("myTank Life:"+myTank.getLife(), 10, 130); g.drawString("Blood recovery:", 750, 540); g.drawString("Directory key movement direction; E: Release fast movement blood", 10, 590); g.drawString("z: Launch Dongfeng-31; a: Launch Dongfeng-41;", 10, 570); g.drawString("F2: Resurrection; F3: Resurrection of the enemy (to the most 20)", 10, 550); g.drawString("R: Position restore; Q: Full of blood", 10, 530); } @Override /*repaint->update->paint*/ public void update(Graphics g) { // TODO Auto-generated method stub super.update(g); if(OffScrennImage == null) OffScrennImage = this.createImage(GAME_WIDTH, GAME_HEIGTH); Graphics goffscrenn = OffScrennImage.getGraphics(); //Set a memory brush color to the foreground image color Color c = goffscrenn.getColor(); //Save the foreground color first, goffscrenn.setColor(Color.green); //Set the color of the memory brush to green goffscrenn.fillRect(0, 0, GAME_WIDTH, GAME_HEIGTH); //Draw the image as the size of the game size goffscrenn.setColor(c); //Restore the color g.drawImage(OffScrennImage, 0, 0, null); //Draw the saved image on the interface paint(goffscrenn); //Call the memory brush to paint } private class PaintThread implements Runnable{ @Override public void run() { // TODO Auto-generated method stub while(true){ repaint(); //Run order repaint->update->paint try{ Thread.sleep(50); //Refresh the screen every 50 milliseconds}catch(Exception e){ e.printStackTrace(); } } } } /*Keyboard response*/ private class KeyMoniton extends KeyAdapter{ /*Press the keyboard response*/ @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub super.keyPressed(e); myTank.KeyPressed(e); } /*Rise the keyboard response*/ @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub super.keyReleased(e); myTank.keyReleased(e); } }}Tank.java
import java.awt.Color;import java.awt.Graphics;import java.awt.Image;import java.awt.Rectangle;import java.awt.event.KeyEvent;import java.util.List;import java.util.Random;import javax.swing.ImageIcon;public class Tank { /*Tank itself data*/ int x, y;//Tank coordinates private int oldX, oldY; //Tank previous coordinates public static final int Whith = 30; //Tank width public static final int Higth = 30; //Tank high public static final int XSPEED = 5; //Length directional movement speed public static final int YSPEED = 5; //Longarogue movement speed private Color color; //Tank color private boolean bL=false, bU=false, bR=false, bD=false; //Four direction control values enum Direction {L, LU, U, RU, R, RD, D, LD, STOP}; //The movement of eight directions is composed of four direction values private Direction dir = Direction.STOP; //Output direction private Direction ptDir = Direction.D; //Original direction of the cannon private boolean good; //Judge the tank's camp private boolean live = true; //Judge whether the tank is alive private static Random r = new Random();//Set a random value variable private static int step = r.nextInt(12)+3; //Enemy tank random movement steps 3-14 private int Life = 100; //Health volume private BloodBar bb = new BloodBar(); //Cleck// ImageIcon icon = new ImageIcon("res//myTank.jpg");// ImageIcon icon2 = new ImageIcon("res//enemyTank.jpg");// Image image = icon.getImage();// Image image2 = icon2.getImage(); private TankClient tc; //Main class permissions public Tank(int x, int y, boolean good, Color color) { super(); this.x = x; this.y = y; this.color = color; this.good = good; } public Tank(int x, int y, boolean good,Color color,Direction dir,TankClient tc){ this(x,y,good,color); this.dir = dir; this.tc = tc; } /*Get tank health*/ public int getLife() { return Life; } /*Set tank health*/ public void setLife(int Life) { this.Life = Life; } /*Get tank camp*/ public boolean isGood() { return good; } /*Set tank camp*/ public void setGood(boolean good) { this.good = good; } /*Get tank survival status*/ public boolean isLive() { return live; } /*Set the tank's survival status*/ public void setLive(boolean live) { this.live = live; } /*Draw tank*/ public void draw(Graphics g){ if(!live){ if(!good){ tc.tanks.remove(this); //Delete in the set when the enemy tank dies//tc.tanks.add(new Tank(r.nextInt(700),r.nextInt(500),false,Color.blue,Direction.D,this.tc)); } return; } /*Save the previous brush color first, and restore the brush color after drawing*/ Color c = g.getColor(); //Get the current brush color g.setColor(color); //Set the brush color to red/*Draw tank*/ g.fillOval(x, y, Whith, High); /*Two methods to draw enemy and us tanks, use the previously added pictures or colors to distinguish*/// if(good)// g.drawImage(image, x, y, Whith, High, null); // else// g.drawImage(image2, x, y, Whith, High, null); if(good) bb.draw(g); // Our tank draws blood bar g.setColor(Color.black); /*Draw the gun barrel through the direction of the gun barrel*/ switch(ptDir){ case L: g.drawLine(x+Tank.Whith/2, y+Tank.Higth/2, x, y+Tank.Higth/2); break; case LU: g.drawLine(x+Tank.Whith/2, y+Tank.Higth/2, x, y); break; case RU: g.drawLine(x+Tank.Whith/2, y+Tank.Higth/2, x+Tank.Whith/2, y); break; case R: g.drawLine(x+Tank.Whith/2, y+Tank.Higth/2, x+Tank.Whith, y+Tank.Higth/2); break; case RD: g.drawLine(x+Tank.Whith/2, y+Tank.Higth/2, x+Tank.Whith, y+Tank.Higth); break; case D: g.drawLine(x+Tank.Whith/2, y+Tank.Higth/2, x, y+Tank.Higth); break; } g.setColor(c); //Restore the brush color move();//Move} /*Keyboard monitoring; key*/ public void KeyPressed(KeyEvent e){ int key = e.getKeyCode(); //Save the keys listened to by the keyboard as integers/*Keyboard mobile tank*/ switch(key){ /*Move key*/ case KeyEvent.VK_UP: bU=true; break; case KeyEvent.VK_DOWN: bD=true; break; case KeyEvent.VK_RIGHT: bR=true; break; case KeyEvent.VK_LEFT: bL=true; break; } locateDirection(); } /*Keyboard monitoring; lift the key*/ public void keyReleased(KeyEvent e){ int key = e.getKeyCode(); //Save the keys listened to by the keyboard as integers/*Keyboard moving tank*/ switch(key){ case KeyEvent.VK_UP: bU=false; break; case KeyEvent.VK_DOWN: bD=false; break; case KeyEvent.VK_RIGHT: bR=false; break; case KeyEvent.VK_LEFT: bL=false; break; case KeyEvent.VK_Z: //Single bullet if(live) fire(); break; case KeyEvent.VK_F2: //Our resurrection if(!this.live){ this.live=true; this.setLife(100); } break; case KeyEvent.VK_F3: //Enemesis resurrection fuhuo(); break; case KeyEvent.VK_A: //Invincible missile superFire(); break; case KeyEvent.VK_Q: //Recover if(this.live) this.Life = 100; break; case KeyEvent.VK_E: //Release blood clot tc.b.fh(); break; /*Restore position key*/ case KeyEvent.VK_R: x = 50; y = 50; break; } locateDirection(); //Composition direction} /*Composition movement direction*/ void locateDirection(){ if(bL&&!bU&&!bR&&!bD) dir=Direction.L; else if(bL&&bU&&!bR&&!bD) dir=Direction.LU; else if(!bL&&bU&&!bD) dir=Direction.U; else if(!bL&&bU&&bR&&!bD) dir=Direction.RU; else if(!bL&&!bU&&bR&&!bD) dir=Direction.R; else if(!bL&&!bU&&bR&&bD) dir=Direction.RD; else if(!bL&&!bU&&!bR&&bD) dir=Direction.D; else if(!bL&&!bU&&!bR&&bD) dir=Direction.LD; else if(!bL&&!bU&&!bD) dir=Direction.STOP; } void move(){ //Move/*Record the position of the previous step*/ oldX = x; oldY = y; switch(dir){ case L: x-=XSPEED; break; case LU: x-=XSPEED; y-=YSPEED; break; case U: y-=YSPEED; break; case RU: x+=XSPEED; y-=YSPEED; break; case R: x+=XSPEED; break; case RD: x+=XSPEED; y+=YSPEED; break; case D: y+=YSPEED; break; case LD: x-=XSPEED; y+=YSPEED; break; case LD: x-=XSPEED; y+=YSPEED; break; case STOP: break; } /*Judge tank movement out of bounds (game boundary)*/ if(x < 5) x = 5; if(y < 25) y = 25; if(x+Whith > tc.GAME_WIDTH-5) x = tc.GAME_WIDTH-Whith-5; if(y+Higth > tc.GAME_HEIGTH-5) y = tc.GAME_HEIGTH-Higth-5; if(dir != Direction.STOP) //If the tank is not stationary, change the direction of the barrel ptDir = dir; /*Enemy tank automatically moves*/ if(!good){ Direction[] dirs = Direction.values(); //Set the direction variable to an array if(step == 0){ step = r.nextInt(12)+3; //Random movement step int randomNumber = r.nextInt(dirs.length); //Random movement direction dir = dirs[randomNumber]; } step--; if(r.nextInt(40)>30) this.fire(); //Does the shells be fired randomly} } /*Enemy tank resurrection*/ public void fuhuo(){ if(tc.tanks.size() < 20) while(true){ int x = r.nextInt(700); int y = r.nextInt(500); Tank t = new Tank(x,y,false,Color.blue,Direction.D,tc); /*If the tank coincides with the wall, re-random position until it does not overlap the new tank is added to the set*/ if(t.getRect().intersects(tc.wall1.getRect())||t.getRect().intersects(tc.wall2.getRect()) ||t.getRect().intersects(tc.wall3.getRect()) ||t.getRect().intersects(tc.wall4.getRect())){ continue; } else{ tc.tanks.add(t); break; } } } /*Bullet launch*/ public void fire(){ int x = this.x + Whith/2 - Missile.Whith/2; //Control the bullet direction to the middle of the tank int y = this.y + Higth/2 - Missile.Higth/2; tc.missiles.add(new Missile(ptDir,color,x,y,good,tc)); //Create a new bullet class to join the bullet collection} /*Collision; get the range of the tank*/ public Rectangle getRect(){ return new Rectangle(x,y,Whith,Higth); } /*The previous step position of the receipt*/ private void stay(){ x = oldX; y = oldY; } /*If you hit the wall, call the stay method and return to the previous step position*/ public boolean hitWall(Wall w){ if(this.live&&this.getRect().intersects(w.getRect())){ this.stay(); return true; } return false; } /*Tank collision event*/ public boolean hitTanks(List<Tank> tanks){ for(int i=0;i<tanks.size();i++){ Tank t=tanks.get(i); if(this!=t){//You cannot collide with yourself/*If you collide, return to the previous position*/ if(this.live&&t.isLive()&&this.getRect().intersects(t.getRect())){ this.stay(); t.stay(); return true; } } return false; } /*Emission function with fire direction*/ public Missile fire(Direction dir){ if(!live) return null; int x=this.x+Whith/2-Missile.Whith/2; int y=this.y+Higth/2-Missile.Higth/2; Missile m=new Missile(dir,color,x, y,good, this.tc); tc.missiles.add(m); return m; } /*Superfire missile*/ private void superFire(){ Direction[] dirs=Direction.values(); for(int i=0;i<8;i++){ fire(dirs[i]);//Cycling eight directions} } /*Added blood clot class*/ private class BloodBar{ /*Draw blood bar*/ public void draw(Graphics g){ Color c=g.getColor(); g.setColor(Color.red); g.drawRect(x, y-10, Whith, 10); int w=Whith*Life/100; g.fillRect(x, y-10, w, 10); g.setColor(c); } } /*blood-eating method*/ public boolean eatBlood(Blood b){ if(this.live&&b.isLive()&&this.isGood()&&this.getRect().intersects(b.getRect())){ this.setLife(100); b.setLive(false); return true; } if(this.getRect().intersects(tc.wb.getRect()))) this.Life = 100; return false; }}Missile.java
import java.awt.Color;import java.awt.Graphics;import java.awt.Rectangle;import java.util.List;public class Missile { /*Bullet data*/ Tank.Direction dir; //Bullet direction Color c; //Bullet color int x,y; //Bullet position public static final int XSPEED = 15; //Length movement speed public static final int YSPEED = 15; //Length movement speed public static final int Whith = 10; //Bullet width public static final int Higth = 10; //Bullet high private boolean live = true; //Judge the survival of the bullet private boolean good; //Judge the bullet and camp private TankClient tc;//Main class permissions public Missile(Tank.Direction dir,Color c, int x, int y) { super(); this.dir = dir; this.x = x; this.y = y; this.c = c; } public Missile(Tank.Direction dir,Color c, int x, int y,boolean good,TankClient tc){ this(dir,c,x,y); this.good = good; this.tc = tc; } /*Get the survival of the bullet*/ public boolean isLive() { return live; } /*Set the survival of the bullet*/ public void setLive(boolean live) { this.live = live; } public void draw(Graphics g){ /*If the bullet dies, delete the bullet in the bullet collection*/ if(!live){ tc.missiles.remove(this); //Delete the return in the collection; } /*Save the previous brush color first, and restore the brush color after drawing*/ Color d = g.getColor(); //Get the current brush color g.setColor(c); //Set the brush color to red/*Print bullet*/ g.fillOval(x, y, Whith, High); g.setColor(d); //Restore the brush color move(); //Move} public void move(){ /*Judge the movement direction and the position of the tank*/ switch(dir){ case L: x-=XSPEED; break; case LU: x-=XSPEED; y-=YSPEED; break; case U: y-=YSPEED; break; case RU: x+=XSPEED; y-=YSPEED; break; case R: x+=XSPEED; break; case RD: x+=XSPEED; y+=YSPEED; break; case D: y+=YSPEED; break; case LD: x-=XSPEED; y+=YSPEED; break; case STOP: break; } /*Judge the outbound situation of the bullet; if outbound, the bullet will die, delete it in the bullet collection*/ if(x<0||y<0||x>TankClient.GAME_WIDTH||y>TankClient.GAME_HEIGTH) live = false; } /*Collision; get the range of the bullet*/ public Rectangle getRect(){ return new Rectangle(x,y,Whith,Higth); } /*Collision process between bullets and tanks*/ public boolean hitTank(Tank t){ /*If the bullet and the tank are in the same range, the bullet and the tank will die at the same time; and the bullet can only kill the opponent's tank*/ if(this.live&&this.getRect().intersects(t.getRect())&&t.isLive()&&this.good!=t.isGood()){ if(t.isGood()){ //Good tank/*Our tank will reduce health points when it hits the bullet, and dies when it hits 0*/ t.setLife(t.getLife()-20); if(t.getLife()<=0) t.setLive(false); }else{ //Bad tank t.setLive(false);//Death} this.live=false;//Bullet death tc.explode.add(new Explode(x, y, tc));//New explosion add set return true; } return false; } /*Collapse tank sets to judge bullet collision*/ public boolean hitTanks(List<Tank> tanks){ for (int i = 0; i < tanks.size(); i++){ if(hitTank(tanks.get(i))) return true; } return false; } /*The collision process between bullets and wall*/ public boolean hitWall(Wall w){ /*If the range of bullets and wall overlaps with the bullet death*/ if(this.live&&this.getRect().intersects(w.getRect())){ this.live=false; //Bullet death return true; } return false; }} Wall.java
import java.awt.Graphics;import java.awt.Rectangle;public class Wall { /*Wall data*/ int x,y,w,h; //Position and width and height private TankClient tc; //Main class permissions public Wall(int x, int y, int w, int h, TankClient tc) { super(); this.x = x; this.y = y; this.w = w; this.h = h; this.tc = tc; } /*Get the scope of the wall*/ public Rectangle getRect(){ return new Rectangle(x,y,w,h); } /*Draw wall*/ public void draw(Graphics g){ g.fillRect(x, y, w, h); }}Explode.java
import java.awt.Color;import java.awt.Graphics;public class Explode { /*Tank explosion attribute*/ int x,y; //Explosion position private boolean live = true; //Is there an explosion int step = 0; //Explosion time control int [] diameter = new int[] {4, 7, 12, 18, 26, 32, 49, 56, 65, 77, 80, 50, 40, 30, 14, 6};//Explosion range private TankClient tc; //Main class permission public Explode(int x, int y, TankClient tc) { super(); this.x = x; this.y = y; this.tc = tc; } /*Draw explosion*/ public void draw(Graphics g){ if(!live) return; //If the explosion death state does not end/*If the explosion time ends, the explosion does not exist and delete it in the set*/ if(step == diameter.length){ live = false; //Explosion death step = 0; //Step time is 0 tc.explode.remove(this); //Delete return in the set; } /*Draw explosion*/ Color c = g.getColor(); g.setColor(Color.orange); g.fillOval(x, y, diameter[step], diameter[step]); g.setColor(c); step++; } }Blood.java
import java.awt.Color;import java.awt.Graphics;import java.awt.Rectangle;import java.util.Random;public class Blood { /* blood clot data*/ int x, y, w, h;// blood clot location and size private TankClient tc; // main class permission private boolean live=true;// blood clot survival private static Random r = new Random();// set a random value variable/* get the survival status of the blood clot*/ public boolean isLive() { return live; } /* set the survival status of the blood clot*/ public void setLive(boolean live) { this.live = live; } /*The initial value of the blood clot position is randomly a value*/ public Blood(){ x=r.nextInt(600)+100; y=r.nextInt(400)+100; w=h=15; } /*Draw blood clot*/ public void draw(Graphics g){ if(!live) return; Color c=g.getColor(); g.setColor(Color.magenta); g.fillRect(x, y, w, h); g.setColor(c); } /*Release blood clot*/ public void fh(){ if(!live){ x = r.nextInt(600)+100; y = r.nextInt(400)+100; live = true; } } /*Get the blood clot range*/ public Rectangle getRect(){ return new Rectangle(x, y, w, h); }}The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.