坦克單機版

BlizCp發表於2020-11-12

                                             單機版程式演示

                                                                                           1、能夠四處移動

                                                                                           2、能夠打擊敵人

                                                                                           3、敵人能夠移動

                                                                                           4、能夠模擬爆炸

                                                                                           5、能夠產生障礙

                                                                                           6、能夠增長生命

 

  遊戲測試類

import java.awt.Color;
import java.awt.Frame;
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 java.util.Map;

public class TankWar extends Frame{
	/**
	 * 1)設定視窗寬度、高度
	 * 2)設定子彈、爆炸、坦克陣列
	 * 3)new Tank\Wall\Blood\
	 */
	public static final int GAME_WIDTH = 800;
	public static final int GAME_HEIGHT = 600;
	
	List<Missile> missiles = new ArrayList();
	List<Explode> explodes = new ArrayList();
	List<Tank>    tanks    = new ArrayList();
	
	Tank myTank = new Tank(this,50,50,Direction.STOP,true);
	
	Wall w1 = new Wall(this, 100, 200, 20, 150), w2 = new Wall(this, 300, 100, 300, 20);
	
	Blood b = new Blood();
	
	public static void main(String[] args) {
		new TankWar().luanch();
	}
	
	/**
	 * 繪製視窗
	 * 
	 */
	public void luanch(){
		this.setBounds(600, 200, GAME_WIDTH, GAME_HEIGHT);
		this.setTitle("TankWar");
		this.setBackground(Color.GREEN);
		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		this.setResizable(false);
		this.addKeyListener(new KeyMonitor());
		setVisible(true);
		new Thread(new PaintThread()).start();
	}
	
	
	public void paint(Graphics g){
		/**
		 * 指明子彈-爆炸-坦克的數量以及坦克的生命值
		 * 當敵方坦克數量小於或等於0時,自動新增5輛坦克
		 */
		
		g.drawString("count  missile:" + missiles.size(), 10, 50);
		g.drawString("count explodes:" + explodes.size(), 10, 70);
		g.drawString("count    tanks:" + tanks.size(), 10, 90);
		g.drawString("tanks     life:" + myTank.getLife(), 10, 110);
		
		if(tanks.size() <= 0){
			for(int i=0;i<5;i++){
				tanks.add(new Tank(this, 50 + 40*(i+1), 50, Direction.D, false));
			}
		}
		/**繪製子彈
		 * 遍歷子彈陣列,Missile類得到每一個子彈
		 * 子彈撞擊敵方坦克、子彈撞擊我方坦克、子彈撞牆
		 */
		for(int i = 0;i<missiles.size();i++){
			Missile m = missiles.get(i);
			m.hitTanks(tanks);
			m.hitTank(myTank);
			m.hitWall(w1);
			m.hitWall(w2);
			m.draw(g);
		}
		/**繪製爆炸
		 * 遍歷爆炸陣列,Explode得到每一個爆炸
		 */
		for(int i = 0;i<explodes.size();i++){
			Explode e = explodes.get(i);
			e.draw(g);
		}
		/**
		 * 遍歷tanks陣列,繪製每個坦克撞牆的方法
		 */
		for(int i = 0;i<tanks.size();i++){
			Tank t = tanks.get(i);
			t.collidesWithWall(w1);
			t.collidesWithWall(w2);
			t.collidesWithTanks(tanks);
			t.draw(g);
		}
		myTank.draw(g);
		myTank.eat(b);
		w1.draw(g);
		w2.draw(g);
		b.draw(g);
	}
	
	//雙緩衝
	Image offScreenImage = null;
	@Override
	public void update(Graphics g) {
		if(offScreenImage == null){
			offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
		}
		Graphics gOffScreen = offScreenImage.getGraphics();
		Color c = gOffScreen.getColor();
		gOffScreen.setColor(Color.GREEN);
		gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
		gOffScreen.setColor(c);
		paint(gOffScreen);
		g.drawImage(offScreenImage, 0, 0, null);
	}
	
	private class PaintThread implements Runnable {

		public void run() {
			while(true) {
				repaint();
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	//鍵盤監聽
	private class KeyMonitor extends KeyAdapter{
		//按下
		@Override
		public void keyPressed(KeyEvent e) {
			myTank.keyPressed(e);
		}
		
		//抬起
		@Override
		public void keyReleased(KeyEvent e) {
			myTank.keyReleased(e);
		}
	}
}

 

坦克

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.Random;

/**
 * 坦克類
 * @author Administrator
 *
 */
public class Tank {
	
	TankWar tc;
	/**一、
	 * 1)設定坦克座標,寬、高以及移動的間距
	 * 2)設定坦克方向位置初始化,以及炮筒方向
	 * 3)設定方向bU\bD\bL\bR為false,
	 * 4)live(存在)即true,!live(消失)即為false
	 * 5)good(我方)true、!good(敵方)false
	 * 6)設定oldX、oldY座標以便為了記錄坦克上一次移動的位置
	 * 7)設定坦克生命值:100
	 * 8)設定隨機步數,最小值:3、最大值:14
	 * 9)new 血條BloodBar
	 */
	private int x,y;
	public static final int WIDTH = 30;
	public static final int HEIGHT = 30;
	
	public static final int XSPEED = 5;
	public static final int YSPEED = 5;
	
	private Direction dir = null;
	private Direction ptDir = Direction.D; 
	
	private boolean bU = false, bD = false, bL = false, bR = false;
	private boolean live = true;
	private boolean good;

	private int oldX,oldY;
	
	private int life = 100;
	
	private Random r = new Random();
	private int step = r.nextInt(12) + 3;
	
	private BloodBar bb = new BloodBar();
	
	/**二、
	 * 有參構造
	 */
	public Tank(TankWar tc, int x, int y, Direction dir,  boolean good) {
		this.tc = tc;
		this.x = x;
		this.y = y;
		this.dir = dir;
		this.good = good;
		
		//記錄第一次的上一步
		this.oldX = x;
		this.oldY = y;
	}

	/**三、
	 * 1)敵方如果死亡就從坦克陣列中移除
	 * 2)繪製我方坦克顏色以及敵方坦克顏色以及座標、大小
	 * 3)繪製炮筒八個方向
	 * 4)我方坦克繪製血條
	 */
	public void draw(Graphics g){
		if(!live){
			if(! good){
				tc.tanks.remove(this);
			}
			return;
		}
		
		Color c = g.getColor();
		if(good) g.setColor(Color.RED);
		else g.setColor(Color.BLUE);
		g.fillOval(x, y, WIDTH, HEIGHT);
		g.setColor(c);
		
		if(good) bb.draw(g);
		
		switch(ptDir){
		case U:
			g.drawLine(x + WIDTH/2, y + HEIGHT/2,x + WIDTH/2, y);
			break;
		case D:
			g.drawLine(x + WIDTH/2, y + HEIGHT/2, x + WIDTH/2, y + HEIGHT);
			break;
		case L:
			g.drawLine(x + WIDTH/2, y + HEIGHT/2, x, y + HEIGHT/2);
			break;
		case R:
			g.drawLine(x + WIDTH/2, y + HEIGHT/2, x + WIDTH, y + HEIGHT/2);
			break;
		case UL:
			g.drawLine(x + WIDTH/2, y + HEIGHT/2, x, y);
			break;
		case UR:
			g.drawLine(x + WIDTH/2, y + HEIGHT/2, x + WIDTH, y);
			break;
		case DL:
			g.drawLine(x + WIDTH/2, y + HEIGHT/2, x, y + HEIGHT);
			break;
		case DR:
			g.drawLine(x + WIDTH/2, y + HEIGHT/2, x + WIDTH, y + HEIGHT);
			break;
		}
		
		move();
	}
	
	/**四、
	 * 1)oldX、oldY還沒有移動之前先記錄坦克xy座標位置
	 * 2)坦克分別向八個方向移動的距離
	 * 3)如果坦克不是靜止狀態,那麼炮筒方向就和坦克方向一致
	 * 4)坦克出界:
	 * 		30是標題欄的高度
	 * 		如果坦克出界就重新定義x、y座標
	 */
	void move() {
		this.oldX = x;
		this.oldY = y;

		switch (dir) {
		case U:
			y -= YSPEED;
			break;
		case D:
			y += YSPEED;
			break;
		case L:
			x -= XSPEED;
			break;
		case R:
			x += XSPEED;
			break;
		case UL:
			x -= XSPEED;
			y -= YSPEED;
			break;
		case UR:
			x += XSPEED;
			y -= YSPEED;
			break;
		case DL:
			x -= XSPEED;
			y += YSPEED;
			break;
		case DR:
			x += XSPEED;
			y += YSPEED;
			break;
		}
		
		if (this.dir != Direction.STOP) {
			this.ptDir = this.dir;
		}
		
		if(x < 0) x = 0;
		if(y <30) y =30;
		if(x + Tank.WIDTH > TankWar.GAME_WIDTH) x = TankWar.GAME_WIDTH - Tank.WIDTH;
		if(y + Tank.HEIGHT > TankWar.GAME_HEIGHT) y = TankWar.GAME_HEIGHT - Tank.HEIGHT;
		
		
		/**五、
		 * 隨機指定一個敵方坦克方向,隨機移動的步數最小值3、最大值14
		 * 每移動一次step步數減1,step步數為零時重新產生一個隨機步數
		 * 每次產生隨機數大於38就發一次炮彈
		 */
		if(!good){
			Direction [] dirs = Direction.values();
			if(step == 0){
				step = r.nextInt(12)+3;
				int rn = r.nextInt(dirs.length);
				dir = dirs[rn];
			}
			step --;
			if(r.nextInt(40) >38)this.fire();
		}
	}
	
	private void stay(){
		x = oldX;
		y = oldY;
	}
	
	/**
	 * 坦克開火(發射子彈)
	 * 如果坦克不存在,直接返回為空
	 * 設定子彈座標位置:坦克寬度、高度分別除以2,得到坦克中心位置。
	 * 再減去子彈寬度、高度的一半即子彈位置座標
	 * fire()用於炮筒方向發射子彈,fire(Dirction dir)用於八個方向超級發射子彈
	 * @return
	 */
	public Missile fire() {
		if(!live) return null;
		int x = this.x + Tank.WIDTH/2 - Missile.WIDTH/2;
		int y = this.y + Tank.HEIGHT/2 - Missile.HEIGHT/2;
		Missile m = new Missile(tc, x, y, ptDir, good);
		tc.missiles.add(m);
		return m;
	}
	
	public Missile fire(Direction dir) {
		if(!live) return null;
		int x = this.x + Tank.WIDTH/2 - Missile.WIDTH/2;
		int y = this.y + Tank.HEIGHT/2 - Missile.HEIGHT/2;
		Missile m = new Missile(tc, x, y, dir, good);
		tc.missiles.add(m);
		return m;
	}

	/**
	 * 鍵盤按下、抬起監聽(方向)
	 * 1)設定F2,如果live消失即(false),馬上變為ture(存在),並且生命值為100
	 * 2)如果單單只是設定了keyPressed(按下)監聽,一旦按下某個方向鍵時就永遠為true,也就意味著會一直執行某個鍵的指令
	 * 3)keyReleased(抬起)存在修正了keyPressed遺留問題,按下為true\抬起為false
	 * 4)設定抬起鍵盤A,新增超級子彈發射方法
	 * @param e
	 */
	public void keyPressed(KeyEvent e) {
		int key = e.getKeyCode();
		switch(key){
		case KeyEvent.VK_F2:
			if(!this.live){
				this.live = true;
				this.life = 100;
			}
			break;
		case KeyEvent.VK_UP:
			bU = true;
			break;
		case KeyEvent.VK_DOWN:
			bD = true;
			break;
		case KeyEvent.VK_LEFT:
			bL = true;
			break;
		case KeyEvent.VK_RIGHT:
			bR = true;
			break;
		}
		locateDirection();
	}
	
	public void keyReleased(KeyEvent e) {
		int key = e.getKeyCode();
		switch(key){
		case KeyEvent.VK_CONTROL:
			fire();
			break;
		case KeyEvent.VK_UP:
			bU = false;
			break;
		case KeyEvent.VK_DOWN:
			bD = false;
			break;
		case KeyEvent.VK_LEFT:
			bL = false;
			break;
		case KeyEvent.VK_RIGHT:
			bR = false;
			break;
		case KeyEvent.VK_A:
			superFire();
			break;
		}
		locateDirection();
	}
	
	/**設定八個方向
	 * 當按下朝"上"的鍵時,沒有按下朝"下"、"左"、"右",方向就向上移動
	 */
	private void locateDirection(){
		     if(bU && !bD && !bL && !bR) dir = Direction.U;
		else if(!bU && bD && !bL && !bR) dir = Direction.D;
		else if(!bU && !bD && bL && !bR) dir = Direction.L;
		else if(!bU && !bD && !bL && bR) dir = Direction.R;
		else if(bU && !bD && bL && !bR) dir = Direction.UL;
		else if(bU && !bD && !bL && bR) dir = Direction.UR;
		else if(!bU && bD && bL && !bR) dir = Direction.DL;
		else if(!bU && bD && !bL && bR) dir = Direction.DR;
		else if(!bU && !bD && !bL && !bR) dir = Direction.STOP;
		
	}
	/**
	 * 分別向八個方向發射子彈
	 */
	private void superFire() {
		Direction[] dirs = Direction.values();
		for(int i=0; i<8; i++) {
			fire(dirs[i]);
		}
	}
	/**
	 * 如果坦克和牆相撞就回到上一步位置
	 * @param w
	 * @return
	 */
	public boolean collidesWithWall(Wall w){
		if(this.live && this.getRect().intersects(w.getRect())){
			this.stay();
			return true;
		}
		return false;
	}
	/**awt和util包中各有List,為了方便區分引數中寫明
	 * 我方和敵方坦克都存在的情況下,撞上返回true,否則false
	 * @param tanks
	 * @return
	 */
	public boolean collidesWithTanks(java.util.List<Tank> tanks){
		for(int i=0;i<tanks.size();i++){
			Tank t = tanks.get(i);
			if(this != t){
				if(this.live && t.isLive() && this.getRect().intersects(t.getRect())){
					this.stay();
					return true;
				}	
			}
		}
		return false;
	}
	/**
	 * 撞的方法
	 * @return
	 */
	public Rectangle getRect(){
		return new Rectangle(x,y,WIDTH,HEIGHT);
	}
	/**血條
	 * 繪製內框 + 外框
	 * @author Administrator
	 *
	 */
	private class BloodBar{
		public void draw(Graphics g){
			Color c = g.getColor();
			g.setColor(Color.RED);
			g.drawRect(x, y-10, WIDTH, 10);
			int w = WIDTH *life/100;
			g.fillRect(x, y-10, w, 10);
			g.setColor(c);
		}
	}
	/**
	 * 如果我方坦克和血塊都存在的情況下,並且相撞
	 * 我方坦克的生命值立即為100,血塊消失
	 * @param b
	 * @return
	 */
	public boolean eat(Blood b){
		if(this.live && b.isLive() &&this.getRect().intersects(b.getRect())){
			this.life = 100;
			b.setLive(false);
			return true;
		}
		return false;
	}
	
	public boolean isGood() {
		return good;
	}
	
	public boolean isLive() {
		return live;
	}

	public void setLive(boolean live) {
		this.live = live;
	}

	public int getLife() {
		return life;
	}

	public void setLife(int life) {
		this.life = life;
	}
}

 

子彈

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.net.URI;
import java.util.List;

/**
 * 子彈類
 * @author Administrator
 *
 */
public class Missile {
	
	TankWar tc;
	/**
	 * 定義子彈寬度、高度以及移動距離、方向
	 * good(我方)true、!good(敵方)false
	 * live(存在)即true、!live(消失)即為false
	 */
	public static final int WIDTH = 10;
	public static final int HEIGHT = 10;
	
	public static final int XSPEED = 10;
	public static final int YSPEED = 10;
	
	int x , y;
	Direction dir;
	
	private boolean good ;
	private boolean live = true;
	
	public Missile(TankWar tc, int x, int y, Direction dir, boolean good) {
		this.tc = tc;
		this.x = x;
		this.y = y;
		this.dir = dir;
		this.good = good;
	}
	
	/**
	 * 如果子彈死亡就從Missile陣列中移除
	 * 繪製子彈顏色形狀大小、以及顏色
	 * @param g
	 */
	public void draw(Graphics g){
		if(!live) {
			tc.missiles.remove(this);
			return;
		}
		Color c = g.getColor();
		g.setColor(Color.RED);
		g.fillOval(x, y, WIDTH, HEIGHT);
		g.setColor(c);
		
		move();
	}

	/**
	 * 分別設定子彈移動的八個方向
	 * 注意子彈類YSPEED、SPEED引數,子彈移動速度須比坦克快
	 * 子彈越界消亡處理
	 */
	private void move() {
		switch(dir){
		case U:
			y -= YSPEED;
			break;
		case D:
			y += YSPEED;
			break;
		case L:
			x -= XSPEED;
			break;
		case R:
			x += XSPEED;
			break;
		case UL:
			x -= XSPEED;
			y -= YSPEED;
			break;
		case UR:
			y -= YSPEED;
			x += XSPEED;
			break;
		case DL:
			x -= XSPEED;
			y += YSPEED;
			break;
		case DR:
			x += XSPEED;
			y += YSPEED;
			break;
		case STOP:
			break;
		}
		if(x < 0 || y < 0 || x > tc.GAME_WIDTH || y > tc.GAME_HEIGHT){
			live = false;
		}
	}
	
	/**
	 * 子彈和坦克碰撞
	 * hitTank(Tank t)
	 * 				1)如果子彈沒有消失  && 碰撞和坦克碰撞  && 我方子彈不打我方坦克  &&  我方坦克存活
	 * 					簡單說就是我方子彈不打我方坦克
	 * 				2)我方坦克被擊中一下減去20的生命值,直到生命值小於或等於零時,我方坦克消失(死亡)
	 * 				3)else反之敵方坦克被子彈擊中立即消失(死亡)
	 */
	public Rectangle getRect(){
		return new Rectangle(x, y, WIDTH, HEIGHT);
	}
	
	public boolean hitTank(Tank t){
		if(this.live && this.getRect().intersects(t.getRect()) && this.good != t.isGood() && t.isLive()){
			if(t.isGood()){
				t.setLife(t.getLife()-20);
				if(t.getLife() <= 0) t.setLive(false);
			}else {
				t.setLive(false);
			}
			this.live = false;
			Explode e = new Explode(tc, x, y);
			tc.explodes.add(e);
			return true;
			}
		return false;
	}
	
	public boolean hitTanks(List<Tank> tanks){
		for(int i = 0;i <tanks.size();i++){
			if(hitTank(tanks.get(i))){
				return true;
			}
		}
		return false;
	}
	
	public boolean hitWall(Wall w){
		if(this.live && this.getRect().intersects(w.getRect())){
			this.live = false;
			return true;//?
		}
		return false;
	}
}

 

爆炸

import java.awt.Color;
import java.awt.Graphics;

/**
 * 爆炸類
 * @author Administrator
 *
 */
public class Explode {
	TankWar tc;
	/**
	 * 1)定義座標,爆炸範圍大小即diameter,步數
	 * 2)live(存在)即true,!live(消失)即為false
	 * 3)有參構造
	 * 4)如果爆炸消失就從explodes移除
	 * 5)爆炸到最後一步,爆炸消失,step初始化為零
	 */
	int x,y;
	int [] diameter = {4,7,12,18,26,32,49,30,14,6};
	int step = 0;
	
	private boolean live = true;
	
	public Explode(TankWar tc, int x, int y) {
		this.tc = tc;
		this.x = x;
		this.y = y;
	}

	public void draw(Graphics g){
		if(!live){
			tc.explodes.remove(this);
			return;
		}
		
		if(step == diameter.length){
			live = false;
			step = 0;
			return;
		}
		
		Color c = g.getColor();
		g.setColor(Color.ORANGE);
		g.fillOval(x, y, diameter[step], diameter[step]);
		g.setColor(c);
		
		step++;
	}
}

 

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

/**
 * 牆類
 * @author Administrator
 *
 */
public class Wall {
	TankWar tc;
	int x,y,w,h;
	
	public Wall(TankWar tc, int x, int y, int w, int h) {
		this.tc = tc;
		this.x = x;
		this.y = y;
		this.w = w;
		this.h = h;
	}
	
	public void draw(Graphics g){
		Color c = g.getColor();
		g.setColor(Color.BLACK);
		g.fillRect(x, y, w, h);
		g.setColor(c);
	}
	
	public Rectangle getRect(){
		return new Rectangle(x, y, w, h);
	}
}

方向

public enum Direction {
	U,D,L,R,UL,UR,DL,DR,STOP
}

血塊

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

/**
 * 血塊類
 * @author Administrator
 *
 */
public class Blood {
	private int x,y,w,h;
	private int step;
	private boolean live = true;
	
	private int [][] pos = {
			 {350, 300}, {360, 300}, {375, 275}, {400, 200}, {360, 270}, {365, 290}, {340, 280}
	};
	
	public Blood() {
		x = pos[0][0];
		y = pos[0][1];
		w = h = 15;
	}
	
	public void draw(Graphics g){
		if(!live) return;
		
		Color c = g.getColor();
		g.setColor(Color.MAGENTA);
		g.fillOval(x, y, w, h);
		g.setColor(c);
		
		move();
	}
	
	/**
	 * 在pos二維陣列中設定點{x,y}座標,每移動一次step +1
	 * step一直加到最後,step初始化為0
	 */
	private void move() {
		step ++;
		if(step == pos.length){
			step = 0;
		}
		x = pos [step][0];
		y = pos [step][1];
	}
	
	public Rectangle getRect(){
		return new Rectangle(x,y,w,h);
	}

	public boolean isLive() {
		return live;
	}

	public void setLive(boolean live) {
		this.live = live;
	}
}

 

相關文章