Java坦克大战
目錄
項(xiàng)目git地址:https://gitee.com/java-course-design_1/java-programming-tank-game
項(xiàng)目簡介:
項(xiàng)目技術(shù):
功能需求分析
功能架構(gòu)圖:
編寫思路及部分代碼
游戲窗口初始化
?坦克類的定義
地圖類
游戲常量類
優(yōu)化 :添加背景音樂和音效
?操作演示視頻
項(xiàng)目git地址:https://gitee.com/java-course-design_1/java-programming-tank-game
項(xiàng)目簡介:
這是一個功能相對全的Java版坦克大戰(zhàn),界面繪制是通過Java的圖形化用戶界面swing完成的,包括了菜單界面和游戲界面。其中菜單界面可以供玩家選擇重新開始游戲、暫停、繼續(xù)、等操作;游戲界面繪制了坦克、草地、墻壁、鷹碉堡等經(jīng)典坦克場景,玩家在游戲界面操作自己的坦克開始對戰(zhàn)。
項(xiàng)目技術(shù):
本游戲使用的主要技術(shù)有Swing編程、面向?qū)ο缶幊獭⒍嗑€程編程。
功能需求分析
游戲?qū)崿F(xiàn)的主要功能有:
1、我方坦克默認(rèn)可以渡河,碰到墻壁不能走,鷹碉堡被擊中游戲結(jié)束
2、坦克可以上下左右、以及左上左下右上右下八個方向移動,移動時添加音效
3、坦克可以發(fā)子彈(可以連發(fā)),發(fā)射時添加音效
4、擊中對方坦克時,坦克消失,顯示爆炸效果;子彈擊中墻壁時,子彈消失
5、我方坦克吃到血塊時,生命值加30(可以自己設(shè)定);我方被擊中時每次血量減50
6、移動過程中檢測碰撞,包括坦克與坦克,坦克與草地、河流、墻壁等
7、聲音處理(開始音樂、背景音樂、移動音效、爆炸音效等)
8、菜單處理(重新開始、暫停/繼續(xù)游戲、是否播放背景音樂、設(shè)置游戲難度、幫助等
功能架構(gòu)圖:
app包下的GameMain類是游戲的入口函數(shù)。
map包下是游戲地圖的繪制
?game包下的類是關(guān)于繪制可視化圖像,使用Graphics類繪制游戲窗口。
tank包下的類是tank的實(shí)體類,tank類分為我方坦克MyTank類、敵方坦克EnemyTank類。兩個坦克類都繼承了坦克父類tank類。
utils包下有工具類、爆炸對象池、子彈對象池、游戲常量對象
編寫思路及部分代碼
游戲窗口初始化
1:對游戲的窗口進(jìn)行初始化,設(shè)置
(主要代碼截圖)
package com.lwj.game;import com.lwj.tank.EnemyTank; import com.lwj.tank.MyTank; import com.lwj.tank.Tank; import com.lwj.util.MusicUtil; import com.lwj.util.MyUtil; import com.lwj.map.GameMap;import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List;import static com.lwj.util.Constant.*;/*** 游戲的主窗口類。* 所有的游戲展示的內(nèi)容都要在該類中實(shí)現(xiàn)。*/ public class GameFrame extends Frame implements Runnable{//第一次使用的時候加載,而不是類加載的時候加載,private Image overImg = null;//1:定義一張和屏幕大小一致的圖片private BufferedImage bufImg = new BufferedImage(FRAME_WIDTH,FRAME_HEIGHT,BufferedImage.TYPE_4BYTE_ABGR);//游戲狀態(tài)private static int gameState;//菜單指向private static int menuIndex;//標(biāo)題欄的高度public static int titleBarH;//定義坦克對象private static Tank myTank;//敵人的坦克容器private static List<Tank> enemies = new ArrayList<>();//用來記錄本關(guān)卡產(chǎn)生了多少個敵人private static int bornEnemyCount;public static int killEnemyCount;//定義地圖相關(guān)的內(nèi)容private static GameMap gameMap = new GameMap();;/*** 對窗口進(jìn)行初始化*/public GameFrame() {initFrame();initEventListener();//啟動用于刷新窗口的線程new Thread(this).start();}/*** 進(jìn)入下一關(guān)的方法*/private void nextLevel() {startGame(LevelInof.getInstance().getLevel()+1);}//開始過關(guān)動畫public static int flashTime;public static final int RECT_WIDTH = 40;public static final int RECT_COUNT = FRAME_WIDTH/RECT_WIDTH+1;public static boolean isOpen = false;public static void startCrossLevel(){gameState = STATE_CROSS;flashTime = 0;isOpen = false;}//繪制過關(guān)動畫public void drawCross(Graphics g){gameMap.drawBk(g);myTank.draw(g);gameMap.drawCover(g);g.setColor(Color.BLACK);//關(guān)閉百葉窗的效果if(!isOpen) {for (int i = 0; i < RECT_COUNT; i++) {g.fillRect(i * RECT_WIDTH, 0, flashTime, FRAME_HEIGHT);}//所有的葉片都關(guān)閉了if (flashTime++ - RECT_WIDTH > 5) {isOpen = true;//初始化下一個地圖gameMap.initMap(LevelInof.getInstance().getLevel()+1);}}else{//開百葉窗的效果for (int i = 0; i < RECT_COUNT; i++) {g.fillRect(i * RECT_WIDTH, 0, flashTime, FRAME_HEIGHT);}if(flashTime-- == 0){startGame(LevelInof.getInstance().getLevel());}}}/*** 對游戲進(jìn)行初始化*/private void initGame() {gameState = STATE_MENU;}/*** 屬性進(jìn)行初始化*/private void initFrame() {//設(shè)置標(biāo)題setTitle(GAME_TITLE);//設(shè)置窗口大小setSize(FRAME_WIDTH, FRAME_HEIGHT);//設(shè)置窗口的左上角的坐標(biāo)setLocation(FRAME_X, FRAME_Y);//設(shè)置窗口大小不可改變setResizable(false);//設(shè)置窗口可見setVisible(true);//求標(biāo)題欄的高度titleBarH = getInsets().top;}/**標(biāo)題,大小,坐標(biāo),窗口的事件的監(jiān)聽
2:游戲菜單的顯示,以及游戲菜單的按鍵的控制
游戲被分成了若干個游戲的狀態(tài): gameState
游戲菜單
游戲幫助
游戲關(guān)于
游戲中
游戲結(jié)束
在不同的狀態(tài)下,繪制不同的內(nèi)容,按鍵有不同的處理的方式
3:調(diào)整整個游戲窗口的重繪的機(jī)制。
FPS:frame per second
要每秒固定的刷新我們的整個窗口。fps==33幀
每隔30ms 刷新一次(對整個窗口進(jìn)行重繪)。repaint()
單獨(dú)啟動一個線程用于窗口的重繪。
運(yùn)行截圖:
?坦克類的定義
4.坦克類的定義,和繪制
5.坦克的四方向行走,以及邊界的控制
6:坦克發(fā)射子彈
7: 坦克的繪制使用圖片
8: 解決屏幕閃爍的問題? 雙緩沖解決
9.敵人坦克的控制
10: 隨機(jī)的在屏幕的左上角和右上角產(chǎn)生敵人的坦克
11: 坦克相關(guān)的類:父類:Tank 子類:MyTank、 EnemyTank
12:?敵人坦克AI:每隔五秒讓敵人坦克隨機(jī)獲得一個狀態(tài)(站立、行走)
敵人發(fā)射子彈的AI:游戲的每一幀都隨機(jī)(0.05 概率)判斷敵人是否發(fā)射子彈
13:添加爆炸類,爆炸效果的控制
14:使用對象池來管理爆炸對象
15: 給坦克添加血條和名字
16:敵人的坦克對象池管理
17:自己的坦克死亡的處理,切換游戲狀態(tài),游戲結(jié)束。
提供兩個選項(xiàng):回到主菜單,退出游戲、重置游戲數(shù)據(jù)、開始新游戲
(相關(guān)代碼如下)
package com.lwj.tank;import com.lwj.game.GameFrame; import com.lwj.game.LevelInof; import com.lwj.util.Constant; import com.lwj.util.EnemyTanksPool; import com.lwj.util.MyUtil;import java.awt.*;/*** 敵人坦克類*/ public class EnemyTank extends Tank {public static final int TYPE_GREEN = 0;public static final int TYPE_YELLOW = 1;private int type = TYPE_GREEN;private static Image[] greenImg;private static Image[] yellowImg;//記錄5秒開始的時間private long aiTime;static {greenImg = new Image[4];greenImg[0] = MyUtil.createImage("res/ul.png");greenImg[1] = MyUtil.createImage("res/dl.png");greenImg[2] = MyUtil.createImage("res/ll.png");greenImg[3] = MyUtil.createImage("res/rl.png");yellowImg = new Image[4];yellowImg[0] = MyUtil.createImage("res/u.png");yellowImg[1] = MyUtil.createImage("res/d.png");yellowImg[2] = MyUtil.createImage("res/l.png");yellowImg[3] = MyUtil.createImage("res/r.png");}private EnemyTank(int x, int y, int dir) {super(x, y, dir);//敵人一旦創(chuàng)建就計時aiTime = System.currentTimeMillis();type = MyUtil.getRandomNumber(0,2);}public EnemyTank(){type = MyUtil.getRandomNumber(0,2);aiTime = System.currentTimeMillis();}//用于創(chuàng)建一個敵人的坦克public static Tank createEnemy(){int x = MyUtil.getRandomNumber(0,2) == 0 ? RADIUS :Constant.FRAME_WIDTH-RADIUS;int y = GameFrame.titleBarH + RADIUS;int dir = DIR_DOWN;EnemyTank enemy = (EnemyTank)EnemyTanksPool.get();enemy.setX(x);enemy.setY(y);enemy.setDir(dir);enemy.setEnemy(true);enemy.setState(STATE_MOVE);//根據(jù)游戲的難度設(shè)置敵人的血量int maxHp = Tank.DEFAULT_HP*LevelInof.getInstance().getLevelType();enemy.setHp(maxHp);enemy.setMaxHP(maxHp);//通過關(guān)卡信息中的敵人類型,來設(shè)置當(dāng)前出生的敵人的類型int enemyType = LevelInof.getInstance().getRandomEnemyType();enemy.setType(enemyType);return enemy;}public int getType() {return type;}public void setType(int type) {this.type = type;}public void drawImgTank(Graphics g){ai();g.drawImage(type == TYPE_GREEN ? greenImg[getDir()] :yellowImg[getDir()],getX()-RADIUS,getY()-RADIUS,null);}/*** 敵人的AI*/private void ai(){if(System.currentTimeMillis() - aiTime > Constant.ENEMY_AI_INTERVAL){//間隔5秒隨機(jī)一個狀態(tài)setDir(MyUtil.getRandomNumber(DIR_UP,DIR_RIGHT+1));setState(MyUtil.getRandomNumber(0,2) == 0 ? STATE_STAND : STATE_MOVE);aiTime = System.currentTimeMillis();}//比較小的概率去開火if(Math.random() < Constant.ENEMY_FIRE_PERCENT){fire();}}} package com.lwj.tank;import com.lwj.util.MyUtil;import java.awt.*;/*** 自己的坦克*/ public class MyTank extends Tank{//坦克的圖片數(shù)組private static Image[] tankImg;//靜態(tài)代碼塊中對它進(jìn)行初始化static{tankImg = new Image[4];for (int i = 0; i <tankImg.length ; i++) {tankImg[i] = MyUtil.createImage("res/tank1_"+i+".png");}}public MyTank(int x, int y, int dir) {super(x, y, dir);}@Overridepublic void drawImgTank(Graphics g) {g.drawImage(tankImg[getDir()],getX()-RADIUS,getY()-RADIUS,null);}} package com.lwj.tank; import com.lwj.game.Bullet; import com.lwj.game.Explode; import com.lwj.game.GameFrame; import com.lwj.map.MapTile; import com.lwj.util.*;import java.awt.*; import java.util.ArrayList; import java.util.List;/*** 坦克類*/ public abstract class Tank {//四個方向public static final int DIR_UP = 0;public static final int DIR_DOWN = 1;public static final int DIR_LEFT = 2;public static final int DIR_RIGHT = 3;//半徑public static final int RADIUS = 20;//默認(rèn)速度 每幀 30mspublic static final int DEFAULT_SPEED = 4;//坦克的狀態(tài)public static final int STATE_STAND = 0;public static final int STATE_MOVE = 1;public static final int STATE_DIE = 2;//坦克的初始生命public static final int DEFAULT_HP = 100;private int maxHP = DEFAULT_HP;private int x,y;private int hp = DEFAULT_HP;private String name;private int atk;public static final int ATK_MAX = 25;public static final int ATK_MIN = 15;private int speed = DEFAULT_SPEED;private int dir;private int state = STATE_STAND;private Color color;private boolean isEnemy = false;private BloodBar bar = new BloodBar();//炮彈private List<Bullet> bullets = new ArrayList();//使用容器來保存當(dāng)前坦克上的所有的爆炸的效果private List<Explode> explodes = new ArrayList<>();public Tank(int x, int y, int dir) {this.x = x;this.y = y;this.dir = dir;initTank();}public Tank(){initTank();}private void initTank(){color = MyUtil.getRandomColor();name = MyUtil.getRandomName();atk = MyUtil.getRandomNumber(ATK_MIN,ATK_MAX);}/*** 繪制坦克* @param g*/public void draw(Graphics g){logic();drawImgTank(g);drawBullets(g);drawName(g);bar.draw(g);}private void drawName(Graphics g){g.setColor(color);g.setFont(Constant.SMALL_FONT);g.drawString(name, x - RADIUS ,y - 35);}/*** 使用圖片的方式去繪制坦克* @param g*/public abstract void drawImgTank(Graphics g);/*** 使用系統(tǒng)的方式去繪制坦克* @param g*/private void drawTank(Graphics g){g.setColor(color);//繪制坦克的圓g.fillOval(x-RADIUS,y-RADIUS,RADIUS<<1,RADIUS<<1);int endX = x;int endY = y;switch(dir){case DIR_UP:endY = y - RADIUS*2;g.drawLine(x-1,y,endX-1,endY);g.drawLine(x+1,y,endX+1,endY);break;case DIR_DOWN:endY = y + RADIUS*2;g.drawLine(x-1,y,endX-1,endY);g.drawLine(x+1,y,endX+1,endY);break;case DIR_LEFT:endX = x - 2 * RADIUS;g.drawLine(x,y-1,endX,endY-1);g.drawLine(x,y+1,endX,endY+1);break;case DIR_RIGHT:endX = x + 2 * RADIUS;g.drawLine(x,y-1,endX,endY-1);g.drawLine(x,y+1,endX,endY+1);break;}g.drawLine(x,y,endX,endY);}//坦克的邏輯處理private void logic(){switch(state){case STATE_STAND:break;case STATE_MOVE:move();break;case STATE_DIE:break;}}private int oldX = -1, oldY = -1;//坦克移動的功能private void move(){oldX = x;oldY = y;switch (dir){case DIR_UP:y -= speed;if(y < RADIUS + GameFrame.titleBarH){y = RADIUS + GameFrame.titleBarH;}break;case DIR_DOWN:y += speed;if(y > Constant.FRAME_HEIGHT-RADIUS){y = Constant.FRAME_HEIGHT-RADIUS;}break;case DIR_LEFT:x -= speed;if(x < RADIUS){x = RADIUS;}break;case DIR_RIGHT:x += speed;if(x > Constant.FRAME_WIDTH-RADIUS){x = Constant.FRAME_WIDTH-RADIUS;}break;}}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;}public int getHp() {return hp;}public void setHp(int hp) {this.hp = hp;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk = atk;}public int getSpeed() {return speed;}public void setSpeed(int speed) {this.speed = speed;}public int getDir() {return dir;}public void setDir(int dir) {this.dir = dir;}public int getState() {return state;}public void setState(int state) {this.state = state;}public Color getColor() {return color;}public void setColor(Color color) {this.color = color;}public List getBullets() {return bullets;}public void setBullets(List bullets) {this.bullets = bullets;}public boolean isEnemy() {return isEnemy;}public void setEnemy(boolean enemy) {isEnemy = enemy;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Tank{" +"x=" + x +", y=" + y +", hp=" + hp +", atk=" + atk +", speed=" + speed +", dir=" + dir +", state=" + state +'}';}//上一次開火的時間private long fireTime;//子彈發(fā)射的最小的間隔public static final int FIRE_INTERVAL = 200;/*** 坦克的功能,坦克開火的方法* 創(chuàng)建了一個子彈對象,子彈對象的屬性信息通過坦克的信息獲得* 然后將創(chuàng)建的子彈添加到坦克管理的容器中。*/public void fire(){if(System.currentTimeMillis() - fireTime >FIRE_INTERVAL) {int bulletX = x;int bulletY = y;switch (dir) {case DIR_UP:bulletY -= RADIUS;break;case DIR_DOWN:bulletY += RADIUS;break;case DIR_LEFT:bulletX -= RADIUS;break;case DIR_RIGHT:bulletX += RADIUS;break;}//從對象池中獲取子彈對象Bullet bullet = BulletsPool.get();//設(shè)置子彈的屬性bullet.setX(bulletX);bullet.setY(bulletY);bullet.setDir(dir);bullet.setAtk(atk);bullet.setColor(color);bullet.setVisible(true);bullets.add(bullet);//發(fā)射子彈之后,記錄本次發(fā)射的時間fireTime = System.currentTimeMillis();// MusicUtil.playBomb();}}/*** 將當(dāng)前坦克的發(fā)射的所有的子彈繪制出來*/private void drawBullets(Graphics g){ // for (Bullet bullet : bullets) { // bullet.draw(g); // }for (int i = 0; i < bullets.size(); i++) {Bullet bullet = bullets.get(i);bullet.draw(g);}//是遍歷所有的子彈,將不可見的子彈移除,并還原回對象池for (int i = 0; i < bullets.size(); i++) {Bullet bullet = bullets.get(i);if(!bullet.isVisible()) {Bullet remove = bullets.remove(i);i--;BulletsPool.theReturn(remove);}}}/*** 坦克銷毀的時候處理坦克的所有的子彈*/public void bulletsReturn(){ // for (Bullet bullet : bullets) { // BulletsPool.theReturn(bullet); // }for (int i = 0; i < bullets.size(); i++) {Bullet bullet = bullets.get(i);BulletsPool.theReturn(bullet);}bullets.clear();}//坦克和敵人的子彈碰撞的方法。public void collideBullets(List<Bullet> bullets){for (int i = 0; i < bullets.size(); i++) {Bullet bullet = bullets.get(i); // }// 遍歷所有的子彈,依次和當(dāng)前的坦克進(jìn)行碰撞的檢測 // for (Bullet bullet : bullets) {int bulletX = bullet.getX();int bulletY = bullet.getY();//子彈和坦克碰上了。if(MyUtil.isCollide(this.x,y,RADIUS,bulletX,bulletY)){//子彈消失bullet.setVisible(false);//坦克受到傷害hurt(bullet);//添加爆炸效果addExplode(x,y+RADIUS);}}}private void addExplode(int x,int y){//添加爆炸效果,以當(dāng)前被擊中的坦克的坐標(biāo)為參考Explode explode = ExplodesPool.get();explode.setX(x);explode.setY(y);explode.setVisible(true);explode.setIndex(0);explodes.add(explode);}//坦克收到傷害private void hurt(Bullet bullet){int atk = bullet.getAtk();System.out.println("atk = "+atk);hp -= atk;if(hp < 0){hp = 0;die();}}//坦克死亡需要處理的內(nèi)容private void die(){//敵人if(isEnemy){GameFrame.killEnemyCount ++;//敵人坦克被消滅了 歸還對象池EnemyTanksPool.theReturn(this);//本關(guān)是否結(jié)束? TODOif (GameFrame.isCrossLevel()){//判斷游戲是否通關(guān)了?if(GameFrame.isLastLevel()){//通關(guān)了GameFrame.setGameState(Constant.STATE_WIN);}else {//TODO 進(jìn)入下一關(guān)GameFrame.startCrossLevel();}}}else{delaySecondsToOver(3000);}}/*** 判斷當(dāng)前的坦克是否死亡* @return*/public boolean isDie(){return hp <= 0;}/*** 繪制當(dāng)前坦克上的所有的爆炸的效果* @param g*/public void drawExplodes(Graphics g){ // for (Explode explode : explodes) {for (int i = 0; i < explodes.size(); i++) {Explode explode = explodes.get(i);explode.draw(g);}//將不可見的爆炸效果刪除,還回對象池for (int i = 0; i < explodes.size(); i++) {Explode explode = explodes.get(i);if(!explode.isVisible()){Explode remove = explodes.remove(i);ExplodesPool.theReturn(remove);i--;}}}//內(nèi)部類,來表示坦克的血條class BloodBar{public static final int BAR_LENGTH = 50;public static final int BAR_HEIGHT = 3;public void draw(Graphics g){//填充底色g.setColor(Color.YELLOW);g.fillRect(x - RADIUS , y-RADIUS-BAR_HEIGHT*2,BAR_LENGTH,BAR_HEIGHT);//紅色的當(dāng)前血量g.setColor(Color.RED);g.fillRect(x - RADIUS , y-RADIUS-BAR_HEIGHT*2,hp*BAR_LENGTH/maxHP,BAR_HEIGHT);//藍(lán)色的邊框g.setColor(Color.WHITE);g.drawRect(x - RADIUS , y-RADIUS-BAR_HEIGHT*2,BAR_LENGTH,BAR_HEIGHT);}}//坦克的子彈和地圖所有的塊的碰撞public void bulletsCollideMapTiles(List<MapTile> tiles){//foreach遍歷容器中的元素,//在遍歷的過程中只能使用迭代器的刪除方式刪除元素//都切換為使用基本的for循環(huán) // for (MapTile tile : tiles) {for (int i = 0; i < tiles.size(); i++) {MapTile tile = tiles.get(i);if(tile.isCollideBullet(bullets)){//添加爆炸效果addExplode(tile.getX()+MapTile.radius,tile.getY() +MapTile.tileW);//地圖水泥塊沒有擊毀的處理if(tile.getType() == MapTile.TYPE_HARD)continue;//設(shè)置地圖塊銷毀tile.setVisible(false);//歸還對象池MapTilePool.theReturn(tile);//當(dāng)老巢被擊毀之后,一秒鐘切換到游戲結(jié)束的畫面if(tile.isHouse()){delaySecondsToOver(3000);}}}// }}/*** 延遲 若干毫秒 切換到 游戲結(jié)束* @param millisSecond*/private void delaySecondsToOver(int millisSecond){new Thread(){public void run() {try {Thread.sleep(millisSecond);} catch (InterruptedException e) {e.printStackTrace();}GameFrame.setGameState(Constant.STATE_LOST);}}.start();}/*** 一個地圖塊和當(dāng)前的坦克碰撞的方法* 從tile 中提取8個點(diǎn) 來判斷 8個點(diǎn)是否有任何一個點(diǎn)和當(dāng)前的坦克有了碰撞* 點(diǎn)的順序從左上角的點(diǎn)開始,順時針遍歷*/public boolean isCollideTile(List<MapTile> tiles){final int len = 2;for (int i = 0; i < tiles.size(); i++) {MapTile tile = tiles.get(i); // } // for (MapTile tile : tiles) {//如果塊不可見,或者是遮擋塊就不進(jìn)行碰撞的檢測if(!tile.isVisible() || tile.getType() == MapTile.TYPE_COVER)continue;//點(diǎn)-1 左上角int tileX = tile.getX();int tileY = tile.getY();boolean collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);//如果碰上了就直接返回,否則繼續(xù)判斷下一個點(diǎn)if(collide){return true;}//點(diǎn)-2 中上點(diǎn)tileX += MapTile.radius;collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);if(collide){return true;}//點(diǎn)-3 右上角點(diǎn)tileX += MapTile.radius;collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);if(collide){return true;}//點(diǎn)-4 右中點(diǎn)tileY += MapTile.radius;collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);if(collide){return true;}//點(diǎn)-5 右下點(diǎn)tileY += MapTile.radius;collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);if(collide){return true;}//點(diǎn)-6 下中點(diǎn)tileX -= MapTile.radius;collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);if(collide){return true;}//點(diǎn)-7 左下點(diǎn)tileX -= MapTile.radius;collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);if(collide){return true;}//點(diǎn)-8 左中點(diǎn)tileY -= MapTile.radius;collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);if(collide){return true;}}return false;}/*** 坦克回退的方法*/public void back(){x = oldX;y = oldY;}public int getMaxHP() {return maxHP;}public void setMaxHP(int maxHP) {this.maxHP = maxHP;} }地圖類
20: 地圖? 地圖元素類
設(shè)計:上、左右,距離窗口的寬度為1.5倍的坦克寬度。
21:實(shí)現(xiàn)一張地圖的繪制顯示
22:優(yōu)化了地圖隨機(jī)初始化。子彈和地圖碰撞
23:坦克和地圖的碰撞
24:敵人坦克和地圖的碰撞
25:玩家的老巢,老巢被擊毀 游戲結(jié)束。延遲若干秒
26:控制玩家的發(fā)射炮彈的速度(最小間隔,200毫秒)
27:添加其他種類的地圖元素
不可擊毀的地圖元素
遮擋物
28:添加不同地圖的種類
package com.lwj.map; import com.lwj.game.GameFrame; import com.lwj.game.LevelInof; import com.lwj.tank.Tank; import com.lwj.util.Constant; import com.lwj.util.MapTilePool; import com.lwj.util.MyUtil;import java.awt.*; import java.io.FileInputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Properties;/*** 游戲地圖類*/ public class GameMap {public static final int MAP_X = Tank.RADIUS*3;public static final int MAP_Y = Tank.RADIUS*3 + GameFrame.titleBarH;public static final int MAP_WIDTH = Constant.FRAME_WIDTH-Tank.RADIUS*6;public static final int MAP_HEIGHT = Constant.FRAME_HEIGHT-Tank.RADIUS*8-GameFrame.titleBarH;//地圖元素塊的容器private List<MapTile> tiles = new ArrayList<>();//大本營private TankHouse house;public GameMap() {}/*** 初始化地圖元素塊,level : 第幾關(guān)*/public void initMap(int level){tiles.clear();try {loadLevel(level);} catch (Exception e) {e.printStackTrace();}//初始化大本營house = new TankHouse();addHouse();}/*** 加載關(guān)卡信息* @param level*/private void loadLevel(int level) throws Exception{//獲得關(guān)卡信息類的唯一實(shí)例對象LevelInof levelInof = LevelInof.getInstance();levelInof.setLevel(level);Properties prop = new Properties();prop.load(new FileInputStream("level/lv_"+level));//將所有的地圖信息都加載進(jìn)來int enemyCount = Integer.parseInt(prop.getProperty("enemyCount"));//設(shè)置敵人數(shù)量levelInof.setEnemyCount(enemyCount);//0,1 對敵人類型解析String[] enemyType = prop.getProperty("enemyType").split(",");int[] type = new int[enemyType.length];for (int i = 0; i < type.length; i++) {type[i] = Integer.parseInt(enemyType[i]);}//設(shè)置敵人類型levelInof.setEnemyType(type);//關(guān)卡難度//如果沒有設(shè)計游戲難度,那么就當(dāng)默認(rèn)的處理String levelType = prop.getProperty("levelType");levelInof.setLevelType(Integer.parseInt(levelType==null? "1" : levelType));String methodName = prop.getProperty("method");int invokeCount = Integer.parseInt(prop.getProperty("invokeCount"));//把實(shí)參都讀取到數(shù)組中來。String[] params = new String[invokeCount];for (int i = 1; i <=invokeCount ; i++) {params[i-1] = prop.getProperty("param"+i);}//使用讀取到的參數(shù),調(diào)用對應(yīng)的方法。invokeMethod(methodName,params);}//根據(jù)方法的名字和參數(shù)調(diào)用對應(yīng)的方法private void invokeMethod(String name,String[] params){for (String param : params) {//獲得每一行的方法的參數(shù),解析。String[] split = param.split(",");//使用一個int 數(shù)組保存解析后的內(nèi)容int[] arr = new int[split.length];int i;for (i = 0; i < split.length-1; i++) {arr[i] = Integer.parseInt(split[i]);}//塊之間的間隔是地圖塊的倍數(shù)final int DIS = MapTile.tileW ;//解析最后一個double值int dis = (int)(Double.parseDouble(split[i])*DIS);switch(name){case "addRow":addRow(MAP_X+arr[0]*DIS,MAP_Y+arr[1]*DIS,MAP_X+MAP_WIDTH-arr[2]*DIS,arr[3],dis);break;case "addCol":addCol(MAP_X+arr[0]*DIS,MAP_Y+arr[1]*DIS,MAP_Y+MAP_HEIGHT-arr[2]*DIS,arr[3],dis);break;case "addRect":addRect(MAP_X+arr[0]*DIS,MAP_Y+arr[1]*DIS,MAP_X+MAP_WIDTH-arr[2]*DIS,MAP_Y+MAP_HEIGHT-arr[3]*DIS,arr[4],dis);break;}}}//將老巢的所有的元素塊添加到地圖的容器中private void addHouse(){tiles.addAll(house.getTiles());}/*** 某一個點(diǎn)確定的地圖塊。是否和 tiles 集合中的所有的塊 有重疊的部分* @param tiles* @param x* @param y* @return 有重疊返回 true,否則 false*/private boolean isCollide(List<MapTile> tiles, int x ,int y){for (MapTile tile : tiles) {int tileX = tile.getX();int tileY = tile.getY();if(Math.abs(tileX-x) < MapTile.tileW && Math.abs(tileY-y) < MapTile.tileW){return true;}}return false;}/*** 只對沒有遮擋效果的塊進(jìn)行繪制* @param g*/public void drawBk(Graphics g){for (MapTile tile : tiles) {if(tile.getType() != MapTile.TYPE_COVER)tile.draw(g);}}/*** 只繪制有遮擋效果的塊* @param g*/public void drawCover(Graphics g){for (MapTile tile : tiles) {if(tile.getType() == MapTile.TYPE_COVER)tile.draw(g);}}public List<MapTile> getTiles() {return tiles;}/*** 將所有的不可見的地圖塊從容器中移除*/public void clearDestroyTile(){for (int i = 0; i < tiles.size(); i++) {MapTile tile = tiles.get(i);if(!tile.isVisible())tiles.remove(i);}}/***往地圖塊容器中添加一行指定類型的地圖塊* @param startX 添加地圖塊的起始的x坐標(biāo)* @param startY 添加地圖塊的起始的Y坐標(biāo)* @param endX 添加地圖塊的結(jié)束的x坐標(biāo)* @param type 地圖塊的類型* @param DIS 地圖塊之間的中心點(diǎn)的間隔 如果是塊的寬度 意味著是連續(xù)的,* 如果大于塊的寬度就是不連續(xù)的*/public void addRow(int startX,int startY, int endX, int type, final int DIS){int count = (endX - startX +DIS )/(MapTile.tileW+DIS);for (int i = 0; i <count ; i++) {MapTile tile = MapTilePool.get();tile.setType(type);tile.setX(startX + i * (MapTile.tileW+DIS));tile.setY(startY);tiles.add(tile);}}/*** 往地圖元素塊容器中添加一列 元素。* @param startX 該列的起始x坐標(biāo)* @param startY 該列的起始y坐標(biāo)* @param endY 改了的結(jié)束y坐標(biāo)* @param type 元素類型* @param DIS 相鄰元素中心點(diǎn)的間距*/public void addCol(int startX,int startY, int endY, int type, final int DIS){int count = (endY - startY +DIS)/(MapTile.tileW+DIS);for (int i = 0; i <count ; i++) {MapTile tile = MapTilePool.get();tile.setType(type);tile.setX(startX );tile.setY(startY + i * (MapTile.tileW+DIS));tiles.add(tile);}}//對指定的矩形區(qū)域添加元素塊public void addRect(int startX,int startY,int endX, int endY, int type, final int DIS){int rows = (endY-startY+DIS)/(MapTile.tileW+DIS);for (int i = 0; i <rows ; i++) {addRow(startX,startY+i*(MapTile.tileW+DIS),endX,type,DIS);}}}運(yùn)行結(jié)果如下
游戲常量類
/*** 游戲常量類*/ public class Constant {/****************游戲窗口相關(guān)屬性*********************/public static final String GAME_TITLE = "坦克大戰(zhàn)";public static final int FRAME_WIDTH = 1000;public static final int FRAME_HEIGHT = 900;//獲取系統(tǒng)屏幕的寬高public static final int SCREEN_W = Toolkit.getDefaultToolkit().getScreenSize().width;public static final int SCREEN_H = Toolkit.getDefaultToolkit().getScreenSize().height;//游戲窗口頂點(diǎn)坐標(biāo)public static final int FRAME_X = SCREEN_W - FRAME_WIDTH >> 1;public static final int FRAME_Y = SCREEN_H - FRAME_HEIGHT >> 1;/****************游戲菜單相關(guān)屬性********************************/public static final int STATE_MENU = 0;public static final int STATE_HELP = 1;public static final int STATE_ABOUT = 2;public static final int STATE_RUN = 3;public static final int STATE_OVER = 4;public static final String[] MENUS = {"開始游戲","繼續(xù)游戲","游戲幫助","游戲相關(guān)","退出游戲"};public static final Font GAME_FONT = new Font("宋體", Font.BOLD, 24);//窗口刷新間隔public static final int REPAINT_INTERVAL = 30;//最大敵人數(shù)量public static final int ENEMY_MAX_COUNT = 10;//敵方坦克生產(chǎn)間隔public static final int ENEMY_BORN_INTERVAL = 5000;//敵方坦克狀態(tài)切換的間隔public static final int ENEMY_AI_INTERVAL = 1000;//敵方坦克每幀發(fā)射子彈的概率public static final double ENEMY_AI_PERCENT = 0.10; } Size().width;public static final int SCREEN_H = Toolkit.getDefaultToolkit().getScreenSize().height;//游戲窗口頂點(diǎn)坐標(biāo)public static final int FRAME_X = SCREEN_W - FRAME_WIDTH >> 1;public static final int FRAME_Y = SCREEN_H - FRAME_HEIGHT >> 1;/****************游戲菜單相關(guān)屬性********************************/public static final int STATE_MENU = 0;public static final int STATE_HELP = 1;public static final int STATE_ABOUT = 2;public static final int STATE_RUN = 3;public static final int STATE_OVER = 4;public static final String[] MENUS = {"開始游戲","繼續(xù)游戲","游戲幫助","游戲相關(guān)","退出游戲"};public static final Font GAME_FONT = new Font("宋體", Font.BOLD, 24);//窗口刷新間隔public static final int REPAINT_INTERVAL = 30;//最大敵人數(shù)量public static final int ENEMY_MAX_COUNT = 10;//敵方坦克生產(chǎn)間隔public static final int ENEMY_BORN_INTERVAL = 5000;//敵方坦克狀態(tài)切換的間隔public static final int ENEMY_AI_INTERVAL = 1000;//敵方坦克每幀發(fā)射子彈的概率public static final double ENEMY_AI_PERCENT = 0.10; }優(yōu)化 :添加背景音樂和音效
33 : 添加了游戲信息類,獲得了所有的關(guān)卡的數(shù)量。游戲通關(guān)的判斷
34:過關(guān)判斷,通關(guān)的處理。
35:切換關(guān)卡的處理
?操作演示視頻
坦克大戰(zhàn) 2022-06-16 20-40-03
總結(jié)
- 上一篇: 详述人工智能在自动驾驶中的应用
- 下一篇: java坦克大战爆炸效果_Java坦克大