java實現坦克大戰(zhàn)小游戲
更新時間:2021年01月22日 10:15:34 作者:始料未及長安
這篇文章主要為大家詳細介紹了java實現坦克大戰(zhàn)小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
用java程序編寫的一個坦克大戰(zhàn),可以實現兩人同時在線
需要代碼的可以私信聯系我
package com.mr.frame;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import javax.swing.JPanel;
import com.mr.model.Base;
import com.mr.model.Boom;
import com.mr.model.Bot;
import com.mr.model.Bullet;
import com.mr.model.Level;
import com.mr.model.Map;
import com.mr.model.Tank;
import com.mr.model.wall.Wall;
import com.mr.type.GameType;
import com.mr.type.TankType;
import com.mr.util.ImageUtil;
/**
* 游戲面板 實際大小[794,572]
*
* @author www.mingrisoft.com
*
*/
public class GamePanel extends JPanel implements KeyListener {
/**
* 游戲界面刷新時間:20毫秒
*/
public static final int FRESH = 20;
private BufferedImage image;// 在面板中顯示的主圖片
private Graphics2D g2;// 圖片的繪圖對象
private MainFrame frame;// 主窗體
private GameType gameType;// 游戲模式
private Tank play1, play2;// 玩家1、玩家2
private boolean y_key, s_key, w_key, a_key, d_key, up_key, down_key, left_key, right_key, num1_key;// 按鍵是否按下標志,左側單詞是按鍵名
private int level;// 關卡值
private List<Bullet> bullets;// 所有子彈集合
private volatile List<Tank> allTanks;// 所有坦克集合
private List<Tank> botTanks;// 電腦坦克集合
private final int botCount = 20;// 電腦坦克總數
private int botReadyCount = botCount;// 準備出場的電腦坦克總數
private int botSurplusCount = botCount;// 電腦坦克剩余量
private int botMaxInMap = 6;// 場上最大電腦坦克數
private int botX[] = { 10, 367, 754 };// 電腦坦克出生的3個橫坐標位置
private List<Tank> playerTanks;// 玩家坦克集合
private volatile boolean finish = false;// 游戲是否結束
private Base base;// 基地
private List<Wall> walls;// 所有墻塊
private List<Boom> boomImage;// 坦克陣亡后的爆炸效果集合
private Random r = new Random();// 隨機數對象
private int createBotTimer = 0;// 生產電腦計時器
private Tank survivor;// (玩家)幸存者,用于繪制最后一個爆炸效果
/**
* 游戲面板構造方法
*
* @param frame
* - 主窗體
* @param level
* - 關卡
* @param gameType
* - 游戲模式
*/
public GamePanel(MainFrame frame, int level, GameType gameType) {
this.frame = frame;
this.level = level;
this.gameType = gameType;
setBackground(Color.WHITE);// 面板使用白色背景
init();// 初始化組件
Thread t = new FreshThead();// 創(chuàng)建游戲幀刷新線程
t.start();// 啟動線程
addListener();// 開啟監(jiān)聽
}
/**
* 組件初始化
*/
private void init() {
bullets = new ArrayList<Bullet>();// 實例化子彈集合
allTanks = new ArrayList<>();// 實例化所有坦克集合
walls = new ArrayList<>();// 實例化所有墻塊集合
boomImage = new ArrayList<>();// 實例化爆炸效果集合
image = new BufferedImage(794, 572, BufferedImage.TYPE_INT_BGR);// 實例化主圖片,采用面板實際大小
g2 = image.createGraphics();// 獲取主圖片繪圖對象
playerTanks = new ArrayList<>();// 實例化玩家坦克集合
play1 = new Tank(278, 537, ImageUtil.PLAYER1_UP_IMAGE_URL, this, TankType.player1);// 實例化玩家1
if (gameType == GameType.TWO_PLAYER) {// 如果是雙人模式
play2 = new Tank(448, 537, ImageUtil.PLAYER2_UP_IMAGE_URL, this, TankType.player2);// 實例化玩家2
playerTanks.add(play2);// 玩家坦克集合添加玩家2
}
playerTanks.add(play1);// 玩家坦克集合添加玩家1
botTanks = new Vector<>();// 實例化電腦坦克集合
botTanks.add(new Bot(botX[0], 1, this, TankType.bot));// 在第一個位置添加電腦
botTanks.add(new Bot(botX[1], 1, this, TankType.bot));// 在第二個位置添加電腦
botTanks.add(new Bot(botX[2], 1, this, TankType.bot));// 在第三個位置添加電腦
botReadyCount -= 3;// 準備出場的坦克總數減去初始化數量
allTanks.addAll(playerTanks);// 所有坦克集合添加玩家坦克集合
allTanks.addAll(botTanks);// 所有坦克集合添加電腦坦克集合
base = new Base(367, 532);// 實例化基地
initWalls();// 初始化地圖中的墻塊
}
/**
* 組件監(jiān)聽
*/
private void addListener() {
frame.addKeyListener(this);// 主窗體載入鍵盤監(jiān)聽,本類已實現KeyListener接口
}
/**
* 初始化地圖中的墻塊
*/
private void initWalls() {
Map map = Map.getMap(level);// 獲取當前關卡的地圖對象
walls.addAll(map.getWalls());// 墻塊集合添加當前地圖中所有墻塊
walls.add(base);// 墻塊集合添加基地
}
/**
* 重寫繪制組件方法
*/
public void paint(Graphics g) {
paintTankActoin();// 執(zhí)行坦克動作
CreateBot();// 循環(huán)創(chuàng)建電腦坦克
paintImage();// 繪制主圖片
g.drawImage(image, 0, 0, this); // 將主圖片繪制到面板上
}
/**
* 繪制主圖片
*/
private void paintImage() {
g2.setColor(Color.WHITE);// 使用白色
g2.fillRect(0, 0, image.getWidth(), image.getHeight());// 填充一個覆蓋整個圖片的白色矩形
panitBoom();// 繪制爆炸效果
paintBotCount();// 在屏幕頂部繪制剩余坦克數量
panitBotTanks();// 繪制電腦坦克
panitPlayerTanks();// 繪制玩家坦克
allTanks.addAll(playerTanks);// 坦克集合添加玩家坦克集合
allTanks.addAll(botTanks);// 坦克集合添加電腦坦克集合
panitWalls();// 繪制墻塊
panitBullets();// 繪制子彈
if (botSurplusCount == 0) {// 如果所有電腦都被消滅
stopThread();// 結束游戲幀刷新線程
paintBotCount();// 在屏幕頂部繪制剩余坦克數量
g2.setFont(new Font("楷體", Font.BOLD, 50));// 設置繪圖字體
g2.setColor(Color.green);// 使用綠色
g2.drawString("勝 利 !", 250, 400);// 在指定坐標繪制文字
gotoNextLevel();// 進入下一關卡
}
if (gameType == GameType.ONE_PLAYER) {// 如果是單人模式
if (!play1.isAlive()) {// 如果玩家陣亡
stopThread();// 結束游戲幀刷新線程
boomImage.add(new Boom(play1.x, play1.y));// 添加玩家1爆炸效果
panitBoom();// 繪制爆炸效果
paintGameOver();// 在屏幕中央繪制game over
gotoPrevisousLevel();// 重新進入本關卡
}
} else {// 如果是雙人模式
if (play1.isAlive() && !play2.isAlive()) {// 如果玩家1是 幸存者
survivor = play1;// 幸存者是玩家1
} else if (!play1.isAlive() && play2.isAlive()) {
survivor = play2;// 幸存者是玩家2
} else if (!(play1.isAlive() || play2.isAlive())) {// 如果兩個玩家全部陣亡
stopThread();// 結束游戲幀刷新線程
boomImage.add(new Boom(survivor.x, survivor.y));// 添加幸存者爆炸效果
panitBoom();// 繪制爆炸效果
paintGameOver();// 在屏幕中央繪制game over
gotoPrevisousLevel();// 重新進入本關卡
}
}
if (!base.isAlive()) {// 如果基地被擊中
stopThread();// 結束游戲幀刷新線程
paintGameOver();// 在屏幕中央繪制game over
base.setImage(ImageUtil.BREAK_BASE_IMAGE_URL);// 基地使用陣亡圖片
gotoPrevisousLevel();// 重新進入本關卡
}
g2.drawImage(base.getImage(), base.x, base.y, this);// 繪制基地
}
/**
* 在屏幕頂部繪制剩余坦克數量
*/
private void paintBotCount() {
g2.setColor(Color.BLUE);// 使用藍色
g2.drawString("敵方坦克剩余:" + botSurplusCount, 337, 15);// 在指定坐標繪制字符串
}
/**
* 在屏幕中央繪制game over
*/
private void paintGameOver() {
g2.setFont(new Font("楷體", Font.BOLD, 50));// 設置繪圖字體
g2.setColor(Color.RED);// 設置繪圖顏色
g2.drawString("Game Over !", 250, 400);// 在指定坐標繪制文字
}
/**
* 繪制爆炸效果
*/
private void panitBoom() {
for (int i = 0; i < boomImage.size(); i++) {// 循環(huán)遍歷爆炸效果集合
Boom boom = boomImage.get(i);// 獲取爆炸對象
if (boom.isAlive()) {// 如果爆炸效果有效
boom.show(g2);// 展示爆炸效果
} else {// 如果爆炸效果無效
boomImage.remove(i);// 在集合中刪除此爆炸對象
i--;// 循環(huán)變量-1,保證下次循環(huán)i的值不會變成i+1,以便有效遍歷集合,且防止下標越界
}
}
}
/**
* 繪制墻塊
*/
private void panitWalls() {
for (int i = 0; i < walls.size(); i++) {// 循環(huán)遍歷墻塊集合
Wall w = walls.get(i);// 獲取墻塊對象
if (w.isAlive()) {// 如果墻塊有效
g2.drawImage(w.getImage(), w.x, w.y, this);// 繪制墻塊
} else {// 如果墻塊無效
walls.remove(i);// 在集合中刪除此墻塊
i--;// 循環(huán)變量-1,保證下次循環(huán)i的值不會變成i+1,以便有效遍歷集合,且防止下標越界
}
}
}
/**
* 繪制子彈
*/
private void panitBullets() {
for (int i = 0; i < bullets.size(); i++) {// 循環(huán)遍歷子彈集合
Bullet b = bullets.get(i);// 獲取子彈對象
if (b.isAlive()) {// 如果子彈有效
b.move();// 子彈執(zhí)行移動操作
b.hitBase();// 子彈執(zhí)行擊中基地判斷
b.hitWall();// 子彈執(zhí)行擊中墻壁判斷
b.hitTank();// 子彈執(zhí)行擊中坦克判斷
g2.drawImage(b.getImage(), b.x, b.y, this);// 繪制子彈
} else {// 如果子彈無效
bullets.remove(i);// 在集合中刪除此子彈
i--;// 循環(huán)變量-1,保證下次循環(huán)i的值不會變成i+1,以便有效遍歷集合,且防止下標越界
}
}
}
/**
* 繪制電腦坦克
*/
private void panitBotTanks() {
for (int i = 0; i < botTanks.size(); i++) {// 循環(huán)遍歷電腦坦克集合
Bot t = (Bot) botTanks.get(i);// 獲取電腦坦克對象
if (t.isAlive()) {// 如果坦克存活
t.go();// 電腦坦克展開行動
g2.drawImage(t.getImage(), t.x, t.y, this);// 繪制坦克
} else {// 如果坦克陣亡
botTanks.remove(i);// 集合中刪除此坦克
i--;// 循環(huán)變量-1,保證下次循環(huán)i的值不會變成i+1,以便有效遍歷集合,且防止下標越界
boomImage.add(new Boom(t.x, t.y));// 在坦克位置創(chuàng)建爆炸效果
decreaseBot();// 剩余坦克數量-1
}
}
}
/**
* 繪制玩家坦克
*/
private void panitPlayerTanks() {
for (int i = 0; i < playerTanks.size(); i++) {// 循環(huán)遍歷玩家坦克
Tank t = playerTanks.get(i);// 獲取玩家坦克對象
if (t.isAlive()) {// 如果坦克存活
g2.drawImage(t.getImage(), t.x, t.y, this);// 繪制坦克
} else {// 如果坦克陣亡
playerTanks.remove(i);// 集合中刪除此坦克
i--;// 循環(huán)變量-1,保證下次循環(huán)i的值不會變成i+1,以便有效遍歷集合,且防止下標越界
boomImage.add(new Boom(t.x, t.y));// 在坦克位置創(chuàng)建爆炸效果
}
}
}
/**
* 結束游戲幀刷新
*/
private synchronized void stopThread() {
frame.removeKeyListener(this);// 主窗體刪除本類鍵盤事件監(jiān)聽對象
finish = true;// 游戲停止標志為true
}
/**
* 游戲幀刷新線程
*/
private class FreshThead extends Thread {
public void run() {// 線程主方法
while (!finish) {// 如果游戲未停止
repaint();// 執(zhí)行本類重繪方法
try {
Thread.sleep(FRESH);// 指定時間后重新繪制界面
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 添加電腦坦克,如果場上坦克未到達最大值,每4秒鐘之后在三個出生位置隨機選擇其一,創(chuàng)建電腦坦克。
*/
private void CreateBot() {
createBotTimer += FRESH;// 計時器按照刷新時間遞增
// “當場上電腦小于場上最大數時” 并且 “準備上場的坦克數量大于0” 并且 “計時器記錄已過去4秒鐘”
if (botTanks.size() < botMaxInMap && botReadyCount > 0 && createBotTimer >= 4000) {
int index = r.nextInt(3);// 隨機獲取0或1或2其中一個值
Rectangle bornRect = new Rectangle(botX[index], 1, 35, 35);// 創(chuàng)建坦克隨機出生區(qū)域
for (int i = 0, lengh = allTanks.size(); i < lengh; i++) {// 循環(huán)遍歷所有坦克集合
Tank t = allTanks.get(i);// 獲取坦克對象
if (t.isAlive() && t.hit(bornRect)) {// 如果場上存在與隨機位置重合并存活的坦克
return;// 結束方法
}
}
botTanks.add(new Bot(botX[index], 1, GamePanel.this, TankType.bot));// 在隨機位置創(chuàng)造電腦坦克
botReadyCount--;// 準備上場電腦數量-1
createBotTimer = 0;// 產生電腦計時器重新計時
}
}
/**
* 進入下一關卡
*/
private void gotoNextLevel() {
Thread jump = new JumpPageThead(Level.nextLevel());// 創(chuàng)建跳轉到下一關卡的線程
jump.start();// 啟動線程
}
/**
* 重新進入本關卡
*/
private void gotoPrevisousLevel() {
Thread jump = new JumpPageThead(Level.previsousLevel());// 創(chuàng)建重新進入本關卡的線程
jump.start();// 啟動線程
}
/**
* 剩余坦克數量減少1
*/
public void decreaseBot() {
botSurplusCount--;// 電腦剩余數量-1
}
/**
* 按鍵按下時
*/
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {// 判斷按下的按鍵值
case KeyEvent.VK_Y:// 如果按下的是“Y”
y_key = true;// “Y”按下標志為true
break;
case KeyEvent.VK_W:// 如果按下的是“W”
w_key = true;// “W”按下標志為true
a_key = false;// “A”按下標志為false
s_key = false;// “S”按下標志為false
d_key = false;// “D”按下標志為false
break;
case KeyEvent.VK_A:// 如果按下的是“A”
w_key = false;// “W”按下標志為false
a_key = true;// “A”按下標志為true
s_key = false;// “S”按下標志為false
d_key = false;// “D”按下標志為false
break;
case KeyEvent.VK_S:// 如果按下的是“S”
w_key = false;// “W”按下標志為false
a_key = false;// “A”按下標志為false
s_key = true;// “S”按下標志為true
d_key = false;// “D”按下標志為false
break;
case KeyEvent.VK_D:// 如果按下的是“D”
w_key = false;// “W”按下標志為false
a_key = false;// “A”按下標志為false
s_key = false;// “S”按下標志為false
d_key = true;// “D”按下標志為true
break;
case KeyEvent.VK_HOME:// 如果按下的是“HOME”,效果同下
case KeyEvent.VK_NUMPAD1:// 如果按下的是小鍵盤數字1
num1_key = true;// 小鍵盤數字1按下標志為true
break;
case KeyEvent.VK_UP:// 如果按下的是“↑”
up_key = true;// “↑”按下標志為true
down_key = false;// “↓”按下標志為false
right_key = false;// “→”按下標志為false
left_key = false;// “←”按下標志為false
break;
case KeyEvent.VK_DOWN:// 如果按下的是“↓”
up_key = false;// “↑”按下標志為false
down_key = true;// “↓”按下標志為true
right_key = false;// “→”按下標志為false
left_key = false;// “←”按下標志為false
break;
case KeyEvent.VK_LEFT:// 如果按下的是“←”
up_key = false;// “↑”按下標志為false
down_key = false;// “↓”按下標志為false
right_key = false;// “→”按下標志為false
left_key = true;// “←”按下標志為true
break;
case KeyEvent.VK_RIGHT:// 如果按下的是“→”
up_key = false;// “↑”按下標志為false
down_key = false;// “↓”按下標志為false
right_key = true;// “→”按下標志為true
left_key = false;// “←”按下標志為false
break;
}
}
/**
* 根據按鍵按下狀態(tài),讓坦克執(zhí)行相應動作
*/
private void paintTankActoin() {
if (y_key) {// 如果“Y”鍵是按下狀態(tài)
play1.attack();// 玩家1坦克攻擊
}
if (w_key) {// 如果“W”鍵是按下狀態(tài)
play1.upward();// 玩家1坦克向上移動
}
if (d_key) {// 如果“D”鍵是按下狀態(tài)
play1.rightward();// 玩家1坦克向右移動
}
if (a_key) {// 如果“A”鍵是按下狀態(tài)
play1.leftward();// 玩家1坦克左移動
}
if (s_key) {// 如果“S”鍵是按下狀態(tài)
play1.downward();// 玩家1坦克向下移動
}
if (gameType == GameType.TWO_PLAYER) {
if (num1_key) {// 如果“M”鍵是按下狀態(tài)
play2.attack();// 玩家2坦克攻擊
}
if (up_key) {// 如果“←”鍵是按下狀態(tài)
play2.upward();// 玩家2坦克向上移動
}
if (right_key) {// 如果“→”鍵是按下狀態(tài)
play2.rightward();// 玩家2坦克向右移動
}
if (left_key) {// 如果“↑”鍵是按下狀態(tài)
play2.leftward();// 玩家2坦克左移動
}
if (down_key) {// 如果“↓”鍵是按下狀態(tài)
play2.downward();// 玩家2坦克后移動
}
}
}
/**
* 按鍵抬起時
*/
@Override
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_Y:// 如果抬起的是“Y”
y_key = false;// “Y”按下標志為false
break;
case KeyEvent.VK_W:// 如果抬起的是“W”
w_key = false;// “W”按下標志為false
break;
case KeyEvent.VK_A:// 如果抬起的是“A”
a_key = false;// “A”按下標志為false
break;
case KeyEvent.VK_S:// 如果抬起的是“S”
s_key = false;// “S”按下標志為false
break;
case KeyEvent.VK_D:// 如果抬起的是“D”
d_key = false;// “D”按下標志為false
break;
case KeyEvent.VK_HOME:// 如果抬起的是“HOME”,效果同下
case KeyEvent.VK_NUMPAD1:// 如果抬起的是小鍵盤1
num1_key = false;// 小鍵盤1按下標志為false
break;
case KeyEvent.VK_UP:// 如果抬起的是“↑”
up_key = false;// “↑”按下標志為false
break;
case KeyEvent.VK_DOWN:// 如果抬起的是“↓”
down_key = false;// “↓”按下標志為false
break;
case KeyEvent.VK_LEFT:// 如果抬起的是“←”
left_key = false;// “←”按下標志為false
break;
case KeyEvent.VK_RIGHT:// 如果抬起的是“→”
right_key = false;// “→”按下標志為false
break;
}
}
/**
* 向子彈集合中添加子彈
*
* @param b
* - 添加的子彈
*/
public void addBullet(Bullet b) {
bullets.add(b);// 子彈集合中添加子彈
}
/**
* 獲取所有墻塊集合
*
* @return 所有墻塊
*/
public List<Wall> getWalls() {
return walls;
}
/**
* 獲取基地對象
*
* @return 基地
*/
public Base getBase() {
return base;
}
/**
* 獲取所有坦克集合
*
* @return 所有坦克
*/
public List<Tank> getTanks() {
return allTanks;
}
/**
* 游戲結束跳轉線程
*/
private class JumpPageThead extends Thread {
int level;// 跳轉的關卡
/**
* 跳轉線程構造方法
*
* @param level
* - 跳轉的關卡
*/
public JumpPageThead(int level) {
this.level = level;
}
/**
* 線程主方法
*/
public void run() {
try {
Thread.sleep(1000);// 1秒鐘后
frame.setPanel(new LevelPanel(level, frame, gameType));// 主窗體跳轉到指定關卡
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 鍵入某按鍵事件
*/
public void keyTyped(KeyEvent e) {
// 不實現此方法,但不可刪除
}
}
圖片


以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

