java坦克大战爆炸效果_Java坦克大战 (五) 之产生敌方坦克和爆炸效果
在此小易將坦克大戰這個項目分為幾個版本,以此對J2SE的知識進行回顧和總結,希望這樣也能給剛學完J2SE的小伙伴們一點啟示!
(此版本基本上就可以和電腦對打了,不信把源碼放到你的電腦上就可以馬上開啟坦克大戰之旅哦)
坦克大戰V0.5實現功能:
1、產生一個敵方坦克
2、將敵人坦克擊斃,相應的子彈和坦克都消失
3、添加爆炸效果
4、添加多輛坦克,并能夠打掉敵方坦克
5、讓敵方坦克可以隨機運動,可以打出子彈打掉我方坦克
6、加兩堵墻出來,可以吞沒子彈,擋住敵方坦克
7、敵方坦克相撞不能穿透過去
注意事項:
1、畫一輛敵人的坦克,步驟:加入區別敵我的量good,根據敵我的不同設置不同的顏色更新Tank的構造函數,加入good,TankClient中new 出敵人的坦克并畫出(注意如果新增加了成員變量,就要馬上考慮是否要更改或新增構造方法)
2、將敵人坦克擊斃,分析:一顆子彈擊中敵人坦克,步驟:Missle中加入hitTank(Tank)方法,返回布爾類型;利用碰撞檢測的輔助類Rectangle;為Tank和Missle都加入getRect方法;當擊中敵人坦克時,坦克被打死,子彈也死去;增加控制Tank生死的量live,如果死去就不畫了
3、加入爆炸,步驟:添加爆炸類;用不同直徑的圓模擬爆炸;加入live;加入位置屬性;加入draw方法。爆炸應該存在于集合類中;TankClient加入集合;將集合中的爆炸逐一畫出(如果死去就去除)。擊斃一輛坦克后應產生爆炸;hitTank時應產生爆炸
4、添加多輛坦克,步驟:用容器來裝敵人的Tank;向容器中裝入多輛敵人Tank;畫出來;運行,不能打掉;添加hitTanks方法,打一系列Tank;TankClient里面每發子彈都打tanks
5、讓敵軍坦克更加智能,步驟:
==>讓敵軍坦克動起來:構造函數中可以指定方向;new敵軍坦克的時候指定敵軍坦克的方向。
==>讓敵軍坦克向隨機方向移動:(Tank)靜態的,添加隨機數產生器 java.util.Random;move完成后,如果是敵軍坦克的,隨機產生一個數,來設定坦克下一個方向;Direction.values()。
==>讓敵軍坦克向隨機方向移動隨機的步驟:添加變量,記錄隨機步驟;當==0時,改變方向,否則,只是隨機步驟遞減;
==>讓敵軍坦克發射炮彈:本軍炮彈不打本軍;炮彈添加好壞bGood,根據好壞畫不同顏色;修改炮彈的構造方法;修改Tank的fire方法;修改hitTank方法,好不能打好,壞不能打壞
6、添加兩堵墻,步驟:建Wall類、建立Wall對象、畫出來。
==>讓每一顆子彈打擊每一堵墻:hitWall()方法;注意子彈速度不能太快,否則很容易穿過墻;
==>讓坦克不能穿過墻:記錄上一次的位置oldX, oldY;修改構造函數;每次move之前紀錄上一次位置;添加stay方法;記錄移動前的位置;當撞到時回到移動前的位置;當碰到墻的時候stay
坦克大戰V0.5源代碼:
TankClient類:
import java.awt.*;
import java.awt.event.*;
import java.util.List; //java.awt包中也有個List,所以此處要導包明確
import java.util.ArrayList;
public class TankClient extends Frame {
public static final int GAME_WIDTH = 800;
public static final int GAME_HEIGHT = 600;
Tank myTank = new Tank(350, 450, true, Tank.Direction.STOP, this); //我方坦克
Wall w1 = new Wall(100, 300, 20, 100, this), w2 = new Wall(300, 250, 150, 20, this);
List missiles = new ArrayList(); //定義一個集合來裝子彈
List explodes = new ArrayList();
List tanks = new ArrayList();
Image offScreenImage = null; //定義一個屏幕后的虛擬圖片
@Override
public void paint(Graphics g) {
g.drawString("missiles Count: " + missiles.size(), 10, 50); //用來記錄missiles中子彈的個數
g.drawString("explodes Count: " + explodes.size(), 10, 70);
g.drawString("tanks Count: " + tanks.size(), 10, 90);
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.drawMissile(g);
}
for (int i = 0; i < explodes.size(); i++) {
Explode e = explodes.get(i);
e.draw(g);
}
for (int i = 0; i < tanks.size(); i++) {
Tank t = tanks.get(i);
t.collidesWithWall(w1); //檢測敵方坦克是否撞墻
t.collidesWithWall(w2);
t.collidesWithTanks(tanks);
t.drawTank(g);
}
myTank.drawTank(g);
w1.draw(g);
w2.draw(g);
}
//利用雙緩沖消除圓圈移動時屏幕的閃動
@Override
public void update(Graphics g) {
if (offScreenImage == null) {
offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT); //判斷是為了避免每次重畫時都給offScreenImage賦值
}
Graphics gOffScreen = offScreenImage.getGraphics(); //定義虛擬圖片上的畫筆gOffScreen
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); //再一次性把虛擬圖片畫到真實屏幕上,在真實屏幕上畫則要用真實屏幕的畫筆g
}
public void luanchFrame() {
for(int i = 0; i < 10; i++) {
tanks.add(new Tank(50 + 40*(i+1), 50, false,Tank.Direction.D, this));
}
this.setLocation(300, 50);
this.setSize(GAME_WIDTH, GAME_HEIGHT);
this.setTitle("坦克大戰 - By:小易 - QQ:381740148");
this.setResizable(false); //不允許改變窗口大小
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}); //添加關閉功能,此處使用匿名類比較合適
this.setBackground(Color.GREEN);
this.addKeyListener(new KeyMonitor());
setVisible(true);
new Thread(new PaintThread()).start(); //啟動線程,實例化線程對象時不要忘了new Thread(Runnable對象);
}
public static void main(String[] args) {
TankClient tc = new TankClient();
tc.luanchFrame();
}
//PaintThread只為TankClient服務,所以寫成內部類好些
public class PaintThread implements Runnable {
public void run() {
while (true) {
repaint(); //repaint()是TankClient或者他的父類的方法,內部類可以訪問外部包裝類的成員,這也是內部類的好處
try {
Thread.sleep(50); //每隔50毫秒重畫一次
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class KeyMonitor extends KeyAdapter {
@Override
public void keyReleased(KeyEvent e) {
myTank.keyReleased(e);
}
@Override
public void keyPressed(KeyEvent e) {
myTank.keyPressed(e);
}
}
}
Tank類:
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class Tank {
public static final int XSPEED = 5; //定義常量X軸速度
public static final int YSPEED = 5;
public static final int WIDTH = 30;
public static final int HEIGHT = 30;
//定義一個隨機數產生器,此時的Random類是java.util.Random,不同于Math中的
private static Random r = new Random(); //隨機數產生器只需要一個,所以定義成靜態,防止每次new一個坦克是都會產生一個隨機數產生器
private boolean good; //定義變量說明是我方還是敵方坦克,為true表示我方坦克
private boolean live = true; //定義變量說明是坦克是否存活
TankClient tc;
private int x , y; //定義變量畫圓圈(坦克)時四邊形左上點的x、y左邊
private int oldX , oldY; //定義坦克上個位置的坐標
private boolean bL = false, bU = false, bR = false, bD = false; //定義變量左上右下的按鍵是否被按下
enum Direction {L,LU,U,RU,R,RD,D,LD,STOP}; //定義枚舉類型,值為左、左上、上、右上、右、右下、下、左下、停止
private Direction dir = Direction.STOP; //定義變量坦克的方向
private Direction ptDir = Direction.D; //定義變量坦克炮筒的方向,起初向下
private int step = r.nextInt(12) + 3; //定義坦克朝著一個方向移動幾步
public Tank(int x, int y, boolean good) {
this.x = x;
this.y = y;
this.good = good;
this.oldX = x;
this.oldY =y;
}
public Tank(int x, int y, boolean good, Direction dir, TankClient tc) {
this(x, y, good); //相當于調用上面的構造方法
this.dir = dir;
this.tc = tc;
}
public void drawTank(Graphics g) {
if(!live) {
if(!good) {
tc.tanks.remove(this);
}
return; //如果坦克沒有存活就直接返回,不用畫坦克了
}
Color c = g.getColor(); //取得g(以后稱為畫筆)的顏色
if(good) g.setColor(Color.RED);
else g.setColor(Color.BLUE);
g.fillOval(x, y, WIDTH, HEIGHT); //"畫圓",利用填充一個四邊形(四邊形的內切圓),參數分別代表:四邊形左上點的坐標X,Y,寬度,高度
g.setColor(c); //用完畫筆后把畫筆默認的顏色(黑色)設置回去
//根據炮筒的方向,畫直線代表炮筒
switch (ptDir) {
case L:
g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x, y+Tank.HEIGHT/2);
break;
case LU:
g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x, y);
break;
case U:
g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH/2, y);
break;
case RU:
g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH, y);
break;
case R:
g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH, y+Tank.HEIGHT/2);
break;
case RD:
g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH, y+Tank.HEIGHT);
break;
case D:
g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x+Tank.WIDTH/2, y+Tank.HEIGHT);
break;
case LD:
g.drawLine(x+Tank.WIDTH/2, y+Tank.HEIGHT/2, x, y+Tank.HEIGHT);
break;
}
move(); //每次按鍵都會重畫,就會調用drawTank,在這里重畫坦克的此時位置
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode(); //得到按鍵的虛擬碼,再和下面的KeyEvent.VK_LEFT等虛擬碼比較看是否是某按鍵
switch (key) {
case KeyEvent.VK_LEFT:
bL = true;
break;
case KeyEvent.VK_UP:
bU = true;
break;
case KeyEvent.VK_RIGHT:
bR = true;
break;
case KeyEvent.VK_DOWN:
bD = true;
break;
}
locateDraction();
if (dir != Direction.STOP) {
ptDir = dir;
}
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
switch (key) {
case KeyEvent.VK_R: //按R就發射子彈調用fire方法
fire(); //只有松開R才能發出子彈
break;
case KeyEvent.VK_LEFT:
bL = false;
break;
case KeyEvent.VK_UP:
bU = false;
break;
case KeyEvent.VK_RIGHT:
bR = false;
break;
case KeyEvent.VK_DOWN:
bD = false;
break;
}
locateDraction();
}
//通過上右下的按鍵是否被按下判斷坦克要運動的方向
void locateDraction() {
if(bL && !bU && !bR && !bD) dir =Direction.L;
else if(bL && bU && !bR && !bD) dir =Direction.LU;
else if(!bL && bU && !bR && !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 && !bR && !bD) dir =Direction.STOP;
}
public void move() {
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 STOP:
break;
}
if(this.dir != Direction.STOP) {
this.ptDir = this.dir;
}
//防止坦克出界
if (x < 0) x = 0;
if (y < 25) y = 25; //考慮了標題欄的高度
if (x + Tank.WIDTH > TankClient.GAME_WIDTH) x = TankClient.GAME_WIDTH - Tank.WIDTH;
if (y + Tank.HEIGHT > TankClient.GAME_HEIGHT) y = TankClient.GAME_HEIGHT - Tank.HEIGHT;
if (!good) {
Direction[] dirs = Direction.values(); //把枚舉轉換成數組
if (step == 0) {
int rn = r.nextInt(dirs.length);
dir = dirs[rn]; //如果移動步數為0就改變方向
step = r.nextInt(12) + 3;
}
step --;
if(r.nextInt(40) > 37) this.fire();
}
}
public void stay() {
x = oldX;
y = oldY;
}
//坦克開火,就new一個子彈出來
private 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(x, y,good, ptDir , this.tc);
tc.missiles.add(m); //每new一個Missile對象就把他裝到集合中
return m; //返回的m,其他地方可調用可不調用
}
public Rectangle getRect() {
return new Rectangle(x, y, WIDTH, HEIGHT); //得到坦克的探測方塊,Rectangle是java.awt包中專門用于游戲碰撞的類
}
public boolean isLive() {
return live;
}
//Tank類的成員方法可生成對應的get和set方法,那么在其他類中就可以訪問了
public void setLive(boolean live) {
this.live = live;
}
public boolean isGood() {
return good;
}
//檢測坦克是否撞墻
public boolean collidesWithWall(Wall w) {
if(this.live && this.getRect().intersects(w.getRect())) {
stay(); //如果坦克撞到墻就讓他回到上一個位置
return true;
}
return false;
}
//檢測坦克是否相撞,java.util.List接口或者類的另一種寫法,java.awt中也有List,所以要寫明確
public boolean collidesWithTanks(java.util.List 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();
t.stay();
return true;
}
}
}
return false;
}
}
Missile類:
import java.awt.*;
import java.util.List;
public class Missile {
public static final int XSPEED = 10;
public static final int YSPEED = 10;
public static final int WIDTH = 10;
public static final int HEIGHT = 10;
int x, y;
Tank.Direction dir;
private boolean good; //定義變量表示是否是我方子彈
private boolean live = true; //定義一個判斷子彈是否出界的變量
private TankClient tc;
public Missile(int x, int y, Tank.Direction dir) {
this.x = x;
this.y = y;
this.dir = dir;
}
public Missile(int x, int y,boolean good, Tank.Direction dir, TankClient tc) {
this(x, y, dir);
this.good = good;
this.tc = tc;
}
public void drawMissile(Graphics g) {
if(!live) {
tc.missiles.remove(this);
return;
}
Color c = g.getColor();
g.setColor(Color.BLACK);
g.fillOval(x, y, WIDTH, HEIGHT);
g.setColor(c);
move();
}
private void move() {
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;
}
if (x < 0 || y < 0 || x > tc.GAME_WIDTH || y > tc.GAME_HEIGHT) {
live = false;
}
}
public boolean isLive() {
return live;
}
public Rectangle getRect() {
return new Rectangle(x, y, WIDTH, HEIGHT); //得到子彈的探測方塊,Rectangle是java.awt包中專門用于游戲碰撞的類
}
//判斷子彈是否打到了坦克
public boolean hitTank(Tank t) {
//intersects是Rectangle的一個方法;t.isLive()是為了判斷坦克是否存活,如果沒有則打掉這個坦克后,之后的子彈到達原來這個坦克的位置就會消失
if (this.live && this.getRect().intersects(t.getRect()) && t.isLive() && this.good != t.isGood()) {
t.setLive(false);
this.live = false;
Explode e = new Explode(x, y, tc);
tc.explodes.add(e);
return true;
}
return false;
}
public boolean hitTanks(List 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;
}
}
Explode類:
import java.awt.*;
//爆炸類(爆炸的英文:explode)
public class Explode {
int x, y;
private TankClient tc;
private boolean live = true;
int[] diameter = {4, 10, 20, 36, 56, 24, 12, 6}; //定義畫不同直徑的圓表示爆炸(直徑的英文:diameter)
int step = 0; //定義變量代表畫爆炸的圓畫到第幾個了
public Explode(int x, int y, TankClient tc) {
this.x = x;
this.y = y;
this.tc = tc;
}
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 ++ ;
}
}
Wall類:
import java.awt.*;
public class Wall {
private int x, y, w, h;
private TankClient tc;
public Wall(int x, int y, int w, int h, TankClient tc) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.tc = tc;
}
public void draw(Graphics g) {
g.fillRect(x, y, w, h);
}
public Rectangle getRect() {
return new Rectangle(x, y, w, h);
}
}
知識點回顧:
1、碰撞檢測的輔助類Rectangle,屬于java.awt.Rectangle中,用法:
==>首先獲取兩個要檢測物體的邊界,相當于一個Rectangle對象,可以給他們定義相應的getRect()方法,返回值為一個Rectangle對象,在函數內return
new Rectangle(x, y, w, h);
==>定義一個方法判斷是否碰撞到了,返回值為boolean,物體1.getRect().intersects(物體2.getRect())的返回值也是一個boolean類型,返回真則表示碰撞到了,再執行相應的功能,反之亦然。
2、將枚舉轉換成數組:枚舉名.values(),返回的是相應的數組。如:Direction[] dirs = Direction.values();返回的是Direction類型的數組
3、隨機數的使用:
==>java.util.Random比較方便,可以直接產生隨機整數,使用時先定義一個隨機數產生器,如:定義隨機數產生器:Random
r = new Random(); 產生隨機數整數:r.nextInt(12)表示產生一個0-11的隨機整數
==>math.random()產生的是一個0-1的數,使用時一般要通過計算轉換,比較麻煩
總結
以上是生活随笔為你收集整理的java坦克大战爆炸效果_Java坦克大战 (五) 之产生敌方坦克和爆炸效果的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机一级报名照片是几寸的,法考报名照片
- 下一篇: 软件系统测试报告范文,软件系统测试报告模